summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-01-01 23:16:38 -0500
committerpommicket <pommicket@gmail.com>2023-01-01 23:16:38 -0500
commitf173b7715cf1385db1cea67a3db07bb7d38174e9 (patch)
tree8d56748af7592ed94f11393252c1fb5419279d26
parent850ab378946e8d6f0818b4ccf8eee413f68dcb95 (diff)
finish os.h amalgamation
-rw-r--r--development.md10
-rw-r--r--main.c2
-rw-r--r--os-posix.c196
-rw-r--r--os-win.c138
-rw-r--r--os.h46
-rw-r--r--process-posix.c198
-rw-r--r--process-win.c137
-rw-r--r--process.h50
8 files changed, 390 insertions, 387 deletions
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 <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
@@ -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 <fcntl.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-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