219 lines
4.7 KiB
C++
219 lines
4.7 KiB
C++
#pragma once
|
|
///@file
|
|
|
|
#include "lix/libutil/async-io.hh"
|
|
#include "lix/libutil/result.hh"
|
|
#include "lix/libutil/types.hh"
|
|
#include "lix/libutil/error.hh"
|
|
#include "lix/libutil/file-descriptor.hh"
|
|
|
|
#include <kj/async.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <optional>
|
|
|
|
namespace nix {
|
|
|
|
struct Sink;
|
|
struct Source;
|
|
|
|
class Pid
|
|
{
|
|
pid_t pid = -1;
|
|
public:
|
|
Pid();
|
|
explicit Pid(pid_t pid): pid(pid) {}
|
|
Pid(Pid && other);
|
|
Pid & operator=(Pid && other);
|
|
~Pid() noexcept(false);
|
|
explicit operator bool() const { return pid != -1; }
|
|
int kill();
|
|
int wait();
|
|
|
|
pid_t release();
|
|
pid_t get() const { return pid; }
|
|
};
|
|
|
|
class ProcessGroup
|
|
{
|
|
Pid leader;
|
|
|
|
public:
|
|
ProcessGroup() = default;
|
|
explicit ProcessGroup(Pid leader) : leader(std::move(leader)) {}
|
|
ProcessGroup(ProcessGroup &&) = default;
|
|
ProcessGroup & operator=(ProcessGroup &&) = default;
|
|
~ProcessGroup() noexcept(false);
|
|
|
|
explicit operator bool() const
|
|
{
|
|
return bool(leader);
|
|
}
|
|
|
|
int wait()
|
|
{
|
|
return leader.wait();
|
|
}
|
|
int kill();
|
|
};
|
|
|
|
/**
|
|
* Kill all processes running under the specified uid by sending them
|
|
* a SIGKILL.
|
|
*/
|
|
void killUser(uid_t uid);
|
|
|
|
|
|
/**
|
|
* Fork a process that runs the given function, and return the child
|
|
* pid to the caller.
|
|
*/
|
|
struct ProcessOptions
|
|
{
|
|
/**
|
|
* use clone() with the specified flags (Linux only)
|
|
*/
|
|
int cloneFlags = 0;
|
|
};
|
|
|
|
[[nodiscard]]
|
|
Pid startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
|
|
|
|
|
/**
|
|
* Run a program and return its stdout in a string (i.e., like the
|
|
* shell backtick operator).
|
|
*/
|
|
kj::Promise<Result<std::string>> runProgram(
|
|
Path program,
|
|
bool searchPath = false,
|
|
const Strings args = Strings(),
|
|
bool isInteractive = false
|
|
);
|
|
|
|
struct RunOptions
|
|
{
|
|
struct Redirection
|
|
{
|
|
int dup, from;
|
|
};
|
|
|
|
Path program;
|
|
bool searchPath = true;
|
|
std::optional<std::string> argv0;
|
|
Strings args = {};
|
|
std::optional<uid_t> uid = {};
|
|
std::optional<uid_t> gid = {};
|
|
std::optional<std::map<std::string, std::string>> environment = {};
|
|
bool captureStdout = false;
|
|
std::vector<Redirection> redirections;
|
|
#if __linux__
|
|
std::set<long> caps;
|
|
#endif
|
|
};
|
|
|
|
struct [[nodiscard("you must call RunningProgram::wait()")]] RunningProgram
|
|
{
|
|
friend RunningProgram runProgram2(const RunOptions & options);
|
|
|
|
protected:
|
|
Path program;
|
|
Pid pid;
|
|
std::unique_ptr<AsyncFdIoStream> childStdout;
|
|
|
|
RunningProgram(PathView program, Pid pid, AutoCloseFD childStdout);
|
|
|
|
public:
|
|
RunningProgram() = default;
|
|
RunningProgram(RunningProgram &&) = default;
|
|
RunningProgram & operator=(RunningProgram &&) = default;
|
|
~RunningProgram();
|
|
|
|
explicit operator bool() const { return bool(pid); }
|
|
|
|
std::tuple<Pid, std::unique_ptr<AsyncFdIoStream>> release();
|
|
|
|
int kill();
|
|
[[nodiscard]]
|
|
int wait();
|
|
void waitAndCheck();
|
|
|
|
std::optional<int> getStdoutFD() const
|
|
{
|
|
return childStdout ? std::optional(childStdout->getFD()) : std::nullopt;
|
|
}
|
|
|
|
AsyncFdIoStream * getStdout() const
|
|
{
|
|
return childStdout.get();
|
|
};
|
|
};
|
|
|
|
kj::Promise<Result<std::pair<int, std::string>>> runProgram(RunOptions options);
|
|
|
|
RunningProgram runProgram2(const RunOptions & options);
|
|
|
|
class [[nodiscard("you must call RunningHelper::wait()")]] RunningHelper : RunningProgram
|
|
{
|
|
friend RunningHelper runHelper(const char * name, RunOptions options);
|
|
|
|
const char * name;
|
|
AutoCloseFD errPipe;
|
|
|
|
RunningHelper(const char * name, RunningProgram && proc, AutoCloseFD && errPipe)
|
|
: RunningProgram(std::move(proc))
|
|
, name(name)
|
|
, errPipe(std::move(errPipe))
|
|
{
|
|
}
|
|
|
|
void check();
|
|
|
|
public:
|
|
RunningHelper() = default;
|
|
RunningHelper(RunningHelper &&) = default;
|
|
RunningHelper & operator=(RunningHelper &&) = default;
|
|
|
|
using RunningProgram::operator bool;
|
|
using RunningProgram::getStdout;
|
|
using RunningProgram::getStdoutFD;
|
|
using RunningProgram::kill;
|
|
using RunningProgram::wait;
|
|
|
|
/**
|
|
* Kill the entire process group the helper runs in. This is usually **unsafe**
|
|
* unless the helper process has made itself a process group or session leader!
|
|
*/
|
|
int killProcessGroup();
|
|
|
|
void waitAndCheck();
|
|
};
|
|
|
|
RunningHelper runHelper(const char * name, RunOptions options);
|
|
|
|
class ExecError : public Error
|
|
{
|
|
public:
|
|
int status;
|
|
|
|
template<typename... Args>
|
|
ExecError(int status, const Args & ... args)
|
|
: Error(args...), status(status)
|
|
{ }
|
|
};
|
|
|
|
/**
|
|
* Convert the exit status of a child as returned by wait() into an
|
|
* error string.
|
|
*/
|
|
std::string statusToString(int status);
|
|
|
|
bool statusOk(int status);
|
|
|
|
}
|