summaryrefslogtreecommitdiff
path: root/process-posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'process-posix.c')
-rw-r--r--process-posix.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/process-posix.c b/process-posix.c
new file mode 100644
index 0000000..53363b6
--- /dev/null
+++ b/process-posix.c
@@ -0,0 +1,119 @@
+#include "process.h"
+
+#include <fcntl.h>
+#include <sys/wait.h>
+
+struct Process {
+ pid_t pid;
+ int pipe;
+ bool stopped;
+ int exit_status; // INT_MAX if process was terminated
+};
+
+static char process_error[64];
+static Process *processes;
+
+static void process_sigaction_sigchld(int signal, siginfo_t *info, void *context) {
+ (void)context;
+ if (signal == SIGCHLD) {
+ pid_t pid = info->si_pid;
+ arr_foreach_ptr(processes, Process, process) {
+ if (process->pid == pid) {
+ process->stopped = true;
+ if (info->si_code == CLD_EXITED)
+ process->exit_status = info->si_status;
+ else
+ process->exit_status = INT_MAX;
+ }
+ }
+ }
+}
+
+void process_init(void) {
+ struct sigaction act = {0};
+ act.sa_sigaction = process_sigaction_sigchld;
+ act.sa_flags = SA_SIGINFO | SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, NULL);
+}
+
+
+
+Process *process_exec(char const *program, char **argv) {
+ Process *proc = NULL;
+
+ int pipefd[2];
+ if (pipe(pipefd) == 0) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child process
+ // send stdout and stderr to pipe
+ dup2(pipefd[1], STDOUT_FILENO);
+ dup2(pipefd[1], STDERR_FILENO);
+ close(pipefd[0]);
+ close(pipefd[1]);
+ if (execv(program, argv) == -1) {
+ dprintf(STDERR_FILENO, "%s: %s\n", program, strerror(errno));
+ exit(127);
+ }
+ } else if (pid > 0) {
+ proc = arr_addp(processes);
+ // parent process
+ close(pipefd[1]);
+ fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); // set pipe to non-blocking
+ proc->pid = pid;
+ proc->pipe = pipefd[0];
+ }
+ } else {
+ strbuf_printf(process_error, "%s", strerror(errno));
+ }
+ return proc;
+}
+
+char const *process_geterr(void) {
+ return *process_error ? process_error : NULL;
+}
+
+long long process_read(Process *proc, char *data, size_t size) {
+ assert(proc->pipe);
+ ssize_t bytes_read = read(proc->pipe, data, size);
+ if (bytes_read >= 0) {
+ return (long long)bytes_read;
+ } else if (errno == EAGAIN) {
+ return -1;
+ } else {
+ strbuf_printf(process_error, "%s", strerror(errno));
+ return -2;
+ }
+
+}
+
+void process_stop(Process *proc) {
+ kill(proc->pid, SIGKILL);
+ // get rid of zombie process
+ waitpid(proc->pid, NULL, 0);
+ close(proc->pipe);
+ for (u32 i = 0; i < arr_len(processes); ++i)
+ if (processes[i].pid == proc->pid)
+ arr_remove(processes, i);
+}
+#include <sys/resource.h>
+int process_check_status(Process *proc, char *message, size_t message_size) {
+ if (proc->stopped) {
+ int status = proc->exit_status;
+ process_stop(proc);
+
+ switch (status) {
+ case 0:
+ if (message) str_printf(message, message_size, "exited successfully");
+ return +1;
+ case INT_MAX:
+ if (message) str_printf(message, message_size, "terminated by a signal");
+ return -1;
+ default:
+ if (message) str_printf(message, message_size, "exited with code %d", status);
+ return -1;
+ }
+ } else {
+ return 0;
+ }
+}