Skip to content

Commit

Permalink
os: Add process
Browse files Browse the repository at this point in the history
  • Loading branch information
vaxerski committed Oct 14, 2024
1 parent 3f52934 commit fd4be8b
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
28 changes: 28 additions & 0 deletions include/hyprutils/os/Process.hpp
Original file line number Diff line number Diff line change
@@ -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 changes: 149 additions & 0 deletions src/os/Process.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions tests/os.cpp
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit fd4be8b

Please sign in to comment.