diff --git a/fx.diagnostics/include/fx/diagnostics/process.h b/fx.diagnostics/include/fx/diagnostics/process.h index 9b43835..a6a202b 100644 --- a/fx.diagnostics/include/fx/diagnostics/process.h +++ b/fx.diagnostics/include/fx/diagnostics/process.h @@ -1,6 +1,7 @@ #ifndef FX_DIAGNOSTICS_PROCESS_H_ #define FX_DIAGNOSTICS_PROCESS_H_ +#include #include #include #include @@ -17,8 +18,7 @@ FX_TYPE_CLASS_DECLARATION_END(fx_process) typedef struct fx_process_start_info { bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr; const char *proc_file_name; - const char **proc_args; - size_t proc_args_count; + const fx_array *proc_args; const fx_hashtable *proc_environment; } fx_process_start_info; diff --git a/fx.diagnostics/sys/apple/process.c b/fx.diagnostics/sys/apple/process.c index 6610253..cab5f67 100644 --- a/fx.diagnostics/sys/apple/process.c +++ b/fx.diagnostics/sys/apple/process.c @@ -1,13 +1,22 @@ +#define _POSIX_C_SOURCE 200809L + #include #include +#include +#include #include #include #include #include #include #include +#include #include +static fx_process *self = NULL; + +extern const char **environ; + FX_API fx_type_id fx_process_iterator_get_type(); FX_DECLARE_TYPE(fx_process_iterator); @@ -21,6 +30,7 @@ struct fx_process_p { bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr; fx_stream *proc_stdin, *proc_stdout, *proc_stderr; pid_t proc_id; + fx_hashtable *proc_environment; }; struct fx_process_iterator_p { @@ -28,12 +38,109 @@ struct fx_process_iterator_p { /*** PRIVATE FUNCTIONS ********************************************************/ +static bool exec_name_is_path(const char *s) +{ + fx_wchar c; + while (*s) { + c = fx_wchar_utf8_codepoint_decode(s); + if (c == FX_WCHAR_INVALID) { + break; + } + + if (c == '/') { + return true; + } + + s += fx_wchar_utf8_codepoint_stride(s); + } + + return false; +} + +const fx_string *get_path(void) +{ + const char *path_env = NULL; + fx_process *self = fx_process_get_self(); + if (!self) { + goto fallback; + } + + const fx_hashtable *env = fx_process_get_environment(self); + if (!env) { + goto fallback; + } + + const fx_value *path_v = fx_hashtable_get(env, &FX_CSTR("PATH")); + if (!path_v) { + goto fallback; + } + + fx_string *result = NULL; + fx_value_get_object(path_v, &result); + return result; + +fallback: + path_env = getenv("PATH"); + if (!path_env) { + return NULL; + } + + return fx_string_create_from_cstr(path_env); +} + +static fx_status find_executable(fx_string *exec_path) +{ + const char *exec_path_cstr = fx_string_get_cstr(exec_path); + const fx_string *path_env = get_path(); + if (!path_env) { + return FX_ERR_NO_ENTRY; + } + + const char *delim[] = {":"}; + size_t nr_delim = sizeof delim / sizeof delim[0]; + fx_path *path = NULL; + fx_iterator *it = fx_string_tokenise( + path_env, + delim, + nr_delim, + FX_STRING_TOK_F_NORMAL); + fx_status status = FX_ERR_NO_ENTRY; + + fx_foreach(tok, it) + { + path = fx_path_join_list(2, tok, &FX_VALUE_OBJECT(exec_path)); + if (fx_path_is_file(path)) { + fx_string_clear(exec_path); + fx_string_append_cstr( + exec_path, + fx_path_get_cstr(path)); + status = FX_SUCCESS; + break; + } + } + + fx_iterator_unref(it); + fx_path_unref(path); + + return status; +} + static fx_status process_start(struct fx_process_p *proc) { fx_iostream *in_r = NULL, *in_w = NULL; fx_iostream *out_r = NULL, *out_w = NULL; fx_iostream *err_r = NULL, *err_w = NULL; fx_status status = FX_SUCCESS; + bool name_is_path + = exec_name_is_path(fx_string_get_cstr(proc->proc_exec_path)); + + if (!name_is_path) { + status = find_executable(proc->proc_exec_path); + } + + if (!FX_OK(status)) { + return status; + } if (proc->proc_redirect_stdin) { status = fx_pipe_create(&in_r, &in_w); @@ -76,7 +183,6 @@ static fx_status process_start(struct fx_process_p *proc) if (proc->proc_redirect_stdin) { fx_iostream_unref(in_w); fd = fx_iostream_steal_os_handle(in_r); - fprintf(stderr, "dup2(%d, %d)\n", fd, 0); int err = dup2(fd, 0); close(fd); } @@ -96,12 +202,23 @@ static fx_status process_start(struct fx_process_p *proc) } const char **argv = NULL; + const char **envp = NULL; + const char *default_argv[] = { + fx_string_get_cstr(proc->proc_exec_path), + NULL, + }; + size_t argc = 0; + size_t envc = 0; if (proc->proc_args) { argc = fx_array_get_size(proc->proc_args); } + if (proc->proc_environment) { + envc = fx_hashtable_get_count(proc->proc_environment); + } + if (argc) { argv = calloc(argc + 1, sizeof(char *)); @@ -110,9 +227,38 @@ static fx_status process_start(struct fx_process_p *proc) = fx_array_get_ref(proc->proc_args, i); fx_value_get_cstr(arg_value, &argv[i]); } + } else { + argv = default_argv; } - execv(fx_string_get_cstr(proc->proc_exec_path), (char *const *)argv); + if (envc) { + envp = calloc(envc + 1, sizeof(char *)); + size_t i = 0; + fx_stringstream *tmp = fx_stringstream_create(); + const fx_iterator *it + = fx_iterator_begin(proc->proc_environment); + fx_foreach(v, it) + { + fx_hashtable_item *item = NULL; + fx_value_get_object(v, &item); + const fx_value *name_v + = fx_hashtable_item_get_key(item); + const fx_value *value_v + = fx_hashtable_item_get_value(item); + + fx_value_to_string(name_v, tmp, NULL); + fx_stream_write_char(tmp, '='); + fx_value_to_string(value_v, tmp, NULL); + envp[i++] = fx_stringstream_steal(tmp); + } + + fx_iterator_unref(it); + fx_string_unref(tmp); + } + + execve(fx_string_get_cstr(proc->proc_exec_path), + (char *const *)argv, + (char *const *)envp); exit(127); cleanup: @@ -159,6 +305,12 @@ static fx_status process_kill(struct fx_process_p *proc) return FX_SUCCESS; } +static const fx_hashtable *process_get_environment( + const struct fx_process_p *proc) +{ + return proc->proc_environment; +} + static fx_stream *process_get_stdin(struct fx_process_p *proc) { return proc->proc_stdin; @@ -210,25 +362,10 @@ fx_process *fx_process_create(const fx_process_start_info *info) return NULL; } - if (info->proc_args_count > 0) { - proc->proc_args = fx_array_create(); - if (!proc->proc_args) { - fx_process_unref(out); - return NULL; - } - } - - for (size_t i = 0; i < info->proc_args_count; i++) { - fx_string *arg = fx_string_create_from_cstr(info->proc_args[i]); - if (!arg) { - fx_process_unref(out); - return NULL; - } - - fx_array_push_back(proc->proc_args, FX_VALUE_OBJECT(arg)); - fx_string_unref(arg); - } + proc->proc_args = fx_array_ref((fx_array *)info->proc_args); + proc->proc_environment + = fx_hashtable_ref((fx_hashtable *)info->proc_environment); proc->proc_redirect_stdin = info->proc_redirect_stdin; proc->proc_redirect_stdout = info->proc_redirect_stdout; proc->proc_redirect_stderr = info->proc_redirect_stderr; @@ -236,6 +373,56 @@ fx_process *fx_process_create(const fx_process_start_info *info) return out; } +fx_process *fx_process_get_self(void) +{ + if (self) { + return self; + } + + self = fx_object_create(FX_DIAGNOSTICS_TYPE_PROCESS); + if (!self) { + return NULL; + } + + struct fx_process_p *proc + = fx_object_get_private(self, FX_DIAGNOSTICS_TYPE_PROCESS); + + proc->proc_id = getpid(); + fx_hashtable *env = fx_hashtable_create(); + + for (size_t i = 0; environ[i]; i++) { + char *envstr = fx_strdup(environ[i]); + + const char *name = envstr; + char *sep = strchr(name, '='); + const char *value = NULL; + if (sep) { + *sep = '\0'; + value = sep + 1; + } + + fx_string *name_str = fx_string_create_from_cstr(name); + fx_string *value_str = fx_string_create(); + if (value) { + fx_string_append_cstr(value_str, value); + } + + fx_hashtable_put( + env, + &FX_VALUE_OBJECT(name_str), + &FX_VALUE_OBJECT(value_str)); + + fx_string_unref(name_str); + fx_string_unref(value_str); + free(envstr); + } + + proc->proc_environment = env; + + fx_register_global(self); + return self; +} + fx_status fx_process_start(fx_process *proc) { FX_CLASS_DISPATCH_STATIC_0( @@ -261,6 +448,14 @@ fx_status fx_process_kill(fx_process *proc) proc); } +const fx_hashtable *fx_process_get_environment(const fx_process *proc) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_DIAGNOSTICS_TYPE_PROCESS, + process_get_environment, + proc); +} + fx_stream *fx_process_get_stdin(fx_process *proc) { FX_CLASS_DISPATCH_STATIC_0( @@ -317,6 +512,13 @@ static void process_init(fx_object *obj, void *priv) static void process_fini(fx_object *obj, void *priv) { + struct fx_process_p *proc = priv; + fx_array_unref(proc->proc_args); + fx_iostream_unref(proc->proc_stdin); + fx_iostream_unref(proc->proc_stdout); + fx_iostream_unref(proc->proc_stderr); + fx_string_unref(proc->proc_exec_path); + fx_hashtable_unref(proc->proc_environment); } /*** ITERATOR DEFINITION ******************************************************/ diff --git a/fx.diagnostics/test/redirect.c b/fx.diagnostics/test/redirect.c index 2885e73..ffe3d72 100644 --- a/fx.diagnostics/test/redirect.c +++ b/fx.diagnostics/test/redirect.c @@ -9,17 +9,19 @@ int main(int argc, const char **argv) const char *exec = argv[1]; const char *msg = argv[2]; - const char *args[] = { - "hello", - "world", + const fx_value arg_values[] = { + FX_CSTR("hello"), + FX_CSTR("world"), }; + fx_array *args = fx_array_create_with_values( + arg_values, + sizeof arg_values / sizeof arg_values[0]); fx_process *self = fx_process_get_self(); const fx_hashtable *env = fx_process_get_environment(self); fx_process_start_info info = { .proc_args = args, - .proc_args_count = sizeof args / sizeof args[0], .proc_redirect_stdout = true, .proc_redirect_stdin = true, .proc_file_name = exec,