mirror of
https://github.com/hyprwm/hyprutils.git
synced 2024-12-22 10:49:48 +01:00
os: Add process
This commit is contained in:
parent
3f5293432b
commit
fd4be8b9ca
4 changed files with 203 additions and 0 deletions
|
@ -78,6 +78,14 @@ add_test(
|
|||
COMMAND hyprutils_math "math")
|
||||
add_dependencies(tests hyprutils_math)
|
||||
|
||||
add_executable(hyprutils_os "tests/os.cpp")
|
||||
target_link_libraries(hyprutils_os PRIVATE hyprutils PkgConfig::deps)
|
||||
add_test(
|
||||
NAME "OS"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
COMMAND hyprutils_os "os")
|
||||
add_dependencies(tests hyprutils_os)
|
||||
|
||||
# Installation
|
||||
install(TARGETS hyprutils)
|
||||
install(DIRECTORY "include/hyprutils" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
|
28
include/hyprutils/os/Process.hpp
Normal file
28
include/hyprutils/os/Process.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Hyprutils {
|
||||
namespace OS {
|
||||
class CProcess {
|
||||
public:
|
||||
/* Creates a process object, doesn't run yet */
|
||||
CProcess(const std::string& binary_, const std::vector<std::string>& args_);
|
||||
|
||||
/* Run the process, synchronously, get the stdout and stderr. False on fail */
|
||||
bool runSync();
|
||||
|
||||
/* Run the process, asynchronously. This will detach the process from this object (and process) and let it live a happy life. False on fail. */
|
||||
bool runAsync();
|
||||
|
||||
// only populated when ran sync
|
||||
const std::string& stdOut();
|
||||
const std::string& stdErr();
|
||||
|
||||
private:
|
||||
std::string binary, out, err;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
}
|
||||
}
|
149
src/os/Process.cpp
Normal file
149
src/os/Process.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include <hyprutils/os/Process.hpp>
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
Hyprutils::OS::CProcess::CProcess(const std::string& binary_, const std::vector<std::string>& args_) : binary(binary_), args(args_) {
|
||||
;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
execvp(binary.c_str(), (char* const*)argsC.data());
|
||||
exit(1);
|
||||
} else {
|
||||
// parent
|
||||
close(outPipe[1]);
|
||||
close(errPipe[1]);
|
||||
|
||||
waitpid(pid, nullptr, 0);
|
||||
|
||||
std::string readOutData;
|
||||
std::array<char, 1024> buf;
|
||||
buf.fill(0);
|
||||
|
||||
// wait for read
|
||||
size_t ret = 0;
|
||||
while ((ret = read(outPipe[0], buf.data(), 1023)) > 0) {
|
||||
readOutData += std::string{(char*)buf.data(), ret};
|
||||
}
|
||||
|
||||
out = readOutData;
|
||||
readOutData = "";
|
||||
|
||||
while ((ret = read(errPipe[0], buf.data(), 1023)) > 0) {
|
||||
readOutData += std::string{(char*)buf.data(), ret};
|
||||
}
|
||||
|
||||
err = readOutData;
|
||||
|
||||
close(outPipe[0]);
|
||||
close(errPipe[0]);
|
||||
|
||||
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;
|
||||
}
|
18
tests/os.cpp
Normal file
18
tests/os.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <hyprutils/os/Process.hpp>
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int ret = 0;
|
||||
|
||||
CProcess process("echo", {"Hello World!"});
|
||||
|
||||
EXPECT(process.runAsync(), true);
|
||||
EXPECT(process.runSync(), true);
|
||||
|
||||
EXPECT(process.stdOut(), std::string{"Hello World!\n"});
|
||||
EXPECT(process.stdErr(), std::string{""});
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in a new issue