5#error "Process.hpp is currently UNIX only, and does not support Windows. Feel free to open a PR to change this"
46 ssize_t
writeToFd(
const std::string& data,
int fd) {
47 ssize_t bytes = write(fd, data.data(), data.size());
55 std::array<char, 4096> buff;
66 while (poll(&pdfs, nfds, 10)) {
73 out << std::string_view {
74 buff.begin(), buff.begin() + bytes
84 std::array<int, 2>
fds;
86 if (pipe(
fds.data()) != 0) {
87 throw std::runtime_error(
"Failed to open pipe");
132 if (openpty(&
master, &
slave,
nullptr,
nullptr,
nullptr) == -1) {
133 throw std::runtime_error(
"Failed to open PTY");
182 return std::make_shared<Pipe>();
198 throw std::runtime_error(
"Must open stdin to write to stdin");
234 return std::make_shared<PTY>();
238 std::map<std::string, std::string>
env = {};
251 for (
auto& [k, v] :
env) {
252 if (k.find(
'=') != std::string::npos) {
253 throw std::runtime_error(
"Illegal key: " + k);
258 throw std::runtime_error(
260 "Working directory set to {}, which does not exist or isn't a directory",
274 std::optional<
decltype(fork())>
pid = std::nullopt;
277 std::variant<Pipes, std::shared_ptr<PTY>>
291 if (!
pid.has_value()) {
292 std::cerr <<
"waitPid called, but pid has no value. Something has gone very wrong" << std::endl;
295 if (waitpid(*
pid, &wstatus, opts) > 0) {
296 if (WIFEXITED(wstatus)) {
299 }
else if (WIFSIGNALED(wstatus)) {
302 }
else if (WIFSTOPPED(wstatus)) {
307 <<
"WARNING: stc::Unix::Process got an unknown status: " << wstatus
324 const std::optional<Environment>& env
326 if (env == std::nullopt) {
331 for (
char **env = environ; *env !=
nullptr; env++) {
339 std::vector<char*>* data =
new std::vector<char*>;
340 if (env->extendEnviron && size > 0) {
342 environ, environ + size
354 for (
const auto& [k, v] : env->env) {
355 if (env->extendEnviron) {
363 [&](
const auto& v) ->
bool {
364 return strncmp(v, k.data(), k.size()) == 0;
370 std::string combined = std::format(
373 auto* newStr = strdup(combined.c_str());
374 if (newStr ==
nullptr) {
375 std::cerr <<
"Failed to copy string to env" << std::endl;
378 data->push_back(newStr);
380 data->push_back(
nullptr);
387 const std::vector<std::string>& command,
388 const std::function<
void()>& readImpl,
389 const std::function<
void()>& prepDuping,
390 const std::optional<Environment>& env
392 if (command.size() == 0) {
393 throw std::runtime_error(
"Cannot run null command");
395 std::vector<const char*> convertedCommand;
402 std::cout <<
"Exec: ";
404 convertedCommand.reserve(command.size() + 1);
405 for (
auto& str : command) {
407 std::cout << std::quoted(str);
411 if (convertedCommand.size() != command.size() - 1) {
415 convertedCommand.push_back(str.c_str());
420 convertedCommand.push_back(
nullptr);
424 throw std::runtime_error(
"Failed to fork");
425 }
else if (
pid == 0) {
427 if (prepDuping !=
nullptr) {
433 std::visit([](
auto& resolved) {
434 using T = std::decay_t<
decltype(resolved)>;
435 if constexpr (std::is_same_v<T, std::shared_ptr<PTY>>) {
443 if (env.has_value()) {
444 if (env->workingDirectory.has_value()) {
447 std::filesystem::current_path(
448 env->workingDirectory.value()
454 convertedCommand.at(0),
455 (
char**) convertedCommand.data(),
460 if (readImpl !=
nullptr) {
461 this->inputCollector = std::thread(
468 void run(
const std::function<
void()>& readImpl) {
475 [[nodiscard(
"Discarding immediately terminates the process. You probably don't want this")]]
477 const std::vector<std::string>& command,
478 const std::optional<Environment>& env = std::nullopt,
484 [[nodiscard(
"Discarding immediately terminates the process. You probably don't want this")]]
486 const std::vector<std::string>& command,
488 const std::optional<Environment>& env = std::nullopt,
494 auto& pipes = std::get<Pipes>(this->
interface.value());
496 std::lock_guard l(
lock);
502 std::lock_guard l(
lock);
509 dup2(pipes.
stdinPipe->readFd(), STDIN_FILENO);
512 dup2(pipes.
stdoutPipe->writeFd(), STDOUT_FILENO);
515 dup2(pipes.
stderrPipe->writeFd(), STDERR_FILENO);
517 std::get<Pipes>(*interface).die();
521 [[nodiscard(
"Discarding immediately terminates the process. You probably don't want this")]]
523 const std::vector<std::string>& command,
524 const std::shared_ptr<PTY>& pty,
525 const std::optional<Environment>& env = std::nullopt,
528 if (pty ==
nullptr) {
529 throw std::runtime_error(
530 "pty cannot be null. If you don't want to attach anything, use the non-pipe/non-PTY constructor instead"
535 auto pty = std::get<std::shared_ptr<PTY>>(this->
interface.value());
538 std::lock_guard l(
lock);
545 dup2(pty->slave, STDIN_FILENO);
546 dup2(pty->slave, STDOUT_FILENO);
547 dup2(pty->slave, STDERR_FILENO);
548 std::get<std::shared_ptr<PTY>>(*interface)->die();
571 std::lock_guard g(
lock);
590 std::lock_guard g(
lock);
603 std::lock_guard g(
lock);
617 return std::visit([&](
auto& resolved) -> ssize_t {
618 using T = std::decay_t<
decltype(resolved)>;
619 if constexpr (std::is_same_v<T, std::shared_ptr<PTY>>) {
620 return resolved->writeToStdin(data);
622 return resolved.writeToStdin(data);
626 throw std::runtime_error(
"Must use pty or pipe mode to write to stdin");
637 if (inputCollector.joinable()) {
649 if (
pid.has_value() && *
pid > 0) {
672 throw std::runtime_error(
"Must use pipe or pty mode to use this function");
Definition Process.hpp:272
char *const * createEnviron(const std::optional< Environment > &env)
Definition Process.hpp:323
void sigkill()
Definition Process.hpp:666
std::string getStderrBuffer(bool reset=false)
Definition Process.hpp:589
ssize_t writeToStdin(const std::string &data)
Definition Process.hpp:615
void run(const std::function< void()> &readImpl)
Definition Process.hpp:468
std::mutex lock
Definition Process.hpp:280
std::optional< decltype(fork())> pid
Definition Process.hpp:274
std::stringstream stderrBuff
Definition Process.hpp:279
Config config
Definition Process.hpp:287
void closeStdin()
Definition Process.hpp:670
std::optional< int > getExitCode()
Definition Process.hpp:685
std::thread inputCollector
Definition Process.hpp:282
std::stringstream stdoutBuff
Definition Process.hpp:279
void stop()
Definition Process.hpp:658
int block()
Definition Process.hpp:635
virtual ~Process()
Definition Process.hpp:554
bool running
Definition Process.hpp:285
bool waitPid(int opts=0)
Definition Process.hpp:289
void resetBuffers()
Definition Process.hpp:602
std::optional< std::variant< Pipes, std::shared_ptr< PTY > > > interface
Definition Process.hpp:278
void signal(int sig)
Definition Process.hpp:647
void doSpawnCommand(const std::vector< std::string > &command, const std::function< void()> &readImpl, const std::function< void()> &prepDuping, const std::optional< Environment > &env)
Definition Process.hpp:386
Process(const std::vector< std::string > &command, const std::shared_ptr< PTY > &pty, const std::optional< Environment > &env=std::nullopt, const Config &config={})
Definition Process.hpp:522
Process(const std::vector< std::string > &command, const std::optional< Environment > &env=std::nullopt, const Config &config={})
Definition Process.hpp:476
std::optional< bool > hasExitedNormally()
Definition Process.hpp:681
std::string getStdoutBuffer(bool reset=false)
Definition Process.hpp:570
Process(const std::vector< std::string > &command, const Pipes &pipes, const std::optional< Environment > &env=std::nullopt, const Config &config={})
Definition Process.hpp:485
std::atomic< std::optional< bool > > exitedNormally
Definition Process.hpp:284
std::atomic< int > statusCode
Definition Process.hpp:283
Definition Process.hpp:43
std::shared_ptr< PTY > createPTY()
Definition Process.hpp:233
std::shared_ptr< Pipe > createPipe()
Definition Process.hpp:181
Definition Process.hpp:268
bool verboseUserOutput
Definition Process.hpp:269
Definition Process.hpp:237
std::map< std::string, std::string > env
Definition Process.hpp:238
bool extendEnviron
Definition Process.hpp:242
void validate() const
Definition Process.hpp:250
std::optional< std::string > workingDirectory
Definition Process.hpp:248
Definition Process.hpp:45
ssize_t writeToFd(const std::string &data, int fd)
Definition Process.hpp:46
ssize_t readFromFd(std::stringstream &out, int fd)
Definition Process.hpp:54
Definition Process.hpp:125
~PTY()
Definition Process.hpp:136
int master
Definition Process.hpp:126
ssize_t writeToStdin(const std::string &data)
Definition Process.hpp:169
PTY()
Definition Process.hpp:128
void closeSlaveChannel()
Definition Process.hpp:158
ssize_t readData(std::stringstream &out)
Definition Process.hpp:173
void closeMasterChannel()
Definition Process.hpp:152
int slave
Definition Process.hpp:126
void die()
Definition Process.hpp:147
Definition Process.hpp:83
int readFd()
Definition Process.hpp:113
~Pipe()
Definition Process.hpp:91
Pipe()
Definition Process.hpp:85
void closeRead()
Definition Process.hpp:99
ssize_t readData(std::stringstream &out)
Definition Process.hpp:120
std::array< int, 2 > fds
Definition Process.hpp:84
void closeWrite()
Definition Process.hpp:105
void die()
Definition Process.hpp:95
int writeFd()
Definition Process.hpp:116
Definition Process.hpp:185
std::shared_ptr< Pipe > stderrPipe
Definition Process.hpp:187
ssize_t writeToStdin(const std::string &data)
Definition Process.hpp:196
std::shared_ptr< Pipe > stdoutPipe
Definition Process.hpp:186
std::shared_ptr< Pipe > stdinPipe
Definition Process.hpp:188
static Pipes separate(bool withStdin=true)
Definition Process.hpp:207
static Pipes shared(bool withStdin=true)
Definition Process.hpp:218
void die()
Definition Process.hpp:190