fx.diagnostics: process: implement environment management
This commit is contained in:
@@ -1,13 +1,22 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <fx/collections/array.h>
|
||||
#include <fx/diagnostics/process.h>
|
||||
#include <fx/global.h>
|
||||
#include <fx/io/path.h>
|
||||
#include <fx/io/pipe.h>
|
||||
#include <fx/io/stream.h>
|
||||
#include <fx/iterator.h>
|
||||
#include <fx/macros.h>
|
||||
#include <fx/string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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 ******************************************************/
|
||||
|
||||
Reference in New Issue
Block a user