AuroraRuntime/Source/Processes/Process.Linux.cpp
2021-06-27 22:25:29 +01:00

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