From 91b5eef3dc154013e8ace7349c373a652bb6676a Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Sun, 19 Nov 2023 22:36:02 +0600 Subject: [PATCH] Process.h implementations for unix and windows --- src/CompilationScenario.c | 11 +-- src/Process.c | 69 ----------------- src/Process.h | 22 ------ src/cbuilld.c | 2 + src/process/Process.h | 33 ++++++++ src/process/Process_posix.c | 99 ++++++++++++++++++++++++ src/process/Process_windows.c | 141 ++++++++++++++++++++++++++++++++++ 7 files changed, 281 insertions(+), 96 deletions(-) delete mode 100644 src/Process.c delete mode 100644 src/Process.h create mode 100644 src/process/Process.h create mode 100644 src/process/Process_posix.c create mode 100644 src/process/Process_windows.c diff --git a/src/CompilationScenario.c b/src/CompilationScenario.c index 4aa2022..cdde033 100644 --- a/src/CompilationScenario.c +++ b/src/CompilationScenario.c @@ -1,7 +1,7 @@ #include #include "CompilationScenario.h" #include "../kerep/src/Filesystem/filesystem.h" -#include "Process.h" +#include "process/process.h" kt_define(Language, NULL, NULL); kt_define(Tool, NULL, NULL); @@ -314,15 +314,16 @@ Maybe Tool_exec(Tool* tool){ Autoarr_add(args_ar, arg)); const char** args = (const char**)Autoarr_toArray(args_ar); + i32 argc = Autoarr_length(args_ar); Autoarr_freeWithoutMembers(args_ar, true); - Process* tool_proc = NULL; - try(process_start(tool->exe_file, args, true), _mtp, - tool_proc = _mtp.value.VoidPtr); + Process tool_proc; + try(process_start(&tool_proc, tool->exe_file, args, argc, true), _m5512, Autoarr_freeWithoutMembers(sources, true)) // TODO wrap tool_proc->io - process_waitForExit(tool_proc); + process_waitForExit(&tool_proc); + Autoarr_freeWithoutMembers(sources, true); return MaybeNull; } diff --git a/src/Process.c b/src/Process.c deleted file mode 100644 index 344a1c5..0000000 --- a/src/Process.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "Process.h" -#ifdef __MINGW32__ -#include -#define pipe(fds) _pipe(fds, 1024, _O_BINARY) -int fork(void); // idk where is it's header, different compilers use their own implementations -#endif - -void Process_close(Process* p){ - p->id=0; - close(p->io.input); - close(p->io.output); - close(p->io.error); -} - -kt_define(Process, (freeMembers_t)Process_close, NULL) - -#define throw_if_negative(expr) if(expr == -1) throw(#expr " error"); -#define safethrow_if_negative(expr) if(expr == -1) safethrow(#expr " error",;); - -Maybe process_start(const char* file_path, const char** args, bool use_PATH){ - int input_pipe[2]; - int output_pipe[2]; - int error_pipe[2]; - safethrow_if_negative(pipe(input_pipe)); - safethrow_if_negative(pipe(output_pipe)); - safethrow_if_negative(pipe(error_pipe)); - - int pid = fork(); - if(pid == -1) - safethrow("fork() error", ;); - - // child process - if(pid == 0){ - throw_if_negative(close(input_pipe[1])); // close writing - throw_if_negative(close(output_pipe[0])); // close reading - throw_if_negative(close(error_pipe[0])); // close reading - - // redirect io streams to pipes - throw_if_negative(dup2(input_pipe[0], STDIN_FILENO)); - throw_if_negative(dup2(output_pipe[1], STDOUT_FILENO)); - throw_if_negative(dup2(error_pipe[1], STDERR_FILENO)); - - // start new process - int rzlt = use_PATH ? execvp(file_path, (char* const*)args) : execv (file_path, (char* const*)args); - if(rzlt != 0) - - // end child process - exit(rzlt); - return MaybeNull; - } - - // parent process - safethrow_if_negative(close(input_pipe[0])); // close reading - safethrow_if_negative(close(output_pipe[1])); // close writing - safethrow_if_negative(close(error_pipe[1])); // close writing - - Process* p = malloc(sizeof(Process)); - p->file_path = file_path; - p->args = args; - p->id=pid; - p->io.input=input_pipe[1]; - p->io.output=output_pipe[0]; - p->io.error=error_pipe[0]; - return SUCCESS(UniHeapPtr(Process, p)); -} - -Maybe process_waitForExit(Process* p){ - safethrow(ERR_NOTIMPLEMENTED, ;); -} diff --git a/src/Process.h b/src/Process.h deleted file mode 100644 index 22fbb08..0000000 --- a/src/Process.h +++ /dev/null @@ -1,22 +0,0 @@ -#include "../kerep/src/base/base.h" -#include - -typedef struct { - int input; - int output; - int error; -} io; - -typedef struct { - int id; - const char* file_path; - const char** args; - io io; -} Process; -kt_declare(Process); - -///@return Maybe -Maybe process_start(const char* file_path, const char** args, bool use_PATH); - -///@return Maybe -Maybe process_waitForExit(Process* p); \ No newline at end of file diff --git a/src/cbuilld.c b/src/cbuilld.c index 76b3941..ac85b44 100644 --- a/src/cbuilld.c +++ b/src/cbuilld.c @@ -2,6 +2,7 @@ #include "../kerep/src/Filesystem/filesystem.h" #include "../kerep/src/DtsodParser/DtsodV24.h" #include "CompilationScenario.h" +#include "process/process.h" #ifndef OS #error undefined OS @@ -42,6 +43,7 @@ int main(const int argc, const char** argv){ kt_register(CompilationScenario); kt_register(Language); kt_register(Tool); + kt_register(Process); kt_endInit(); tasks = Autoarr_create(Pointer, 16, 32); diff --git a/src/process/Process.h b/src/process/Process.h new file mode 100644 index 0000000..380867d --- /dev/null +++ b/src/process/Process.h @@ -0,0 +1,33 @@ +#include "../../kerep/src/base/base.h" + +#ifdef _WIN32 +#define USE_WINDOWS_PROCESS_API +typedef void* PipeHandle; +#else +typedef int PipeHandle; +#endif + +typedef struct Process { + int id; + const char* file_path; + const char** args; + PipeHandle input; + PipeHandle output; + PipeHandle error; +#ifdef USE_WINDOWS_PROCESS_API + void* _winProcHandle; +#endif +} Process; +kt_declare(Process); + +///@param search_in_PATH if true and file_path doesn't contain path separator characters, will search in PATH for the file_path +///@return Maybe +Maybe process_start(Process* ptr, const char* file_path, const char** args, int argc, bool search_in_PATH); + +///@return Maybe +Maybe process_waitForExit(Process* p); + +///@return Maybe +Maybe Process_stop(Process* p); + +i32 PipeHandle_read(PipeHandle pipe, char* buf, i32 bufsize); diff --git a/src/process/Process_posix.c b/src/process/Process_posix.c new file mode 100644 index 0000000..24db1f8 --- /dev/null +++ b/src/process/Process_posix.c @@ -0,0 +1,99 @@ +#include "process.h" + +#ifndef USE_WINDOWS_PROCESS_API + +#include +#include +#include +#include +extern int kill (__pid_t __pid, int __sig) __THROW; +extern void * memset(void * __dst, int __val, size_t __n); + +#define throw_if_negative(expr) if(expr < 0) throw(cptr_concat(#expr " exited with error ", toString_i64(errno))); +#define safethrow_if_negative(expr) if(expr < 0) safethrow(cptr_concat(#expr " exited with error ", toString_i64(errno)), ;); + +Maybe process_start(Process* p, const char* file_path, const char** args, int argc, bool search_in_PATH){ + memset(p, 0, sizeof(Process)); + int input_pipe[2]; + int output_pipe[2]; + int error_pipe[2]; + safethrow_if_negative(pipe(input_pipe)); + safethrow_if_negative(pipe(output_pipe)); + safethrow_if_negative(pipe(error_pipe)); + + int pid = fork(); + if(pid == -1) + safethrow("fork() error", ;); + + // child process + if(pid == 0){ + printf("child"); + throw_if_negative(close(input_pipe[1])); // close writing + throw_if_negative(close(output_pipe[0])); // close reading + throw_if_negative(close(error_pipe[0])); // close reading + + // redirect io streams to pipes + throw_if_negative(dup2(input_pipe[0], STDIN_FILENO)); + throw_if_negative(dup2(output_pipe[1], STDOUT_FILENO)); + throw_if_negative(dup2(error_pipe[1], STDERR_FILENO)); + + // + const char** argv = malloc(sizeof(char*) * (argc+2)); + argv[0] = file_path; + for(int i=0; ifile_path = file_path; + p->args = args; + p->id=pid; + p->input=input_pipe[1]; + p->output=output_pipe[0]; + p->error=error_pipe[0]; + return MaybeNull; +} + +Maybe Process_closeHandles(Process* p){ + safethrow_if_negative(close(p->input)); + safethrow_if_negative(close(p->output)); + safethrow_if_negative(close(p->error)); + p->id=0; + return MaybeNull; +} + +Maybe process_waitForExit(Process* p){ + int wstatus=0; + if(waitpid(p->id, &wstatus, 0) == -1) + safethrow("waitpid() error", ;); + if(!WIFEXITED(wstatus)) + safethrow(ERR_NOTIMPLEMENTED, ;) + int exitCode = WEXITSTATUS(wstatus); + if(exitCode != 0) + safethrow(cptr_concat("process ", toString_i64(p->id), " exited with code ", toString_i64(exitCode)), ;) + Process_closeHandles(p); + return MaybeNull; +} + +Maybe Process_stop(Process* p){ + safethrow_if_negative(kill(p->id, SIGINT)); + try(Process_closeHandles(p), _m864, ;); + return MaybeNull; +} + +kt_define(Process, (freeMembers_t)(void*)Process_stop, NULL) + +i32 PipeHandle_read(PipeHandle pipe, char* buf, i32 bufsize){ + return read(pipe, buf, bufsize); +} + +#endif \ No newline at end of file diff --git a/src/process/Process_windows.c b/src/process/Process_windows.c new file mode 100644 index 0000000..1fa3e0d --- /dev/null +++ b/src/process/Process_windows.c @@ -0,0 +1,141 @@ +#include "process.h" + +#ifdef USE_WINDOWS_PROCESS_API +#include +#include "../../kerep/src/String/StringBuilder.h" + +#define safethrow_if_false(expr) if(!expr) safethrow(cptr_concat(#expr " exited with error ", toString_i64(GetLastError())), ;); + +Maybe process_start(Process* p, const char* file_path, const char** args, int argc, bool search_in_PATH){ + memset(p, 0, sizeof(Process)); + if(search_in_PATH && !cptr_contains(file_path, "\\")){ + LPSTR lpFilePart; + char search_rezult[MAX_PATH]; + safethrow_if_false(SearchPath( NULL, file_path, NULL, MAX_PATH, search_rezult, &lpFilePart)) + file_path = cptr_copy(search_rezult); + } + + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + // Set the bInheritHandle flag so pipe handles are inherited + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + HANDLE in_pipe_r = NULL; + HANDLE in_pipe_w = NULL; + HANDLE out_pipe_r = NULL; + HANDLE out_pipe_w = NULL; + HANDLE err_pipe_r = NULL; + HANDLE err_pipe_w = NULL; + + // Create a pipe for the child process's STDIN. + safethrow_if_false( CreatePipe(&in_pipe_r, &in_pipe_w, &saAttr, 0)); + // Ensure the write handle to the pipe for STDIN is not inherited. + safethrow_if_false( SetHandleInformation(in_pipe_w, HANDLE_FLAG_INHERIT, 0) ); + + // Create a pipe for the child process's STDOUT. + safethrow_if_false( CreatePipe(&out_pipe_r, &out_pipe_w, &saAttr, 0) ) + // Ensure the read handle to the pipe for STDOUT is not inherited. + safethrow_if_false( SetHandleInformation(out_pipe_r, HANDLE_FLAG_INHERIT, 0) ); + + // Create a pipe for the child process's STDERR. + safethrow_if_false( CreatePipe(&err_pipe_r, &err_pipe_w, &saAttr, 0) ); + // Ensure the read handle to the pipe for STDERR is not inherited. + safethrow_if_false( SetHandleInformation(err_pipe_r, HANDLE_FLAG_INHERIT, 0) ) + + STARTUPINFO si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.hStdInput = in_pipe_r; + si.hStdOutput = out_pipe_w; + si.hStdError = err_pipe_w; + si.dwFlags |= STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + + StringBuilder* b = StringBuilder_create(); + + StringBuilder_append_char(b, '"'); + StringBuilder_append_cptr(b, file_path); + StringBuilder_append_char(b, '"'); + StringBuilder_append_char(b, ' '); + for(int i = 0; i < argc; i++) { + StringBuilder_append_char(b, '"'); + StringBuilder_append_cptr(b, args[i]); + StringBuilder_append_char(b, '"'); + StringBuilder_append_char(b, ' '); + } + string args_str = StringBuilder_build(b); + + // Start the child process. + if( !CreateProcess( + file_path, // Program executable path (optional) + args_str.ptr, // Command line args + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + TRUE, // Inherit IO handles + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure + ) { + safethrow(cptr_concat("process_start(", file_path, ", ", args_str, ", ", search_in_PATH ? "true" : "false", + ") error: CreateProcess() failed with error code ", toString_i64(GetLastError())), ;) + } + + + // Close thread handle and child process pipe ends + safethrow_if_false(CloseHandle(in_pipe_r)); + safethrow_if_false(CloseHandle(out_pipe_w)); + safethrow_if_false(CloseHandle(err_pipe_w)); + safethrow_if_false(CloseHandle(pi.hThread)); + + p->file_path = file_path; + p->args = args; + p->id=pi.dwProcessId; + p->_winProcHandle = pi.hProcess; + + p->input = in_pipe_w; + p->output= out_pipe_r; + p->error = err_pipe_r; + return MaybeNull; +} + +Maybe Process_closeHandles(Process* p){ + safethrow_if_false(CloseHandle(p->_winProcHandle)); + safethrow_if_false(CloseHandle(p->input)); + safethrow_if_false(CloseHandle(p->output)); + safethrow_if_false(CloseHandle(p->error)); + p->id=0; + return MaybeNull; +} + +Maybe process_waitForExit(Process* p){ + if(WaitForSingleObject(p->_winProcHandle, INFINITE) != 0) + safethrow("WaitForSingleObject() failed", ;); + DWORD exitCode = 0; + safethrow_if_false(GetExitCodeProcess(p->_winProcHandle, &exitCode)); + if(exitCode != 0) + safethrow(cptr_concat("process ", toString_i64(p->id), " exited with code ", toString_i64(exitCode)), ;) + Process_closeHandles(p); + return MaybeNull; +} + +Maybe Process_stop(Process* p){ + safethrow_if_false(TerminateProcess(p->_winProcHandle, 1)); + try(Process_closeHandles(p), _m864, ;); + return MaybeNull; +} + +kt_define(Process, (freeMembers_t)(void*)Process_stop, NULL) + +i32 PipeHandle_read(PipeHandle pipe, char* buf, i32 bufsize){ + DWORD bytesRead = 0; + if(!ReadFile(pipe, buf, bufsize, &bytesRead, NULL)) + return -1; + return bytesRead; +} + +#endif \ No newline at end of file