summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/kit/_lib.c2
-rw-r--r--source/kit/process.h26
-rw-r--r--source/kit/process.posix.c308
-rw-r--r--source/kit/process.win32.c4
-rw-r--r--source/kit/status.h11
5 files changed, 348 insertions, 3 deletions
diff --git a/source/kit/_lib.c b/source/kit/_lib.c
index 3b5d97a..0f7cc19 100644
--- a/source/kit/_lib.c
+++ b/source/kit/_lib.c
@@ -10,6 +10,8 @@
#include "atomic.win32.c"
#include "threads.posix.c"
#include "threads.win32.c"
+#include "process.posix.c"
+#include "process.win32.c"
#include "shared_memory.posix.c"
#include "shared_memory.win32.c"
#include "xml.c"
diff --git a/source/kit/process.h b/source/kit/process.h
index 4a56055..f0aa200 100644
--- a/source/kit/process.h
+++ b/source/kit/process.h
@@ -7,14 +7,31 @@
#include "types.h"
#include "string_ref.h"
+#if !defined(_WIN32) || defined(__CYGWIN__)
+# include <unistd.h>
+#else
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
+enum {
+ KIT_PROCESS_NO_ARGUMENTS = 1,
+ KIT_PROCESS_NO_ENVIRONMENT = (1 << 1),
+ KIT_PROCESS_NO_PIPES = (1 << 2),
+};
+
typedef struct {
s32 status;
- i32 exit_code;
-#if defined(_WIN32) && !defined(__CYGWIN__)
+ u8 exit_code;
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ b8 _ready;
+ b8 _running;
+ pid_t _id;
+ i32 _stdin;
+ i32 _stdout;
+ i32 _stderr;
#else
#endif
} kit_process_t;
@@ -28,18 +45,21 @@ typedef KIT_AR(kit_str_t) kit_process_args_t;
typedef KIT_AR(kit_process_env_var_t) kit_process_env_t;
typedef struct {
+ u32 flags;
kit_str_t file_name;
kit_process_args_t command_line;
kit_process_env_t environment;
kit_str_t working_directory;
} kit_process_info_t;
-s32 kit_process_init(kit_process_t *p, kit_process_info_t info);
+s32 kit_process_init(kit_process_t *p, kit_process_info_t info);
+void kit_process_cleanup(kit_process_t *p);
i64 kit_process_write_stdin(kit_process_t *p, kit_str_t in_data);
i64 kit_process_read_stdout(kit_process_t *p, kit_str_t out_data);
i64 kit_process_read_stderr(kit_process_t *p, kit_str_t out_data);
s32 kit_process_terminate(kit_process_t *p);
+b8 kit_process_alive(kit_process_t *p);
s32 kit_process_wait(kit_process_t *p);
#ifdef __cplusplus
diff --git a/source/kit/process.posix.c b/source/kit/process.posix.c
index aaf2a9f..b4039e6 100644
--- a/source/kit/process.posix.c
+++ b/source/kit/process.posix.c
@@ -1 +1,309 @@
#include "process.h"
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+# include "status.h"
+
+# include <assert.h>
+# include <fcntl.h>
+# include <signal.h>
+# include <sys/wait.h>
+
+char *kit_process_argv_null_[] = { "", NULL };
+char *kit_process_env_null_[] = { NULL };
+
+char **kit_init_argv_(kit_process_args_t args, u32 flags) {
+ // TODO
+ //
+
+ if ((flags & KIT_PROCESS_NO_ARGUMENTS) != 0)
+ return kit_process_argv_null_;
+
+ return NULL;
+}
+
+char **kit_init_envp_(kit_process_env_t env, u32 flags) {
+ // TODO
+ //
+
+ if ((flags & KIT_PROCESS_NO_ENVIRONMENT) != 0)
+ return kit_process_env_null_;
+
+ return NULL;
+}
+
+s32 kit_process_init(kit_process_t *p, kit_process_info_t info) {
+ assert(p != NULL);
+ assert(info.working_directory.size == 0 ||
+ info.working_directory.values != NULL);
+
+ if (p == NULL || (info.working_directory.size != 0 &&
+ info.working_directory.values == NULL))
+ return KIT_ERROR_INVALID_ARGUMENT;
+
+ memset(p, 0, sizeof *p);
+
+ p->_stdin = -1;
+ p->_stdout = -1;
+ p->_stderr = -1;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGALRM, SIG_IGN); // pipes
+
+ i32 pipe_in[2];
+ i32 pipe_out[2];
+ i32 pipe_err[2];
+
+ if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) {
+ if (pipe(pipe_in) == -1) {
+ assert(0);
+ return KIT_ERROR_PIPE_FAILED;
+ }
+
+ if (pipe(pipe_out) == -1) {
+ assert(0);
+ close(pipe_in[0]);
+ close(pipe_in[1]);
+ return KIT_ERROR_PIPE_FAILED;
+ }
+
+ if (pipe(pipe_err) == -1) {
+ assert(0);
+ close(pipe_in[0]);
+ close(pipe_in[1]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ return KIT_ERROR_PIPE_FAILED;
+ }
+
+ // non-blocking writing for stdout, stderr
+ if (fcntl(pipe_out[1], F_SETFL, O_NONBLOCK) == -1 ||
+ fcntl(pipe_err[1], F_SETFL, O_NONBLOCK) == -1) {
+ assert(0);
+ close(pipe_in[0]);
+ close(pipe_in[1]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ close(pipe_err[0]);
+ close(pipe_err[1]);
+ return KIT_ERROR_PIPE_FAILED;
+ }
+ }
+
+ pid_t id = fork();
+
+ // NOTE
+ // We can safely use BS because there's only one thread after
+ // fork.
+ //
+
+ switch (id) {
+ case -1: return KIT_ERROR_FORK_FAILED;
+
+ case 0:
+ // Child process
+ //
+
+ if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) {
+ // Redirect IO
+ if (dup2(pipe_in[0], STDIN_FILENO) == -1 ||
+ dup2(pipe_out[1], STDOUT_FILENO) == -1 ||
+ dup2(pipe_err[1], STDERR_FILENO) == -1) {
+ assert(0);
+ close(pipe_in[0]);
+ close(pipe_in[1]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ close(pipe_err[0]);
+ close(pipe_err[1]);
+ return KIT_ERROR_DUP2_FAILED;
+ }
+
+ // Close pipes
+ close(pipe_in[0]);
+ close(pipe_in[1]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ close(pipe_err[0]);
+ close(pipe_err[1]);
+ }
+
+ // Change working directory
+ if (info.working_directory.size != 0 &&
+ chdir(BS(info.working_directory)) == -1) {
+ assert(0);
+ return KIT_ERROR_CHDIR_FAILED;
+ }
+
+ execve(BS(info.file_name),
+ kit_init_argv_(info.command_line, info.flags),
+ kit_init_envp_(info.environment, info.flags));
+ // Doesn't return on success
+
+ return KIT_ERROR_EXECVE_FAILED;
+
+ default:
+ // Parent process
+ //
+
+ p->status = KIT_OK;
+ p->_ready = 1;
+ p->_running = 1;
+ p->_id = id;
+
+ if ((info.flags & KIT_PROCESS_NO_PIPES) == 0) {
+ p->_stdin = pipe_in[1];
+ p->_stdout = pipe_out[0];
+ p->_stderr = pipe_err[0];
+
+ // Close unused pipes
+ close(pipe_in[0]);
+ close(pipe_out[1]);
+ close(pipe_err[1]);
+ }
+ }
+
+ return KIT_OK;
+}
+
+void kit_process_cleanup(kit_process_t *p) {
+ assert(p != NULL);
+ assert(p->_ready);
+
+ if (p == NULL || !p->_ready)
+ return;
+
+ if (p->_stdin != -1)
+ close(p->_stdin);
+ if (p->_stdout != -1)
+ close(p->_stdout);
+ if (p->_stderr != -1)
+ close(p->_stderr);
+
+ memset(p, 0, sizeof *p);
+}
+
+i64 kit_process_write_stdin(kit_process_t *p, kit_str_t in_data) {
+ assert(p != NULL && (in_data.size == 0 || in_data.values != NULL) &&
+ p->_running);
+
+ if (p == NULL || (in_data.size != 0 && in_data.values == NULL))
+ return KIT_ERROR_INVALID_ARGUMENT;
+ if (in_data.size == 0 || !p->_running || p->_stdin == -1)
+ return 0;
+
+ i64 n = write(p->_stdin, in_data.values, in_data.size);
+
+ assert(n >= 0);
+ if (n < 0)
+ return 0;
+
+ return n;
+}
+
+i64 kit_process_read_stdout(kit_process_t *p, kit_str_t out_data) {
+ assert(p != NULL &&
+ (out_data.size == 0 || out_data.values != NULL) &&
+ p->_ready);
+
+ if (p == NULL || (out_data.size != 0 && out_data.values == NULL))
+ return KIT_ERROR_INVALID_ARGUMENT;
+ if (out_data.size == 0 || !p->_ready || p->_stdout == -1)
+ return 0;
+
+ i64 n = read(p->_stdout, out_data.values, out_data.size);
+
+ assert(n >= 0);
+ if (n < 0)
+ return 0;
+
+ return n;
+}
+
+i64 kit_process_read_stderr(kit_process_t *p, kit_str_t out_data) {
+ assert(p != NULL &&
+ (out_data.size == 0 || out_data.values != NULL) &&
+ p->_ready);
+
+ if (p == NULL || (out_data.size != 0 && out_data.values == NULL))
+ return KIT_ERROR_INVALID_ARGUMENT;
+ if (out_data.size == 0 || !p->_ready || p->_stderr == -1)
+ return 0;
+
+ i64 n = read(p->_stderr, out_data.values, out_data.size);
+
+ assert(n >= 0);
+ if (n < 0)
+ return 0;
+
+ return n;
+}
+
+s32 kit_process_terminate(kit_process_t *p) {
+ assert(p != NULL && p->_running);
+ if (p == NULL || !p->_running)
+ return KIT_ERROR_INVALID_ARGUMENT;
+
+ if (kill(p->_id, SIGTERM) == -1)
+ return KIT_ERROR_KILL_FAILED;
+
+ return KIT_OK;
+}
+
+b8 kit_process_alive(kit_process_t *p) {
+ assert(p != NULL);
+ if (p == NULL || p->status != KIT_OK)
+ return 0;
+
+ if (!p->_running)
+ return 0;
+
+ int status;
+
+ pid_t id = waitpid(p->_id, &status, WNOHANG);
+
+ if (id == -1) {
+ p->status = KIT_ERROR_WAITPID_FAILED;
+ return 0;
+ }
+
+ if (id == 0)
+ return 1;
+
+ if (WIFEXITED(status)) {
+ p->exit_code = WEXITSTATUS(status);
+ p->_running = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+s32 kit_process_wait(kit_process_t *p) {
+ assert(p != NULL);
+ if (p == NULL)
+ return KIT_ERROR_INVALID_ARGUMENT;
+
+ if (p->status != KIT_OK)
+ return p->status;
+ if (!p->_running)
+ return KIT_OK;
+
+ for (;;) {
+ int status;
+
+ pid_t id = waitpid(p->_id, &status, 0);
+
+ if (id == -1)
+ return KIT_ERROR_WAITPID_FAILED;
+
+ if (WIFEXITED(status)) {
+ p->exit_code = WEXITSTATUS(status);
+ p->_running = 0;
+ break;
+ }
+ }
+
+ return KIT_OK;
+}
+
+#endif
diff --git a/source/kit/process.win32.c b/source/kit/process.win32.c
index aaf2a9f..ebff7b1 100644
--- a/source/kit/process.win32.c
+++ b/source/kit/process.win32.c
@@ -1 +1,5 @@
#include "process.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#endif
diff --git a/source/kit/status.h b/source/kit/status.h
index 9d1eb70..7ea1c4d 100644
--- a/source/kit/status.h
+++ b/source/kit/status.h
@@ -1,6 +1,10 @@
#ifndef KIT_STATUS_H
#define KIT_STATUS_H
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
enum {
KIT_OK = 0,
KIT_PARSING_FAILED = 1,
@@ -25,6 +29,13 @@ enum {
KIT_ERROR_SYNC_FAILED = (1 << 19),
KIT_ERROR_CLOSE_FAILED = (1 << 20),
KIT_ERROR_RESOURCE_UNAVAILABLE = (1 << 21),
+ KIT_ERROR_FORK_FAILED = (1 << 22),
+ KIT_ERROR_EXECVE_FAILED = (1 << 23),
+ KIT_ERROR_WAITPID_FAILED = (1 << 24),
+ KIT_ERROR_PIPE_FAILED = (1 << 25),
+ KIT_ERROR_DUP2_FAILED = (1 << 26),
+ KIT_ERROR_CHDIR_FAILED = (1 << 27),
+ KIT_ERROR_KILL_FAILED = (1 << 28),
KIT_ERROR_INTERNAL = (1 << 30),
KIT_ERROR_NOT_IMPLEMENTED = -1,
};