From f173b7715cf1385db1cea67a3db07bb7d38174e9 Mon Sep 17 00:00:00 2001 From: pommicket Date: Sun, 1 Jan 2023 23:16:38 -0500 Subject: finish os.h amalgamation --- development.md | 10 +++ main.c | 2 - os-posix.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ os-win.c | 138 +++++++++++++++++++++++++++++++++++++++ os.h | 46 +++++++++++++ process-posix.c | 198 -------------------------------------------------------- process-win.c | 137 --------------------------------------- process.h | 50 -------------- 8 files changed, 390 insertions(+), 387 deletions(-) delete mode 100644 process-posix.c delete mode 100644 process-win.c delete mode 100644 process.h diff --git a/development.md b/development.md index 7c643a2..437443b 100644 --- a/development.md +++ b/development.md @@ -1,5 +1,15 @@ (work in progress) +As much as possible, OS-dependent functions should be put in `os.h/os-*.c`. +(But "pure" functions like `qsort_with_context`, which could +in theory be implemented on any platform with just plain C, should be put +in `util.c` even if they use OS-specific library functions.) + + +## Adding settings + +## Adding languages + ## Releasing When releasing a new version of `ted`: diff --git a/main.c b/main.c index 72a9ff7..97e4103 100644 --- a/main.c +++ b/main.c @@ -91,10 +91,8 @@ no_warn_end #if _WIN32 #include "os-win.c" -#include "process-win.c" #elif __unix__ #include "os-posix.c" -#include "process-posix.c" #else #error "Unrecognized operating system." #endif diff --git a/os-posix.c b/os-posix.c index 68e1d4f..193897a 100644 --- a/os-posix.c +++ b/os-posix.c @@ -1,6 +1,8 @@ #include "os.h" #include #include +#include +#include #include #include #include @@ -132,3 +134,197 @@ void time_sleep_ns(u64 ns) { while (nanosleep(&req, &rem) == EINTR) // sleep interrupted by signal req = rem; } + + +struct Process { + pid_t pid; + int stdout_pipe; + // only applicable if separate_stderr was specified. + int stderr_pipe; + int stdin_pipe; + char error[64]; +}; + +int process_get_id(void) { + return getpid(); +} + +static void set_nonblocking(int fd) { + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); +} + +bool process_run_ex(Process *proc, const char *command, const ProcessSettings *settings) { + memset(proc, 0, sizeof *proc); + + int stdin_pipe[2] = {0}, stdout_pipe[2] = {0}, stderr_pipe[2] = {0}; + if (pipe(stdin_pipe) != 0) { + strbuf_printf(proc->error, "%s", strerror(errno)); + return false; + } + if (pipe(stdout_pipe) != 0) { + strbuf_printf(proc->error, "%s", strerror(errno)); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + return false; + } + if (settings->separate_stderr) { + if (pipe(stderr_pipe) != 0) { + strbuf_printf(proc->error, "%s", strerror(errno)); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + return false; + } + } + + bool success = false; + pid_t pid = fork(); + if (pid == 0) { + // child process + chdir(settings->working_directory); + // put child in its own group. it will be in this group with all of its descendents, + // so by killing everything in the group, we kill all the descendents of this process. + // if we didn't do this, we would just be killing the sh process in process_kill. + setpgid(0, 0); + // pipe stuff + dup2(stdout_pipe[1], STDOUT_FILENO); + if (stderr_pipe[1]) + dup2(stderr_pipe[1], STDERR_FILENO); + else + dup2(stdout_pipe[1], STDERR_FILENO); + dup2(stdin_pipe[0], STDIN_FILENO); + // don't need these file descriptors anymore + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + if (stderr_pipe[0]) { + close(stderr_pipe[0]); + close(stderr_pipe[1]); + } + + char *program = "/bin/sh"; + char *argv[] = {program, "-c", (char *)command, NULL}; + if (execv(program, argv) == -1) { + dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno)); + exit(127); + } + } else if (pid > 0) { + // parent process + + // we're reading from (the child's) stdout/stderr and writing to stdin, + // so we don't need the write end of the stdout pipe or the + // read end of the stdin pipe. + close(stdout_pipe[1]); + if (stderr_pipe[1]) + close(stderr_pipe[1]); + close(stdin_pipe[0]); + // set pipes to non-blocking + if (!settings->stdout_blocking) + set_nonblocking(stdout_pipe[0]); + if (stderr_pipe[0] && !settings->stderr_blocking) + set_nonblocking(stderr_pipe[0]); + if (!settings->stdin_blocking) + set_nonblocking(stdin_pipe[1]); + proc->pid = pid; + proc->stdout_pipe = stdout_pipe[0]; + if (stderr_pipe[0]) + proc->stderr_pipe = stderr_pipe[0]; + proc->stdin_pipe = stdin_pipe[1]; + success = true; + } + return success; +} + +bool process_run(Process *proc, char const *command) { + const ProcessSettings settings = {0}; + return process_run_ex(proc, command, &settings); +} + + +char const *process_geterr(Process *p) { + return *p->error ? p->error : NULL; +} + +long long process_write(Process *proc, const char *data, size_t size) { + assert(proc->stdin_pipe); // check that process hasn't been killed + ssize_t bytes_written = write(proc->stdin_pipe, data, size); + if (bytes_written >= 0) { + return (long long)bytes_written; + } else if (errno == EAGAIN) { + return 0; + } else { + strbuf_printf(proc->error, "%s", strerror(errno)); + return -2; + } +} + +static long long process_read_fd(Process *proc, int fd, char *data, size_t size) { + assert(fd); + ssize_t bytes_read = read(fd, data, size); + if (bytes_read >= 0) { + return (long long)bytes_read; + } else if (errno == EAGAIN) { + return -1; + } else { + strbuf_printf(proc->error, "%s", strerror(errno)); + return -2; + } +} + +long long process_read(Process *proc, char *data, size_t size) { + return process_read_fd(proc, proc->stdout_pipe, data, size); +} + +long long process_read_stderr(Process *proc, char *data, size_t size) { + return process_read_fd(proc, proc->stderr_pipe, data, size); +} + +static void process_close_pipes(Process *proc) { + close(proc->stdin_pipe); + close(proc->stdout_pipe); + close(proc->stderr_pipe); + proc->stdin_pipe = 0; + proc->stdout_pipe = 0; + proc->stderr_pipe = 0; +} + +void process_kill(Process *proc) { + kill(-proc->pid, SIGKILL); // kill everything in process group + // get rid of zombie process + waitpid(proc->pid, NULL, 0); + proc->pid = 0; + process_close_pipes(proc); +} + +int process_check_status(Process *proc, char *message, size_t message_size) { + int wait_status = 0; + int ret = waitpid(proc->pid, &wait_status, WNOHANG); + if (ret == 0) { + // process still running + return 0; + } else if (ret > 0) { + if (WIFEXITED(wait_status)) { + process_close_pipes(proc); + int code = WEXITSTATUS(wait_status); + if (code == 0) { + str_printf(message, message_size, "exited successfully"); + return +1; + } else { + str_printf(message, message_size, "exited with code %d", code); + return -1; + } + } else if (WIFSIGNALED(wait_status)) { + process_close_pipes(proc); + str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status)); + return -1; + } + return 0; + } else { + // this process is gone or something? + process_close_pipes(proc); + str_printf(message, message_size, "process ended unexpectedly"); + return -1; + } +} diff --git a/os-win.c b/os-win.c index df32cdf..ff29359 100644 --- a/os-win.c +++ b/os-win.c @@ -126,3 +126,141 @@ void time_sleep_ns(u64 ns) { // windows.... Sleep((DWORD)(ns / 1000000)); } + +#error "@TODO : implement process_write, separate_stderr, working_directory" + +#include "process.h" + +struct Process { + HANDLE pipe_read, pipe_write; + HANDLE job; + PROCESS_INFORMATION process_info; + char error[200]; +}; + +static void get_last_error_str(char *out, size_t out_sz) { + size_t size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), out, (DWORD)out_sz - 1, NULL); + out[size] = 0; + char *cr = strchr(out, '\r'); + if (cr) *cr = '\0'; // get rid of carriage return+newline at end of error +} + +bool process_run(Process *process, char const *command) { + // thanks to https://stackoverflow.com/a/35658917 for the pipe code + // thanks to https://devblogs.microsoft.com/oldnewthing/20131209-00/?p=2433 for the job code + + bool success = false; + memset(process, 0, sizeof *process); + char *command_line = str_dup(command); + if (!command_line) { + strbuf_printf(process->error, "Out of memory."); + return false; + } + // we need to create a "job" for this, because when you kill a process on windows, + // all its children just keep going. so cmd.exe would die, but not the actual build process. + // jobs fix this, apparently. + HANDLE job = CreateJobObjectA(NULL, NULL); + if (job) { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0}; + job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof job_info); + HANDLE pipe_read, pipe_write; + SECURITY_ATTRIBUTES security_attrs = {sizeof(SECURITY_ATTRIBUTES)}; + security_attrs.bInheritHandle = TRUE; + if (CreatePipe(&pipe_read, &pipe_write, &security_attrs, 0)) { + STARTUPINFOA startup = {sizeof(STARTUPINFOA)}; + startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + startup.hStdOutput = pipe_write; + startup.hStdError = pipe_write; + startup.wShowWindow = SW_HIDE; + PROCESS_INFORMATION *process_info = &process->process_info; + if (CreateProcessA(NULL, command_line, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_SUSPENDED, + NULL, NULL, &startup, process_info)) { + // create a suspended process, add it to the job, then resume (unsuspend) the process + if (AssignProcessToJobObject(job, process_info->hProcess)) { + if (ResumeThread(process_info->hThread) != (DWORD)-1) { + process->job = job; + process->pipe_read = pipe_read; + process->pipe_write = pipe_write; + success = true; + } + } + if (!success) { + TerminateProcess(process_info->hProcess, 1); + CloseHandle(process_info->hProcess); + CloseHandle(process_info->hThread); + } + } else { + char buf[150]; + get_last_error_str(buf, sizeof buf); + strbuf_printf(process->error, "Couldn't run `%s`: %s", command, buf); + } + free(command_line); + if (!success) { + CloseHandle(pipe_read); + CloseHandle(pipe_write); + } + } else { + char buf[150]; + get_last_error_str(buf, sizeof buf); + strbuf_printf(process->error, "Couldn't create pipe: %s", buf); + } + if (!success) + CloseHandle(job); + } + return success; +} + +char const *process_geterr(Process *p) { + return *p->error ? p->error : NULL; +} + +long long process_read(Process *process, char *data, size_t size) { + DWORD bytes_read = 0, bytes_avail = 0, bytes_left = 0; + if (PeekNamedPipe(process->pipe_read, data, (DWORD)size, &bytes_read, &bytes_avail, &bytes_left)) { + if (bytes_read == 0) { + return -1; + } else { + ReadFile(process->pipe_read, data, (DWORD)size, &bytes_read, NULL); // make sure data is removed from pipe + return bytes_read; + } + } else { + char buf[150]; + get_last_error_str(buf, sizeof buf); + strbuf_printf(process->error, "Couldn't read from pipe: %s", buf); + return -2; + } +} + +void process_kill(Process *process) { + CloseHandle(process->job); + CloseHandle(process->pipe_read); + CloseHandle(process->pipe_write); + CloseHandle(process->process_info.hProcess); + CloseHandle(process->process_info.hThread); +} + +int process_check_status(Process *process, char *message, size_t message_size) { + HANDLE hProcess = process->process_info.hProcess; + DWORD exit_code = 1; + if (GetExitCodeProcess(hProcess, &exit_code)) { + if (exit_code == STILL_ACTIVE) { + return 0; + } else { + process_kill(process); + if (exit_code == 0) { + str_printf(message, message_size, "exited successfully"); + return +1; + } else { + str_printf(message, message_size, "exited with code %d", (int)exit_code); + return -1; + } + } + } else { + // something has gone wrong. + str_printf(message, message_size, "couldn't get process exit status"); + process_kill(process); + return -1; + } +} diff --git a/os.h b/os.h index ebf0317..4e49057 100644 --- a/os.h +++ b/os.h @@ -1,3 +1,4 @@ +// a bunch of OS-dependent functions #ifndef OS_H_ #define OS_H_ @@ -75,5 +76,50 @@ static void time_sleep_s(u64 s) { } +typedef struct Process Process; + +// zero everything except what you're using +typedef struct { + bool stdin_blocking; + bool stdout_blocking; + bool separate_stderr; + bool stderr_blocking; // not applicable if separate_stderr is false. + const char *working_directory; +} ProcessSettings; + +// get process ID of this process +int process_get_id(void); +// execute the given command (like if it was passed to system()). +// returns false on failure +bool process_run_ex(Process *proc, const char *command, const ProcessSettings *props); +// like process_run_ex, but with the default settings +bool process_run(Process *process, char const *command); +// returns the error last error produced, or NULL if there was no error. +char const *process_geterr(Process *process); +// write to stdin +// returns: +// -2 on error +// or a non-negative number indicating the number of bytes written. +long long process_write(Process *process, const char *data, size_t size); +// read from stdout+stderr +// returns: +// -2 on error +// -1 if no data is available right now +// 0 on end of file +// or a positive number indicating the number of bytes read to data (at most size) +long long process_read(Process *process, char *data, size_t size); +// like process_read, but reads stderr. +// this function ALWAYS RETURNS -2 if separate_stderr is not specified in the ProcessSettings. +// if separate_stderr is false, then both stdout and stderr will be sent via process_read. +long long process_read_stderr(Process *process, char *data, size_t size); +// Checks if the process has exited. Returns: +// -1 if the process returned a non-zero exit code, or got a signal. +// 1 if the process exited successfully +// 0 if the process hasn't exited. +// If message is not NULL, it will be set to a description of what happened (e.g. "exited successfully") +int process_check_status(Process *process, char *message, size_t message_size); +// kills process if still running +void process_kill(Process *process); + #endif // OS_H_ diff --git a/process-posix.c b/process-posix.c deleted file mode 100644 index 57af73f..0000000 --- a/process-posix.c +++ /dev/null @@ -1,198 +0,0 @@ -#include "process.h" - -#include -#include -#include - -struct Process { - pid_t pid; - int stdout_pipe; - // only applicable if separate_stderr was specified. - int stderr_pipe; - int stdin_pipe; - char error[64]; -}; - -int process_get_id(void) { - return getpid(); -} - -static void set_nonblocking(int fd) { - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); -} - -bool process_run_ex(Process *proc, const char *command, const ProcessSettings *settings) { - memset(proc, 0, sizeof *proc); - - int stdin_pipe[2] = {0}, stdout_pipe[2] = {0}, stderr_pipe[2] = {0}; - if (pipe(stdin_pipe) != 0) { - strbuf_printf(proc->error, "%s", strerror(errno)); - return false; - } - if (pipe(stdout_pipe) != 0) { - strbuf_printf(proc->error, "%s", strerror(errno)); - close(stdin_pipe[0]); - close(stdin_pipe[1]); - return false; - } - if (settings->separate_stderr) { - if (pipe(stderr_pipe) != 0) { - strbuf_printf(proc->error, "%s", strerror(errno)); - close(stdin_pipe[0]); - close(stdin_pipe[1]); - close(stdout_pipe[0]); - close(stdout_pipe[1]); - return false; - } - } - - bool success = false; - pid_t pid = fork(); - if (pid == 0) { - // child process - chdir(settings->working_directory); - // put child in its own group. it will be in this group with all of its descendents, - // so by killing everything in the group, we kill all the descendents of this process. - // if we didn't do this, we would just be killing the sh process in process_kill. - setpgid(0, 0); - // pipe stuff - dup2(stdout_pipe[1], STDOUT_FILENO); - if (stderr_pipe[1]) - dup2(stderr_pipe[1], STDERR_FILENO); - else - dup2(stdout_pipe[1], STDERR_FILENO); - dup2(stdin_pipe[0], STDIN_FILENO); - // don't need these file descriptors anymore - close(stdin_pipe[0]); - close(stdin_pipe[1]); - close(stdout_pipe[0]); - close(stdout_pipe[1]); - if (stderr_pipe[0]) { - close(stderr_pipe[0]); - close(stderr_pipe[1]); - } - - char *program = "/bin/sh"; - char *argv[] = {program, "-c", (char *)command, NULL}; - if (execv(program, argv) == -1) { - dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno)); - exit(127); - } - } else if (pid > 0) { - // parent process - - // we're reading from (the child's) stdout/stderr and writing to stdin, - // so we don't need the write end of the stdout pipe or the - // read end of the stdin pipe. - close(stdout_pipe[1]); - if (stderr_pipe[1]) - close(stderr_pipe[1]); - close(stdin_pipe[0]); - // set pipes to non-blocking - if (!settings->stdout_blocking) - set_nonblocking(stdout_pipe[0]); - if (stderr_pipe[0] && !settings->stderr_blocking) - set_nonblocking(stderr_pipe[0]); - if (!settings->stdin_blocking) - set_nonblocking(stdin_pipe[1]); - proc->pid = pid; - proc->stdout_pipe = stdout_pipe[0]; - if (stderr_pipe[0]) - proc->stderr_pipe = stderr_pipe[0]; - proc->stdin_pipe = stdin_pipe[1]; - success = true; - } - return success; -} - -bool process_run(Process *proc, char const *command) { - const ProcessSettings settings = {0}; - return process_run_ex(proc, command, &settings); -} - - -char const *process_geterr(Process *p) { - return *p->error ? p->error : NULL; -} - -long long process_write(Process *proc, const char *data, size_t size) { - assert(proc->stdin_pipe); // check that process hasn't been killed - ssize_t bytes_written = write(proc->stdin_pipe, data, size); - if (bytes_written >= 0) { - return (long long)bytes_written; - } else if (errno == EAGAIN) { - return 0; - } else { - strbuf_printf(proc->error, "%s", strerror(errno)); - return -2; - } -} - -static long long process_read_fd(Process *proc, int fd, char *data, size_t size) { - assert(fd); - ssize_t bytes_read = read(fd, data, size); - if (bytes_read >= 0) { - return (long long)bytes_read; - } else if (errno == EAGAIN) { - return -1; - } else { - strbuf_printf(proc->error, "%s", strerror(errno)); - return -2; - } -} - -long long process_read(Process *proc, char *data, size_t size) { - return process_read_fd(proc, proc->stdout_pipe, data, size); -} - -long long process_read_stderr(Process *proc, char *data, size_t size) { - return process_read_fd(proc, proc->stderr_pipe, data, size); -} - -static void process_close_pipes(Process *proc) { - close(proc->stdin_pipe); - close(proc->stdout_pipe); - close(proc->stderr_pipe); - proc->stdin_pipe = 0; - proc->stdout_pipe = 0; - proc->stderr_pipe = 0; -} - -void process_kill(Process *proc) { - kill(-proc->pid, SIGKILL); // kill everything in process group - // get rid of zombie process - waitpid(proc->pid, NULL, 0); - proc->pid = 0; - process_close_pipes(proc); -} - -int process_check_status(Process *proc, char *message, size_t message_size) { - int wait_status = 0; - int ret = waitpid(proc->pid, &wait_status, WNOHANG); - if (ret == 0) { - // process still running - return 0; - } else if (ret > 0) { - if (WIFEXITED(wait_status)) { - process_close_pipes(proc); - int code = WEXITSTATUS(wait_status); - if (code == 0) { - str_printf(message, message_size, "exited successfully"); - return +1; - } else { - str_printf(message, message_size, "exited with code %d", code); - return -1; - } - } else if (WIFSIGNALED(wait_status)) { - process_close_pipes(proc); - str_printf(message, message_size, "terminated by signal %d", WTERMSIG(wait_status)); - return -1; - } - return 0; - } else { - // this process is gone or something? - process_close_pipes(proc); - str_printf(message, message_size, "process ended unexpectedly"); - return -1; - } -} diff --git a/process-win.c b/process-win.c deleted file mode 100644 index 58b7f73..0000000 --- a/process-win.c +++ /dev/null @@ -1,137 +0,0 @@ -#error "@TODO : implement process_write, separate_stderr, working_directory" - -#include "process.h" - -struct Process { - HANDLE pipe_read, pipe_write; - HANDLE job; - PROCESS_INFORMATION process_info; - char error[200]; -}; - -static void get_last_error_str(char *out, size_t out_sz) { - size_t size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), out, (DWORD)out_sz - 1, NULL); - out[size] = 0; - char *cr = strchr(out, '\r'); - if (cr) *cr = '\0'; // get rid of carriage return+newline at end of error -} - -bool process_run(Process *process, char const *command) { - // thanks to https://stackoverflow.com/a/35658917 for the pipe code - // thanks to https://devblogs.microsoft.com/oldnewthing/20131209-00/?p=2433 for the job code - - bool success = false; - memset(process, 0, sizeof *process); - char *command_line = str_dup(command); - if (!command_line) { - strbuf_printf(process->error, "Out of memory."); - return false; - } - // we need to create a "job" for this, because when you kill a process on windows, - // all its children just keep going. so cmd.exe would die, but not the actual build process. - // jobs fix this, apparently. - HANDLE job = CreateJobObjectA(NULL, NULL); - if (job) { - JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {0}; - job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof job_info); - HANDLE pipe_read, pipe_write; - SECURITY_ATTRIBUTES security_attrs = {sizeof(SECURITY_ATTRIBUTES)}; - security_attrs.bInheritHandle = TRUE; - if (CreatePipe(&pipe_read, &pipe_write, &security_attrs, 0)) { - STARTUPINFOA startup = {sizeof(STARTUPINFOA)}; - startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - startup.hStdOutput = pipe_write; - startup.hStdError = pipe_write; - startup.wShowWindow = SW_HIDE; - PROCESS_INFORMATION *process_info = &process->process_info; - if (CreateProcessA(NULL, command_line, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_SUSPENDED, - NULL, NULL, &startup, process_info)) { - // create a suspended process, add it to the job, then resume (unsuspend) the process - if (AssignProcessToJobObject(job, process_info->hProcess)) { - if (ResumeThread(process_info->hThread) != (DWORD)-1) { - process->job = job; - process->pipe_read = pipe_read; - process->pipe_write = pipe_write; - success = true; - } - } - if (!success) { - TerminateProcess(process_info->hProcess, 1); - CloseHandle(process_info->hProcess); - CloseHandle(process_info->hThread); - } - } else { - char buf[150]; - get_last_error_str(buf, sizeof buf); - strbuf_printf(process->error, "Couldn't run `%s`: %s", command, buf); - } - free(command_line); - if (!success) { - CloseHandle(pipe_read); - CloseHandle(pipe_write); - } - } else { - char buf[150]; - get_last_error_str(buf, sizeof buf); - strbuf_printf(process->error, "Couldn't create pipe: %s", buf); - } - if (!success) - CloseHandle(job); - } - return success; -} - -char const *process_geterr(Process *p) { - return *p->error ? p->error : NULL; -} - -long long process_read(Process *process, char *data, size_t size) { - DWORD bytes_read = 0, bytes_avail = 0, bytes_left = 0; - if (PeekNamedPipe(process->pipe_read, data, (DWORD)size, &bytes_read, &bytes_avail, &bytes_left)) { - if (bytes_read == 0) { - return -1; - } else { - ReadFile(process->pipe_read, data, (DWORD)size, &bytes_read, NULL); // make sure data is removed from pipe - return bytes_read; - } - } else { - char buf[150]; - get_last_error_str(buf, sizeof buf); - strbuf_printf(process->error, "Couldn't read from pipe: %s", buf); - return -2; - } -} - -void process_kill(Process *process) { - CloseHandle(process->job); - CloseHandle(process->pipe_read); - CloseHandle(process->pipe_write); - CloseHandle(process->process_info.hProcess); - CloseHandle(process->process_info.hThread); -} - -int process_check_status(Process *process, char *message, size_t message_size) { - HANDLE hProcess = process->process_info.hProcess; - DWORD exit_code = 1; - if (GetExitCodeProcess(hProcess, &exit_code)) { - if (exit_code == STILL_ACTIVE) { - return 0; - } else { - process_kill(process); - if (exit_code == 0) { - str_printf(message, message_size, "exited successfully"); - return +1; - } else { - str_printf(message, message_size, "exited with code %d", (int)exit_code); - return -1; - } - } - } else { - // something has gone wrong. - str_printf(message, message_size, "couldn't get process exit status"); - process_kill(process); - return -1; - } -} diff --git a/process.h b/process.h deleted file mode 100644 index 163d7cf..0000000 --- a/process.h +++ /dev/null @@ -1,50 +0,0 @@ -// like popen, but allowing for non-blocking reads -#ifndef PROCESS_H_ -#define PROCESS_H_ - -typedef struct Process Process; - -// zero everything except what you're using -typedef struct { - bool stdin_blocking; - bool stdout_blocking; - bool separate_stderr; - bool stderr_blocking; // not applicable if separate_stderr is false. - const char *working_directory; -} ProcessSettings; - -// get process ID of this process -int process_get_id(void); -// execute the given command (like if it was passed to system()). -// returns false on failure -bool process_run_ex(Process *proc, const char *command, const ProcessSettings *props); -// like process_run_ex, but with the default settings -bool process_run(Process *process, char const *command); -// returns the error last error produced, or NULL if there was no error. -char const *process_geterr(Process *process); -// write to stdin -// returns: -// -2 on error -// or a non-negative number indicating the number of bytes written. -long long process_write(Process *process, const char *data, size_t size); -// read from stdout+stderr -// returns: -// -2 on error -// -1 if no data is available right now -// 0 on end of file -// or a positive number indicating the number of bytes read to data (at most size) -long long process_read(Process *process, char *data, size_t size); -// like process_read, but reads stderr. -// this function ALWAYS RETURNS -2 if separate_stderr is not specified in the ProcessSettings. -// if separate_stderr is false, then both stdout and stderr will be sent via process_read. -long long process_read_stderr(Process *process, char *data, size_t size); -// Checks if the process has exited. Returns: -// -1 if the process returned a non-zero exit code, or got a signal. -// 1 if the process exited successfully -// 0 if the process hasn't exited. -// If message is not NULL, it will be set to a description of what happened (e.g. "exited successfully") -int process_check_status(Process *process, char *message, size_t message_size); -// kills process if still running -void process_kill(Process *process); - -#endif -- cgit v1.2.3