#include "Process.hpp" #include #include #include #include #include "WordExp.hpp" namespace nanosm { inline static pid_t Clone3(const clone_args& args) { return syscall(SYS_clone3, &args, sizeof(clone_args)); } Process::~Process() { Kill(); Reset(); } int Process::GetFD() const { return pidFd; } int Process::InterestedEvents() const { /// Pidfd only returns EPOLLIN. return EPOLLIN; } void Process::Spawn(const std::string& commandLine) { commLine = commandLine; Respawn(); } void Process::Respawn() { pid = Clone3({ .flags = CLONE_PIDFD, .pidfd = std::bit_cast(&pidFd), .exit_signal = SIGCHLD, }); if(pid < 0) perror("Clone3()"); if(pid == 0) { // Forked from the parent successfully, execute the given thing auto exp = nanosm::WordExp::Expand(commLine); std::vector argv(exp.words.size()); for(usize i = 0; i < exp.words.size(); ++i) argv[i] = exp.words[i].data(); execvp(exp.words[0].data(), argv.data()); } else { // Parent: monitor FD eventLoop.AddObject(IoObject::Ptr(shared_from_this())); } } void Process::OnReady(int eventMask) { // In our case, any readiness signaled by the pidfd means the process exited // so this will never block (or really, wait). waitid(P_PIDFD, pidFd, &siginfo, WNOHANG); // Post a callback to call the user's exit callback (if one exists) eventLoop.Post([self = shared_from_this()]() { // Prepare for re-attaching, or etc. self->eventLoop.RemoveObject(self); self->Reset(); if(self->onProcessExit) self->onProcessExit(self->siginfo.si_status); }); } void Process::Kill() { if(pid != -1) kill(pid, SIGTERM); } void Process::SetExitCallback(std::function f) { onProcessExit = f; } void Process::Reset() { if(pidFd != -1) { close(pidFd); pidFd = -1; } pid = -1; siginfo = {}; } } // namespace nanosm