added process
This commit is contained in:
@@ -5,6 +5,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"terminal.integrated.defaultProfile.windows": "MSYS2 UCRT",
|
"terminal.integrated.defaultProfile.windows": "MSYS2 UCRT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ckitty {
|
||||||
|
|
||||||
|
namespace proc {
|
||||||
|
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
struct ProcessInfo;
|
||||||
|
|
||||||
|
ProcessInfo* createProcess(const std::string& exec, const std::vector<std::string>& 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
||||||
|
|
||||||
|
#include "common_proc.hpp"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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<std::string>& 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<char*> argv;
|
||||||
|
argv.push_back(const_cast<char*>(exec.c_str()));
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
argv.push_back(const_cast<char*>(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
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "common_proc.hpp"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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<std::string>& 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<int>(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeInput(ProcessInfo* info, std::string_view data) {
|
||||||
|
if (!info || !info->running) return;
|
||||||
|
DWORD written = 0;
|
||||||
|
WriteFile(info->hStdin, data.data(), static_cast<DWORD>(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<DWORD>(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<DWORD>(avail, sizeof(buf)), &read, nullptr) && read > 0) {
|
||||||
|
info->error.append(buf, read);
|
||||||
|
return info->error.substr(info->error.size() - read, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include "Process.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,66 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <sstream>
|
#include <vector>
|
||||||
#include <optional>
|
|
||||||
|
#include <desktoplib/os/common_proc.hpp>
|
||||||
|
|
||||||
namespace ckitty {
|
namespace ckitty {
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a process, which is executed by an OS.
|
|
||||||
* It exposes streams to the input and output of the process.
|
|
||||||
*/
|
|
||||||
class Process {
|
class Process {
|
||||||
private:
|
private:
|
||||||
|
ckitty::proc::os::ProcessInfo* ProcessInfo = nullptr;
|
||||||
|
std::string exec_;
|
||||||
|
std::vector<std::string> args_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Process() = default;
|
||||||
Process();
|
|
||||||
|
|
||||||
~Process();
|
~Process();
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the program string.
|
|
||||||
*/
|
|
||||||
void setExec(std::string_view view);
|
void setExec(std::string_view view);
|
||||||
|
void addParam(std::string_view param);
|
||||||
/**
|
|
||||||
* Sets the parameters, with a map<string, string> map.
|
|
||||||
* They can also be passed as a raw string in the setExec method.
|
|
||||||
*/
|
|
||||||
void setParams();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts executing the program.
|
|
||||||
*/
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
bool isRunning();
|
bool isRunning();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the stop code.
|
|
||||||
* Returns 0 if the code is still running.
|
|
||||||
*/
|
|
||||||
int stopCode();
|
int stopCode();
|
||||||
|
|
||||||
void addInput(std::string_view view);
|
void addInput(std::string_view view);
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives all the output so far.
|
|
||||||
*/
|
|
||||||
std::string_view getOutput();
|
std::string_view getOutput();
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives the output fresh from the pipe.
|
|
||||||
*/
|
|
||||||
std::string_view nextOutput();
|
std::string_view nextOutput();
|
||||||
|
|
||||||
// same pipe for error...
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user