275 lines
7.1 KiB
C++
275 lines
7.1 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: Process.Linux.cpp
|
|
Date: 2021-6-12
|
|
Author: Reece
|
|
***/
|
|
#include <RuntimeInternal.hpp>
|
|
#include "Processes.hpp"
|
|
#include "Process.Linux.hpp"
|
|
|
|
#include <unistd.h>
|
|
#include <sys/syscall.h>
|
|
|
|
namespace Aurora::Processes
|
|
{
|
|
class ProcessImpl : public IProcess
|
|
{
|
|
public:
|
|
ProcessImpl(AuString cmd, AuList<AuString> args) : module_(cmd), args_(args);
|
|
~ProcessImpl();
|
|
|
|
void ShutdownPipes();
|
|
|
|
bool TryKill() override;
|
|
bool Terminate() override;
|
|
Aurora::Threading::IWaitable *AsWaitable() override;
|
|
|
|
AuSInt GetExitCode() override;
|
|
|
|
bool Read (bool error, void *buffer, AuUInt32 &len) override;
|
|
bool Write(const void *buffer, AuUInt32 len) override;
|
|
|
|
bool Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn) override;
|
|
|
|
private:
|
|
|
|
int pipeStdOut_[2]{};
|
|
int pipeStdErr_[2]{};
|
|
int pipeStdIn_ [2]{};
|
|
|
|
AuString module_;
|
|
ESpawnType type_;
|
|
|
|
AuList<AuString> args_;
|
|
AuList<const char *> cargs_;
|
|
|
|
AuString windows_;
|
|
|
|
Threading::Threads::ThreadUnique_t thread_;
|
|
std::atomic<int> handle_;
|
|
|
|
AuSInt exitCode_;
|
|
};
|
|
|
|
ProcessImpl::ProcessImpl(AuString cmd, AuList<AuString> args) : module_(cmd), args_(args)
|
|
{
|
|
this->args_.insert(this->args_.begin(), cmd);
|
|
|
|
for (const auto &arg : this->args_)
|
|
{
|
|
this->cargs_.push_back(arg.c_str());
|
|
this->windows_ += arg + " ";
|
|
}
|
|
|
|
this->cargs_.push_back(nullptr);
|
|
this->windows_.resize(this->windows_.size() - 1);
|
|
}
|
|
|
|
ProcessImpl::~ProcessImpl()
|
|
{
|
|
if (this->type_ == ESpawnType::eSpawnChildProcessWorker)
|
|
{
|
|
TryKill();
|
|
Terminate();
|
|
}
|
|
|
|
if (this->handle_)
|
|
{
|
|
if (this->type_ == ESpawnType::eSpawnThreadLeader)
|
|
{
|
|
sys_pidfd_send_signal(this->handle_, SIGCONT, NULL, 0);
|
|
}
|
|
}
|
|
|
|
if (this->thread_)
|
|
{
|
|
this->thread_.reset();
|
|
}
|
|
|
|
if (auto handle = this->handle_.exchange(0))
|
|
{
|
|
close(handle);
|
|
}
|
|
|
|
ShutdownPipes();
|
|
}
|
|
|
|
void ProcessImpl::ShutdownPipes()
|
|
{
|
|
if (auto fd = std::exchange(pipeStdErr_[0], {})) close(fd);
|
|
if (auto fd = std::exchange(pipeStdErr_[1], {})) close(fd);
|
|
if (auto fd = std::exchange(pipeStdOut_[0], {})) close(fd);
|
|
if (auto fd = std::exchange(pipeStdOut_[1], {})) close(fd);
|
|
if (auto fd = std::exchange(pipeStdIn_[0], {})) close(fd);
|
|
if (auto fd = std::exchange(pipeStdIn_[1], {})) close(fd);
|
|
}
|
|
|
|
bool ProcessImpl::TryKill()
|
|
{
|
|
if (this->handle_)
|
|
{
|
|
return sys_pidfd_send_signal(this->handle_, SIGTERM, NULL, 0) == 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ProcessImpl::Terminate()
|
|
{
|
|
if (this->handle_)
|
|
{
|
|
sys_pidfd_send_signal(this->handle_, SIGKILL, NULL, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Aurora::Threading::IWaitable *ProcessImpl::AsWaitable()
|
|
{
|
|
if (!this->thread_) return nullptr;
|
|
return this->thread_->AsWaitable();
|
|
}
|
|
|
|
AuSInt ProcessImpl::GetExitCode()
|
|
{
|
|
return this->exitCode_;
|
|
}
|
|
|
|
static int aurora_sys_waitid(int which, pid_t pid, siginfo_t *info, int options,
|
|
struct rusage *ru)
|
|
{
|
|
return syscall(__NR_waitid, which, pid, info, options, ru);
|
|
}
|
|
|
|
bool ProcessImpl::Read(bool error, void *buffer, AuUInt32 &len)
|
|
{
|
|
auto handle = error ? pipeStdErr_[0] : pipeStdOut_[0];
|
|
if (handle < 0) return false;
|
|
len = read(handle, buffer, len);
|
|
return ret;
|
|
}
|
|
|
|
bool ProcessImpl::Write(const void *buffer, AuUInt32 len)
|
|
{
|
|
auto handle = pipeStdIn_[1];
|
|
if (!handle) return false;
|
|
return write(handle, buffer, len) == len;
|
|
}
|
|
|
|
bool ProcessImpl::Start(enum ESpawnType type, bool fwdOut, bool fwdErr, bool fwdIn)
|
|
{
|
|
this->exitCode_ = 0x10110100;
|
|
this->type_ = type;
|
|
|
|
if (type == ESpawnType::eSpawnAtomicOvermap)
|
|
{
|
|
execv(this->module_.c_str(), this->cargs_.data());
|
|
SysPushErrorGen("execv didn't overwrite the process map, given {} ({})", this->module_, this->windows_);
|
|
return false;
|
|
}
|
|
|
|
if (fwdOut)
|
|
{
|
|
if (!pipe(pipeStdOut_))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (fwdErr)
|
|
{
|
|
if (!pipe(pipeStdErr_))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (fwdIn)
|
|
{
|
|
if (!pipe(pipeStdIn_))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Threading::Threads::AbstractThreadVectors handler;
|
|
|
|
pid_t pid;
|
|
{
|
|
Semaphore semaphore;
|
|
pid = fork();
|
|
if (pid == 0)
|
|
{
|
|
semaphore.Lock();
|
|
|
|
if (fwdIn)
|
|
{
|
|
dup2(pipeStdIn_[0], STDIN_FILENO);
|
|
close(std::exchange(pipeStdIn_[0], 0));
|
|
}
|
|
|
|
if (fwdErr)
|
|
{
|
|
dup2(pipeStdErr_[1], STDERR_FILENO);
|
|
close(std::exchange(pipeStdErr_[1], 0));
|
|
}
|
|
|
|
if (fwdOut)
|
|
{
|
|
dup2(pipeStdOut_[1], STDOUT_FILENO);
|
|
close(std::exchange(pipeStdOut_[1], 0));
|
|
}
|
|
|
|
if (type != ESpawnType::eSpawnSubProcessWorker)
|
|
{
|
|
setsid();
|
|
}
|
|
|
|
execv(this->module_.c_str(), this->cargs_.data());
|
|
SysPushErrorGen("execv didn't overwrite the process map, given {} ({})", this->module_, this->windows_);
|
|
return false;
|
|
}
|
|
else if (pid < 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
this->handle_ = pidfd_open(pid, 0);
|
|
semaphore.unlock();
|
|
}
|
|
}
|
|
|
|
handler.DoRun = [=](Threading::Threads::IAuroraThread *)
|
|
{
|
|
{
|
|
// TODO: experimental (and requires kernel 5.3+)
|
|
siginfo_t info = {
|
|
.si_signo = 0,
|
|
};
|
|
|
|
// TODO: vaildate response
|
|
aurora_sys_waitid(P_PIDFD, this->handle_, &info, WEXITED, NULL);
|
|
|
|
// TODO: confirm this works?!?
|
|
this->exitCode_ = info.si_status;
|
|
}
|
|
};
|
|
|
|
this->thread_ = Threading::Threads::ThreadUnique(handler);
|
|
if (!this->thread_) return false;
|
|
|
|
this->thread_->Run();
|
|
return true;
|
|
}
|
|
|
|
AUKN_SYM IProcess *SpawnNew(const AuString &app, const AuList<AuString> &args)
|
|
{
|
|
return _new ProcessImpl(app, args);
|
|
}
|
|
|
|
AUKN_SYM void SpawnRelease(IProcess *process)
|
|
{
|
|
SafeDelete<ProcessImpl *>(process);
|
|
}
|
|
} |