hyprutils/src/os/Process.cpp

223 lines
5.4 KiB
C++
Raw Normal View History

2024-10-14 22:26:41 +01:00
#include <hyprutils/os/Process.hpp>
using namespace Hyprutils::OS;
#include <csignal>
2024-10-14 22:26:41 +01:00
#include <cstdio>
#include <unistd.h>
#include <cstring>
#include <array>
#include <thread>
2024-10-14 22:26:41 +01:00
#include <sys/fcntl.h>
2024-10-14 22:26:41 +01:00
#include <sys/wait.h>
2024-11-11 22:33:38 +00:00
#include <sys/poll.h>
2024-10-14 22:26:41 +01:00
Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) {
;
}
2024-11-15 20:28:59 +00:00
void Hyprutils::OS::CProcess::addEnv(const std::string& name, const std::string& value) {
env.emplace_back(std::make_pair<>(name, value));
}
2024-10-14 22:26:41 +01:00
bool Hyprutils::OS::CProcess::runSync() {
int outPipe[2], errPipe[2];
if (pipe(outPipe))
return false;
if (pipe(errPipe)) {
close(outPipe[0]);
close(outPipe[1]);
return false;
}
int pid = fork();
if (pid == -1) {
close(outPipe[0]);
close(outPipe[1]);
close(outPipe[0]);
close(outPipe[1]);
return false;
}
if (!pid) {
// child
close(outPipe[0]);
close(errPipe[0]);
dup2(outPipe[1], 1 /* stdout */);
dup2(errPipe[1], 2 /* stderr */);
// build argv
std::vector<const char*> argsC;
argsC.emplace_back(strdup(binary.c_str()));
for (auto& arg : args) {
// TODO: does this leak? Can we just pipe c_str() as the strings won't be realloc'd?
argsC.emplace_back(strdup(arg.c_str()));
}
argsC.emplace_back(nullptr);
2024-11-15 20:28:59 +00:00
// pass env
for (auto& [n, v] : env) {
setenv(n.c_str(), v.c_str(), 1);
}
2024-10-14 22:26:41 +01:00
execvp(binary.c_str(), (char* const*)argsC.data());
exit(1);
} else {
// parent
close(outPipe[1]);
close(errPipe[1]);
out = "";
err = "";
2024-10-14 22:26:41 +01:00
std::array<char, 1024> buf;
buf.fill(0);
// wait for read
ssize_t ret = 0;
2024-10-14 22:26:41 +01:00
int fdFlags = fcntl(outPipe[0], F_GETFL, 0);
if (fcntl(outPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0)
return false;
fdFlags = fcntl(errPipe[0], F_GETFL, 0);
if (fcntl(errPipe[0], F_SETFL, fdFlags | O_NONBLOCK) < 0)
return false;
2024-10-14 22:26:41 +01:00
2024-11-11 22:33:38 +00:00
pollfd pollfds[2] = {
{
.fd = outPipe[0],
.events = POLLIN,
},
{
.fd = errPipe[0],
.events = POLLIN,
},
};
while (1337) {
int ret = poll(pollfds, 2, 5000);
if (ret < 0) {
if (errno == EINTR)
continue;
return false;
}
2024-11-11 22:33:38 +00:00
bool hupd = false;
2024-11-11 22:33:38 +00:00
for (size_t i = 0; i < 2; ++i) {
if (pollfds[i].revents & POLLHUP) {
hupd = true;
break;
}
}
2024-11-11 22:33:38 +00:00
if (hupd)
break;
2024-10-14 22:26:41 +01:00
2024-11-11 22:33:38 +00:00
if (pollfds[0].revents & POLLIN) {
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
out += std::string_view{(char*)buf.data(), (size_t)ret};
}
2024-11-11 22:33:38 +00:00
buf.fill(0);
}
2024-11-11 22:33:38 +00:00
if (pollfds[1].revents & POLLIN) {
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
err += std::string_view{(char*)buf.data(), (size_t)ret};
}
2024-11-11 22:33:38 +00:00
buf.fill(0);
}
}
2024-11-11 22:33:38 +00:00
// Final reads. Nonblock, so its ok.
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
out += std::string_view{(char*)buf.data(), (size_t)ret};
}
2024-10-14 22:26:41 +01:00
2024-11-11 22:33:38 +00:00
buf.fill(0);
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
err += std::string_view{(char*)buf.data(), (size_t)ret};
}
buf.fill(0);
2024-10-14 22:26:41 +01:00
close(outPipe[0]);
close(errPipe[0]);
2024-11-13 17:19:47 +00:00
// reap child
waitpid(pid, nullptr, 0);
2024-10-14 22:26:41 +01:00
return true;
}
return true;
}
bool Hyprutils::OS::CProcess::runAsync() {
int socket[2];
if (pipe(socket) != 0)
return false;
pid_t child, grandchild;
child = fork();
if (child < 0) {
close(socket[0]);
close(socket[1]);
return false;
}
if (child == 0) {
// run in child
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
grandchild = fork();
if (grandchild == 0) {
// run in grandchild
close(socket[0]);
close(socket[1]);
// build argv
std::vector<const char*> argsC;
argsC.emplace_back(strdup(binary.c_str()));
for (auto& arg : args) {
// TODO: does this leak? Can we just pipe c_str() as the strings won't be realloc'd?
argsC.emplace_back(strdup(arg.c_str()));
}
argsC.emplace_back(nullptr);
execvp(binary.c_str(), (char* const*)argsC.data());
// exit grandchild
_exit(0);
}
close(socket[0]);
write(socket[1], &grandchild, sizeof(grandchild));
close(socket[1]);
// exit child
_exit(0);
}
// run in parent
close(socket[1]);
read(socket[0], &grandchild, sizeof(grandchild));
close(socket[0]);
// clear child and leave grandchild to init
waitpid(child, NULL, 0);
return true;
}
const std::string& Hyprutils::OS::CProcess::stdOut() {
return out;
}
const std::string& Hyprutils::OS::CProcess::stdErr() {
return err;
}