From 8f895f075701d8a8968ccf4af5850899a60331ed Mon Sep 17 00:00:00 2001 From: Kittycannon Date: Mon, 7 Sep 2026 15:24:52 -0600 Subject: [PATCH] added process --- desktoplib.code-workspace | 2 +- src/desktoplib/os/common_proc.hpp | 33 ++++++ src/desktoplib/os/unix_proc.cpp | 154 +++++++++++++++++++++++++++ src/desktoplib/os/windows_proc.cpp | 165 +++++++++++++++++++++++++++++ src/desktoplib/proc/Process.cpp | 59 +++++++++++ src/desktoplib/proc/Process.hpp | 49 ++------- 6 files changed, 420 insertions(+), 42 deletions(-) create mode 100644 src/desktoplib/os/common_proc.hpp create mode 100644 src/desktoplib/os/unix_proc.cpp create mode 100644 src/desktoplib/os/windows_proc.cpp diff --git a/desktoplib.code-workspace b/desktoplib.code-workspace index bbf19a1..d25fa26 100644 --- a/desktoplib.code-workspace +++ b/desktoplib.code-workspace @@ -5,6 +5,6 @@ } ], "settings": { - "terminal.integrated.defaultProfile.windows": "MSYS2 UCRT", + "terminal.integrated.defaultProfile.windows": "MSYS2 UCRT" } } \ No newline at end of file diff --git a/src/desktoplib/os/common_proc.hpp b/src/desktoplib/os/common_proc.hpp new file mode 100644 index 0000000..7353848 --- /dev/null +++ b/src/desktoplib/os/common_proc.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +namespace ckitty { + + namespace proc { + + namespace os { + + struct ProcessInfo; + + ProcessInfo* createProcess(const std::string& exec, const std::vector& args); + + void destroyProcess(ProcessInfo* info); + + bool isRunning(ProcessInfo* info); + + int getExitCode(ProcessInfo* info); + + void writeInput(ProcessInfo* info, std::string_view data); + + std::string readOutput(ProcessInfo* info); + + std::string readError(ProcessInfo* info); + + } + + } + +} \ No newline at end of file diff --git a/src/desktoplib/os/unix_proc.cpp b/src/desktoplib/os/unix_proc.cpp new file mode 100644 index 0000000..029a6c9 --- /dev/null +++ b/src/desktoplib/os/unix_proc.cpp @@ -0,0 +1,154 @@ +#if defined(__unix__) || defined(__APPLE__) || defined(__linux__) + +#include "common_proc.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace ckitty { + namespace proc { + namespace os { + + struct ProcessInfo { + pid_t pid = -1; + int stdin_fd = -1; + int stdout_fd = -1; + int stderr_fd = -1; + bool running = false; + std::string output; + std::string error; + }; + + ProcessInfo* createProcess(const std::string& exec, const std::vector& args) { + int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2]; + if (pipe(stdin_pipe) != 0) return nullptr; + if (pipe(stdout_pipe) != 0) { close(stdin_pipe[0]); close(stdin_pipe[1]); return nullptr; } + if (pipe(stderr_pipe) != 0) { close(stdin_pipe[0]); close(stdin_pipe[1]); close(stdout_pipe[0]); close(stdout_pipe[1]); return nullptr; } + + pid_t pid = fork(); + if (pid == 0) { + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stderr_pipe[0]); + + dup2(stdin_pipe[0], STDIN_FILENO); + dup2(stdout_pipe[1], STDOUT_FILENO); + dup2(stderr_pipe[1], STDERR_FILENO); + + close(stdin_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[1]); + + std::vector argv; + argv.push_back(const_cast(exec.c_str())); + for (const auto& arg : args) { + argv.push_back(const_cast(arg.c_str())); + } + argv.push_back(nullptr); + + execvp(argv[0], argv.data()); + _exit(127); + } + + if (pid < 0) { + close(stdin_pipe[0]); close(stdin_pipe[1]); + close(stdout_pipe[0]); close(stdout_pipe[1]); + close(stderr_pipe[0]); close(stderr_pipe[1]); + return nullptr; + } + + close(stdin_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[1]); + + int flags = fcntl(stdout_pipe[0], F_GETFL, 0); + fcntl(stdout_pipe[0], F_SETFL, flags | O_NONBLOCK); + flags = fcntl(stderr_pipe[0], F_GETFL, 0); + fcntl(stderr_pipe[0], F_SETFL, flags | O_NONBLOCK); + + auto* info = new ProcessInfo(); + info->pid = pid; + info->stdin_fd = stdin_pipe[1]; + info->stdout_fd = stdout_pipe[0]; + info->stderr_fd = stderr_pipe[0]; + info->running = true; + + return info; + } + + void destroyProcess(ProcessInfo* info) { + if (!info) return; + if (info->running) { + kill(info->pid, SIGKILL); + waitpid(info->pid, nullptr, 0); + } + if (info->stdin_fd != -1) close(info->stdin_fd); + if (info->stdout_fd != -1) close(info->stdout_fd); + if (info->stderr_fd != -1) close(info->stderr_fd); + delete info; + } + + bool isRunning(ProcessInfo* info) { + if (!info || !info->running) return false; + int status = 0; + pid_t result = waitpid(info->pid, &status, WNOHANG); + if (result == info->pid) { + info->running = false; + char buf[4096]; + ssize_t n; + while ((n = read(info->stdout_fd, buf, sizeof(buf))) > 0) { + info->output.append(buf, n); + } + while ((n = read(info->stderr_fd, buf, sizeof(buf))) > 0) { + info->error.append(buf, n); + } + return false; + } + return true; + } + + int getExitCode(ProcessInfo* info) { + if (!info) return 0; + if (info->running) return 0; + int status = 0; + waitpid(info->pid, &status, WNOHANG); + if (WIFEXITED(status)) return WEXITSTATUS(status); + return 1; + } + + void writeInput(ProcessInfo* info, std::string_view data) { + if (!info || !info->running) return; + write(info->stdin_fd, data.data(), data.size()); + } + + std::string readOutput(ProcessInfo* info) { + if (!info) return ""; + char buf[4096]; + ssize_t n = read(info->stdout_fd, buf, sizeof(buf)); + if (n > 0) { + info->output.append(buf, n); + return info->output.substr(info->output.size() - n, n); + } + return ""; + } + + std::string readError(ProcessInfo* info) { + if (!info) return ""; + char buf[4096]; + ssize_t n = read(info->stderr_fd, buf, sizeof(buf)); + if (n > 0) { + info->error.append(buf, n); + return info->error.substr(info->error.size() - n, n); + } + return ""; + } + + } + } +} + +#endif \ No newline at end of file diff --git a/src/desktoplib/os/windows_proc.cpp b/src/desktoplib/os/windows_proc.cpp new file mode 100644 index 0000000..b59a1c6 --- /dev/null +++ b/src/desktoplib/os/windows_proc.cpp @@ -0,0 +1,165 @@ +#ifdef _WIN32 + +#include "common_proc.hpp" +#include +#include +#include +#include + +namespace ckitty { + namespace proc { + namespace os { + + struct ProcessInfo { + PROCESS_INFORMATION pi = {}; + HANDLE hStdin = nullptr; + HANDLE hStdout = nullptr; + HANDLE hStderr = nullptr; + bool running = false; + std::string output; + std::string error; + }; + + ProcessInfo* createProcess(const std::string& exec, const std::vector& args) { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = nullptr; + + HANDLE hStdinRead = nullptr, hStdinWrite = nullptr; + HANDLE hStdoutRead = nullptr, hStdoutWrite = nullptr; + HANDLE hStderrRead = nullptr, hStderrWrite = nullptr; + + if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) return nullptr; + if (!CreatePipe(&hStderrRead, &hStderrWrite, &sa, 0)) { + CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite); + return nullptr; + } + if (!CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0)) { + CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite); + CloseHandle(hStderrRead); CloseHandle(hStderrWrite); + return nullptr; + } + + SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(hStderrRead, HANDLE_FLAG_INHERIT, 0); + SetHandleInformation(hStdinWrite, HANDLE_FLAG_INHERIT, 0); + + STARTUPINFOA si = { sizeof(STARTUPINFOA) }; + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdOutput = hStdoutWrite; + si.hStdError = hStderrWrite; + si.hStdInput = hStdinRead; + + std::string cmdLine = exec; + for (const auto& arg : args) { + cmdLine += ' '; + cmdLine += arg; + } + + PROCESS_INFORMATION pi = {}; + BOOL result = CreateProcessA( + nullptr, + &cmdLine[0], + nullptr, + nullptr, + TRUE, + 0, + nullptr, + nullptr, + &si, + &pi + ); + + CloseHandle(hStdoutWrite); + CloseHandle(hStderrWrite); + CloseHandle(hStdinRead); + + if (!result) { + CloseHandle(hStdoutRead); + CloseHandle(hStderrRead); + CloseHandle(hStdinWrite); + return nullptr; + } + + auto* info = new ProcessInfo(); + info->pi = pi; + info->hStdin = hStdinWrite; + info->hStdout = hStdoutRead; + info->hStderr = hStderrRead; + info->running = true; + + return info; + } + + void destroyProcess(ProcessInfo* info) { + if (!info) return; + if (info->running) { + TerminateProcess(info->pi.hProcess, 1); + WaitForSingleObject(info->pi.hProcess, 1000); + } + if (info->pi.hProcess) CloseHandle(info->pi.hProcess); + if (info->pi.hThread) CloseHandle(info->pi.hThread); + if (info->hStdin) CloseHandle(info->hStdin); + if (info->hStdout) CloseHandle(info->hStdout); + if (info->hStderr) CloseHandle(info->hStderr); + delete info; + } + + bool isRunning(ProcessInfo* info) { + if (!info || !info->running) return false; + DWORD result = WaitForSingleObject(info->pi.hProcess, 0); + if (result == WAIT_OBJECT_0) { + info->running = false; + return false; + } + return true; + } + + int getExitCode(ProcessInfo* info) { + if (!info) return 0; + if (info->running) return 0; + DWORD code = 0; + GetExitCodeProcess(info->pi.hProcess, &code); + return static_cast(code); + } + + void writeInput(ProcessInfo* info, std::string_view data) { + if (!info || !info->running) return; + DWORD written = 0; + WriteFile(info->hStdin, data.data(), static_cast(data.size()), &written, nullptr); + } + + std::string readOutput(ProcessInfo* info) { + if (!info) return ""; + char buf[4096]; + DWORD read = 0; + DWORD avail = 0; + if (PeekNamedPipe(info->hStdout, nullptr, 0, nullptr, &avail, nullptr) && avail > 0) { + if (ReadFile(info->hStdout, buf, std::min(avail, sizeof(buf)), &read, nullptr) && read > 0) { + info->output.append(buf, read); + return info->output.substr(info->output.size() - read, read); + } + } + return ""; + } + + std::string readError(ProcessInfo* info) { + if (!info) return ""; + char buf[4096]; + DWORD read = 0; + DWORD avail = 0; + if (PeekNamedPipe(info->hStderr, nullptr, 0, nullptr, &avail, nullptr) && avail > 0) { + if (ReadFile(info->hStderr, buf, std::min(avail, sizeof(buf)), &read, nullptr) && read > 0) { + info->error.append(buf, read); + return info->error.substr(info->error.size() - read, read); + } + } + return ""; + } + + } + } +} + +#endif \ No newline at end of file diff --git a/src/desktoplib/proc/Process.cpp b/src/desktoplib/proc/Process.cpp index e69de29..eb7524c 100644 --- a/src/desktoplib/proc/Process.cpp +++ b/src/desktoplib/proc/Process.cpp @@ -0,0 +1,59 @@ +#include "Process.hpp" + +#include + +namespace ckitty { + + Process::~Process() { + if (ProcessInfo) { + ckitty::proc::os::destroyProcess(ProcessInfo); + ProcessInfo = nullptr; + } + } + + void Process::setExec(std::string_view view) { + exec_ = view; + } + + void Process::addParam(std::string_view param) { + args_.push_back(std::string(param)); + } + + void Process::run() { + if (ProcessInfo) return; + ProcessInfo = ckitty::proc::os::createProcess(exec_, args_); + } + + bool Process::isRunning() { + if (!ProcessInfo) return false; + return ckitty::proc::os::isRunning(ProcessInfo); + } + + int Process::stopCode() { + if (!ProcessInfo) return 0; + return ckitty::proc::os::getExitCode(ProcessInfo); + } + + void Process::addInput(std::string_view view) { + if (!ProcessInfo) return; + ckitty::proc::os::writeInput(ProcessInfo, view); + } + + std::string_view Process::getOutput() { + if (!ProcessInfo) return ""; + static std::string accumulated; + std::string fresh = ckitty::proc::os::readOutput(ProcessInfo); + if (!fresh.empty()) { + accumulated += fresh; + } + return accumulated; + } + + std::string_view Process::nextOutput() { + if (!ProcessInfo) return ""; + static std::string fresh; + fresh = ckitty::proc::os::readOutput(ProcessInfo); + return fresh; + } + +} \ No newline at end of file diff --git a/src/desktoplib/proc/Process.hpp b/src/desktoplib/proc/Process.hpp index a0fa67d..884b9a8 100644 --- a/src/desktoplib/proc/Process.hpp +++ b/src/desktoplib/proc/Process.hpp @@ -1,66 +1,33 @@ #pragma once -#include #include #include -#include -#include +#include + +#include namespace ckitty { - /** - * Represents a process, which is executed by an OS. - * It exposes streams to the input and output of the process. - */ class Process { private: + ckitty::proc::os::ProcessInfo* ProcessInfo = nullptr; + std::string exec_; + std::vector args_; public: - - Process(); - + Process() = default; ~Process(); - public: - - /** - * Sets the program string. - */ void setExec(std::string_view view); - - /** - * Sets the parameters, with a map map. - * They can also be passed as a raw string in the setExec method. - */ - void setParams(); - - /** - * Starts executing the program. - */ + void addParam(std::string_view param); void run(); bool isRunning(); - - /** - * Returns the stop code. - * Returns 0 if the code is still running. - */ int stopCode(); void addInput(std::string_view view); - - /** - * Receives all the output so far. - */ std::string_view getOutput(); - - /** - * Receives the output fresh from the pipe. - */ std::string_view nextOutput(); - - // same pipe for error... - }; } \ No newline at end of file