mirror of https://gitlab.com/cppit/jucipp
6 changed files with 7 additions and 575 deletions
@ -1,25 +0,0 @@
|
||||
#include "process.hpp" |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
Process::Process(const std::string &command, const std::string &path, |
||||
std::function<void(const char* bytes, size_t n)> read_stdout, |
||||
std::function<void(const char* bytes, size_t n)> read_stderr, |
||||
bool open_stdin, size_t buffer_size): |
||||
closed(true), read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) { |
||||
open(command, path); |
||||
async_read(); |
||||
} |
||||
|
||||
Process::~Process() { |
||||
close_fds(); |
||||
} |
||||
|
||||
Process::id_type Process::get_id() { |
||||
return data.id; |
||||
} |
||||
|
||||
bool Process::write(const std::string &data) { |
||||
return write(data.c_str(), data.size()); |
||||
} |
||||
@ -1,81 +0,0 @@
|
||||
#ifndef TINY_PROCESS_LIBRARY_HPP_ |
||||
#define TINY_PROCESS_LIBRARY_HPP_ |
||||
|
||||
#include <string> |
||||
#include <functional> |
||||
#include <vector> |
||||
#include <mutex> |
||||
#include <thread> |
||||
#include <mutex> |
||||
#ifdef _WIN32 |
||||
#include <windows.h> |
||||
#else |
||||
#include <sys/wait.h> |
||||
#endif |
||||
|
||||
///Create a new process given command and run path.
|
||||
///TODO: on Windows it is harder to specify which pipes to redirect.
|
||||
///Thus, at the moment, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false,
|
||||
///the stdout, stderr and stdin are sent to the parent process instead.
|
||||
///Compile with -DMSYS_PROCESS_USE_SH to run command using "sh -c [command]" on Windows as well.
|
||||
class Process { |
||||
public: |
||||
#ifdef _WIN32 |
||||
typedef DWORD id_type; //Process id type
|
||||
typedef HANDLE fd_type; //File descriptor type
|
||||
#else |
||||
typedef pid_t id_type; |
||||
typedef int fd_type; |
||||
#endif |
||||
private: |
||||
class Data { |
||||
public: |
||||
Data(); |
||||
id_type id; |
||||
#ifdef _WIN32 |
||||
HANDLE handle; |
||||
#endif |
||||
}; |
||||
public: |
||||
Process(const std::string &command, const std::string &path=std::string(), |
||||
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr, |
||||
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr, |
||||
bool open_stdin=false, |
||||
size_t buffer_size=131072); |
||||
~Process(); |
||||
|
||||
///Get the process id of the started process.
|
||||
id_type get_id(); |
||||
///Wait until process is finished, and return exit status.
|
||||
int get_exit_status(); |
||||
///Write to stdin.
|
||||
bool write(const char *bytes, size_t n); |
||||
///Write to stdin. Convenience function using write(const char *, size_t).
|
||||
bool write(const std::string &data); |
||||
///Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent.
|
||||
void close_stdin(); |
||||
|
||||
///Kill the process.
|
||||
void kill(bool force=false); |
||||
///Kill a given process id. Use kill(bool force) instead if possible.
|
||||
static void kill(id_type id, bool force=false); |
||||
|
||||
private: |
||||
Data data; |
||||
bool closed; |
||||
std::mutex close_mutex; |
||||
std::function<void(const char* bytes, size_t n)> read_stdout; |
||||
std::function<void(const char* bytes, size_t n)> read_stderr; |
||||
std::thread stdout_thread, stderr_thread; |
||||
bool open_stdin; |
||||
std::mutex stdin_mutex; |
||||
const size_t buffer_size; |
||||
|
||||
std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd; |
||||
|
||||
id_type open(const std::string &command, const std::string &path); |
||||
void async_read(); |
||||
void close_fds(); |
||||
}; |
||||
|
||||
#endif // TINY_PROCESS_LIBRARY_HPP_
|
||||
@ -1,183 +0,0 @@
|
||||
#include "process.hpp" |
||||
#include <cstdlib> |
||||
#include <unistd.h> |
||||
#include <signal.h> |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
Process::Data::Data(): id(-1) {} |
||||
|
||||
Process::id_type Process::open(const std::string &command, const std::string &path) { |
||||
if(open_stdin) |
||||
stdin_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
if(read_stdout) |
||||
stdout_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
if(read_stderr) |
||||
stderr_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
|
||||
int stdin_p[2], stdout_p[2], stderr_p[2]; |
||||
|
||||
if(stdin_fd && pipe(stdin_p)!=0) { |
||||
close(stdin_p[0]); |
||||
close(stdin_p[1]); |
||||
return -1; |
||||
} |
||||
if(stdout_fd && pipe(stdout_p)!=0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
close(stdout_p[0]); |
||||
close(stdout_p[1]); |
||||
return -1; |
||||
} |
||||
if(stderr_fd && pipe(stderr_p)!=0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
close(stderr_p[0]); |
||||
close(stderr_p[1]); |
||||
return -1; |
||||
} |
||||
|
||||
id_type pid = fork(); |
||||
|
||||
if (pid < 0) { |
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
if(stderr_fd) close(stderr_p[0]); |
||||
if(stderr_fd) close(stderr_p[1]); |
||||
return pid; |
||||
} |
||||
else if (pid == 0) { |
||||
if(stdin_fd) close(stdin_p[1]); |
||||
if(stdout_fd) close(stdout_p[0]); |
||||
if(stderr_fd) close(stderr_p[0]); |
||||
if(stdin_fd) dup2(stdin_p[0], 0); |
||||
if(stdout_fd) dup2(stdout_p[1], 1); |
||||
if(stderr_fd) dup2(stderr_p[1], 2); |
||||
|
||||
setpgid(0, 0); |
||||
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
|
||||
//TODO: One solution is: echo "command;exit"|script -q /dev/null
|
||||
|
||||
if(!path.empty()) |
||||
execl("/bin/sh", "sh", "-c", ("cd \""+path+"\" && "+command).c_str(), NULL); |
||||
else |
||||
execl("/bin/sh", "sh", "-c", command.c_str(), NULL); |
||||
|
||||
_exit(EXIT_FAILURE); |
||||
} |
||||
|
||||
if(stdin_fd) close(stdin_p[0]); |
||||
if(stdout_fd) close(stdout_p[1]); |
||||
if(stderr_fd) close(stderr_p[1]); |
||||
|
||||
if(stdin_fd) *stdin_fd = stdin_p[1]; |
||||
if(stdout_fd) *stdout_fd = stdout_p[0]; |
||||
if(stderr_fd) *stderr_fd = stderr_p[0]; |
||||
|
||||
closed=false; |
||||
data.id=pid; |
||||
return pid; |
||||
} |
||||
|
||||
void Process::async_read() { |
||||
if(data.id<=0) |
||||
return; |
||||
if(stdout_fd) { |
||||
stdout_thread=std::thread([this](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(*stdout_fd, buffer, buffer_size)) > 0) |
||||
read_stdout(buffer, static_cast<size_t>(n)); |
||||
}); |
||||
} |
||||
if(stderr_fd) { |
||||
stderr_thread=std::thread([this](){ |
||||
char buffer[buffer_size]; |
||||
ssize_t n; |
||||
while ((n=read(*stderr_fd, buffer, buffer_size)) > 0) |
||||
read_stderr(buffer, static_cast<size_t>(n)); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
int Process::get_exit_status() { |
||||
if(data.id<=0) |
||||
return -1; |
||||
int exit_status; |
||||
waitpid(data.id, &exit_status, 0); |
||||
close_mutex.lock(); |
||||
closed=true; |
||||
close_mutex.unlock(); |
||||
|
||||
close_fds(); |
||||
|
||||
return exit_status; |
||||
} |
||||
|
||||
void Process::close_fds() { |
||||
if(stdout_thread.joinable()) |
||||
stdout_thread.join(); |
||||
if(stderr_thread.joinable()) |
||||
stderr_thread.join(); |
||||
|
||||
if(stdin_fd) |
||||
close_stdin(); |
||||
if(stdout_fd) { |
||||
close(*stdout_fd); |
||||
stdout_fd.reset(); |
||||
} |
||||
if(stderr_fd) { |
||||
close(*stderr_fd); |
||||
stderr_fd.reset(); |
||||
} |
||||
} |
||||
|
||||
bool Process::write(const char *bytes, size_t n) { |
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
if(::write(*stdin_fd, bytes, n)>=0) { |
||||
stdin_mutex.unlock(); |
||||
return true; |
||||
} |
||||
else { |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
} |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
|
||||
void Process::close_stdin() { |
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
close(*stdin_fd); |
||||
stdin_fd.reset(); |
||||
} |
||||
stdin_mutex.unlock(); |
||||
} |
||||
|
||||
void Process::kill(bool force) { |
||||
close_mutex.lock(); |
||||
if(data.id>0 && !closed) { |
||||
if(force) |
||||
::kill(-data.id, SIGTERM); |
||||
else |
||||
::kill(-data.id, SIGINT); |
||||
} |
||||
close_mutex.unlock(); |
||||
} |
||||
|
||||
void Process::kill(id_type id, bool force) { |
||||
if(id<=0) |
||||
return; |
||||
if(force) |
||||
::kill(-id, SIGTERM); |
||||
else |
||||
::kill(-id, SIGINT); |
||||
} |
||||
@ -1,281 +0,0 @@
|
||||
#include "process.hpp" |
||||
#include <cstring> |
||||
#include "TlHelp32.h" |
||||
|
||||
#include <iostream> //TODO: remove |
||||
using namespace std; //TODO: remove
|
||||
|
||||
Process::Data::Data(): id(0), handle(NULL) {} |
||||
|
||||
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
|
||||
Process::id_type Process::open(const std::string &command, const std::string &path) { |
||||
if(open_stdin) |
||||
stdin_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
if(read_stdout) |
||||
stdout_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
if(read_stderr) |
||||
stderr_fd=std::unique_ptr<fd_type>(new fd_type); |
||||
|
||||
HANDLE g_hChildStd_IN_Rd = NULL; |
||||
HANDLE g_hChildStd_IN_Wr = NULL; |
||||
HANDLE g_hChildStd_OUT_Rd = NULL; |
||||
HANDLE g_hChildStd_OUT_Wr = NULL; |
||||
HANDLE g_hChildStd_ERR_Rd = NULL; |
||||
HANDLE g_hChildStd_ERR_Wr = NULL; |
||||
|
||||
SECURITY_ATTRIBUTES saAttr; |
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); |
||||
saAttr.bInheritHandle = TRUE; |
||||
saAttr.lpSecurityDescriptor = NULL; |
||||
|
||||
if(stdin_fd) { |
||||
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) |
||||
return 0; |
||||
if(!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { |
||||
CloseHandle(g_hChildStd_IN_Rd); |
||||
CloseHandle(g_hChildStd_IN_Wr); |
||||
return 0; |
||||
} |
||||
} |
||||
if(stdout_fd) { |
||||
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); |
||||
return 0; |
||||
} |
||||
if(!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); |
||||
CloseHandle(g_hChildStd_OUT_Rd); |
||||
CloseHandle(g_hChildStd_OUT_Wr); |
||||
return 0; |
||||
} |
||||
} |
||||
if(stderr_fd) { |
||||
if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); |
||||
return 0; |
||||
} |
||||
if(!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Wr); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Rd); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); |
||||
CloseHandle(g_hChildStd_ERR_Rd); |
||||
CloseHandle(g_hChildStd_ERR_Wr); |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
PROCESS_INFORMATION process_info; |
||||
STARTUPINFO siStartInfo; |
||||
|
||||
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); |
||||
|
||||
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); |
||||
siStartInfo.cb = sizeof(STARTUPINFO); |
||||
if(stdin_fd) siStartInfo.hStdInput = g_hChildStd_IN_Rd; |
||||
if(stdout_fd) siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; |
||||
if(stderr_fd) siStartInfo.hStdError = g_hChildStd_ERR_Wr; |
||||
if(stdin_fd || stdout_fd || stderr_fd) |
||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES; |
||||
|
||||
char* path_cstr; |
||||
if(path=="") |
||||
path_cstr=NULL; |
||||
else { |
||||
path_cstr=new char[path.size()+1]; |
||||
std::strcpy(path_cstr, path.c_str()); |
||||
} |
||||
|
||||
char* command_cstr; |
||||
#ifdef MSYS_PROCESS_USE_SH |
||||
size_t pos=0; |
||||
std::string sh_command=command; |
||||
while((pos=sh_command.find('\\', pos))!=std::string::npos) { |
||||
sh_command.replace(pos, 1, "\\\\\\\\"); |
||||
pos+=4; |
||||
} |
||||
pos=0; |
||||
while((pos=sh_command.find('\"', pos))!=std::string::npos) { |
||||
sh_command.replace(pos, 1, "\\\""); |
||||
pos+=2; |
||||
} |
||||
sh_command.insert(0, "sh -c \""); |
||||
sh_command+="\""; |
||||
command_cstr=new char[sh_command.size()+1]; |
||||
std::strcpy(command_cstr, sh_command.c_str()); |
||||
#else |
||||
command_cstr=new char[command.size()+1]; |
||||
std::strcpy(command_cstr, command.c_str()); |
||||
#endif |
||||
|
||||
BOOL bSuccess = CreateProcess(NULL, command_cstr, NULL, NULL, TRUE, 0, |
||||
NULL, path_cstr, &siStartInfo, &process_info); |
||||
delete[] path_cstr; |
||||
delete[] command_cstr; |
||||
|
||||
if(!bSuccess) { |
||||
CloseHandle(process_info.hProcess); |
||||
CloseHandle(process_info.hThread); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); |
||||
if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr); |
||||
return 0; |
||||
} |
||||
else { |
||||
CloseHandle(process_info.hThread); |
||||
if(stdin_fd) CloseHandle(g_hChildStd_IN_Rd); |
||||
if(stdout_fd) CloseHandle(g_hChildStd_OUT_Wr); |
||||
if(stderr_fd) CloseHandle(g_hChildStd_ERR_Wr); |
||||
} |
||||
|
||||
if(stdin_fd) *stdin_fd=g_hChildStd_IN_Wr; |
||||
if(stdout_fd) *stdout_fd=g_hChildStd_OUT_Rd; |
||||
if(stderr_fd) *stderr_fd=g_hChildStd_ERR_Rd; |
||||
|
||||
closed=false; |
||||
data.id=process_info.dwProcessId; |
||||
data.handle=process_info.hProcess; |
||||
return process_info.dwProcessId; |
||||
} |
||||
|
||||
void Process::async_read() { |
||||
if(data.id==0) |
||||
return; |
||||
if(stdout_fd) { |
||||
stdout_thread=std::thread([this](){ |
||||
DWORD n; |
||||
char buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(*stdout_fd, static_cast<CHAR*>(buffer), static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
read_stdout(buffer, static_cast<size_t>(n)); |
||||
} |
||||
}); |
||||
} |
||||
if(stderr_fd) { |
||||
stderr_thread=std::thread([this](){ |
||||
DWORD n; |
||||
char buffer[buffer_size]; |
||||
for (;;) { |
||||
BOOL bSuccess = ReadFile(*stderr_fd, static_cast<CHAR*>(buffer), static_cast<DWORD>(buffer_size), &n, NULL); |
||||
if(!bSuccess || n == 0) |
||||
break; |
||||
read_stderr(buffer, static_cast<size_t>(n)); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
int Process::get_exit_status() { |
||||
if(data.id==0) |
||||
return -1; |
||||
DWORD exit_status; |
||||
WaitForSingleObject(data.handle, INFINITE); |
||||
if(!GetExitCodeProcess(data.handle, &exit_status)) |
||||
exit_status=-1; |
||||
close_mutex.lock(); |
||||
CloseHandle(data.handle); |
||||
closed=true; |
||||
close_mutex.unlock(); |
||||
|
||||
close_fds(); |
||||
|
||||
return static_cast<int>(exit_status); |
||||
} |
||||
|
||||
void Process::close_fds() { |
||||
if(stdout_thread.joinable()) |
||||
stdout_thread.join(); |
||||
if(stderr_thread.joinable()) |
||||
stderr_thread.join(); |
||||
|
||||
if(stdin_fd) |
||||
close_stdin(); |
||||
if(stdout_fd) { |
||||
CloseHandle(*stdout_fd); |
||||
stdout_fd.reset(); |
||||
} |
||||
if(stderr_fd) { |
||||
CloseHandle(*stderr_fd); |
||||
stderr_fd.reset(); |
||||
} |
||||
} |
||||
|
||||
bool Process::write(const char *bytes, size_t n) { |
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
DWORD written; |
||||
BOOL bSuccess=WriteFile(*stdin_fd, bytes, static_cast<DWORD>(n), &written, NULL); |
||||
if(!bSuccess || written==0) { |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
else { |
||||
stdin_mutex.unlock(); |
||||
return true; |
||||
} |
||||
} |
||||
stdin_mutex.unlock(); |
||||
return false; |
||||
} |
||||
|
||||
void Process::close_stdin() { |
||||
stdin_mutex.lock(); |
||||
if(stdin_fd) { |
||||
CloseHandle(*stdin_fd); |
||||
stdin_fd.reset(); |
||||
} |
||||
stdin_mutex.unlock(); |
||||
} |
||||
|
||||
//Based on http://stackoverflow.com/a/1173396
|
||||
void Process::kill(bool force) { |
||||
close_mutex.lock(); |
||||
if(data.id>0 && !closed) { |
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
||||
if(snapshot) { |
||||
PROCESSENTRY32 process; |
||||
ZeroMemory(&process, sizeof(process)); |
||||
process.dwSize = sizeof(process); |
||||
if(Process32First(snapshot, &process)) { |
||||
do { |
||||
if(process.th32ParentProcessID==data.id) { |
||||
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID); |
||||
if(process_handle) TerminateProcess(process_handle, 2); |
||||
} |
||||
} while (Process32Next(snapshot, &process)); |
||||
} |
||||
} |
||||
TerminateProcess(data.handle, 2); |
||||
} |
||||
close_mutex.unlock(); |
||||
} |
||||
|
||||
//Based on http://stackoverflow.com/a/1173396
|
||||
void Process::kill(id_type id, bool force) { |
||||
if(id==0) |
||||
return; |
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
||||
if(snapshot) { |
||||
PROCESSENTRY32 process; |
||||
ZeroMemory(&process, sizeof(process)); |
||||
process.dwSize = sizeof(process); |
||||
if(Process32First(snapshot, &process)) { |
||||
do { |
||||
if(process.th32ParentProcessID==id) { |
||||
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID); |
||||
if(process_handle) TerminateProcess(process_handle, 2); |
||||
} |
||||
} while (Process32Next(snapshot, &process)); |
||||
} |
||||
} |
||||
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id); |
||||
if(process_handle) TerminateProcess(process_handle, 2); |
||||
} |
||||
Loading…
Reference in new issue