Files
lix/lix/libutil/processes.hh
T
eldritch horrors 0453be06b6 libutil: remove unused RunOptions::{chdir, createSession}
Change-Id: Ib2b9f5c094233661a01ad8cea30a44e11931c941
2026-01-22 16:59:39 +01:00

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);
}