Process.h implementations for unix and windows

This commit is contained in:
Timerix22 2023-11-19 22:36:02 +06:00
parent 1b5efdb146
commit 91b5eef3dc
7 changed files with 281 additions and 96 deletions

View File

@ -1,7 +1,7 @@
#include <unistd.h>
#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;
}

View File

@ -1,69 +0,0 @@
#include "Process.h"
#ifdef __MINGW32__
#include <fcntl.h>
#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, ;);
}

View File

@ -1,22 +0,0 @@
#include "../kerep/src/base/base.h"
#include <unistd.h>
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<Process*>
Maybe process_start(const char* file_path, const char** args, bool use_PATH);
///@return Maybe<void>
Maybe process_waitForExit(Process* p);

View File

@ -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);

33
src/process/Process.h Normal file
View File

@ -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<void>
Maybe process_start(Process* ptr, const char* file_path, const char** args, int argc, bool search_in_PATH);
///@return Maybe<void>
Maybe process_waitForExit(Process* p);
///@return Maybe<void>
Maybe Process_stop(Process* p);
i32 PipeHandle_read(PipeHandle pipe, char* buf, i32 bufsize);

View File

@ -0,0 +1,99 @@
#include "process.h"
#ifndef USE_WINDOWS_PROCESS_API
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
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; i<argc; i++){
argv[i+1] = args[i];
}
argv[argc+1] = NULL;
// start new process
int rzlt = search_in_PATH ? execvp(file_path, (char* const*)argv) : execv (file_path, (char* const*)argv);
exit(rzlt);
}
// 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
p->file_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

View File

@ -0,0 +1,141 @@
#include "process.h"
#ifdef USE_WINDOWS_PROCESS_API
#include <windows.h>
#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