fx.diagnostics: process: implement environment management
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#ifndef FX_DIAGNOSTICS_PROCESS_H_
|
#ifndef FX_DIAGNOSTICS_PROCESS_H_
|
||||||
#define FX_DIAGNOSTICS_PROCESS_H_
|
#define FX_DIAGNOSTICS_PROCESS_H_
|
||||||
|
|
||||||
|
#include <fx/collections/array.h>
|
||||||
#include <fx/collections/hashtable.h>
|
#include <fx/collections/hashtable.h>
|
||||||
#include <fx/macros.h>
|
#include <fx/macros.h>
|
||||||
#include <fx/stream.h>
|
#include <fx/stream.h>
|
||||||
@@ -17,8 +18,7 @@ FX_TYPE_CLASS_DECLARATION_END(fx_process)
|
|||||||
typedef struct fx_process_start_info {
|
typedef struct fx_process_start_info {
|
||||||
bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr;
|
bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr;
|
||||||
const char *proc_file_name;
|
const char *proc_file_name;
|
||||||
const char **proc_args;
|
const fx_array *proc_args;
|
||||||
size_t proc_args_count;
|
|
||||||
const fx_hashtable *proc_environment;
|
const fx_hashtable *proc_environment;
|
||||||
} fx_process_start_info;
|
} fx_process_start_info;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include <fx/collections/array.h>
|
#include <fx/collections/array.h>
|
||||||
#include <fx/diagnostics/process.h>
|
#include <fx/diagnostics/process.h>
|
||||||
|
#include <fx/global.h>
|
||||||
|
#include <fx/io/path.h>
|
||||||
#include <fx/io/pipe.h>
|
#include <fx/io/pipe.h>
|
||||||
#include <fx/io/stream.h>
|
#include <fx/io/stream.h>
|
||||||
#include <fx/iterator.h>
|
#include <fx/iterator.h>
|
||||||
#include <fx/macros.h>
|
#include <fx/macros.h>
|
||||||
#include <fx/string.h>
|
#include <fx/string.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static fx_process *self = NULL;
|
||||||
|
|
||||||
|
extern const char **environ;
|
||||||
|
|
||||||
FX_API fx_type_id fx_process_iterator_get_type();
|
FX_API fx_type_id fx_process_iterator_get_type();
|
||||||
|
|
||||||
FX_DECLARE_TYPE(fx_process_iterator);
|
FX_DECLARE_TYPE(fx_process_iterator);
|
||||||
@@ -21,6 +30,7 @@ struct fx_process_p {
|
|||||||
bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr;
|
bool proc_redirect_stdin, proc_redirect_stdout, proc_redirect_stderr;
|
||||||
fx_stream *proc_stdin, *proc_stdout, *proc_stderr;
|
fx_stream *proc_stdin, *proc_stdout, *proc_stderr;
|
||||||
pid_t proc_id;
|
pid_t proc_id;
|
||||||
|
fx_hashtable *proc_environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fx_process_iterator_p {
|
struct fx_process_iterator_p {
|
||||||
@@ -28,12 +38,109 @@ struct fx_process_iterator_p {
|
|||||||
|
|
||||||
/*** PRIVATE FUNCTIONS ********************************************************/
|
/*** 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)
|
static fx_status process_start(struct fx_process_p *proc)
|
||||||
{
|
{
|
||||||
fx_iostream *in_r = NULL, *in_w = NULL;
|
fx_iostream *in_r = NULL, *in_w = NULL;
|
||||||
fx_iostream *out_r = NULL, *out_w = NULL;
|
fx_iostream *out_r = NULL, *out_w = NULL;
|
||||||
fx_iostream *err_r = NULL, *err_w = NULL;
|
fx_iostream *err_r = NULL, *err_w = NULL;
|
||||||
fx_status status = FX_SUCCESS;
|
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) {
|
if (proc->proc_redirect_stdin) {
|
||||||
status = fx_pipe_create(&in_r, &in_w);
|
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) {
|
if (proc->proc_redirect_stdin) {
|
||||||
fx_iostream_unref(in_w);
|
fx_iostream_unref(in_w);
|
||||||
fd = fx_iostream_steal_os_handle(in_r);
|
fd = fx_iostream_steal_os_handle(in_r);
|
||||||
fprintf(stderr, "dup2(%d, %d)\n", fd, 0);
|
|
||||||
int err = dup2(fd, 0);
|
int err = dup2(fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
@@ -96,12 +202,23 @@ static fx_status process_start(struct fx_process_p *proc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char **argv = NULL;
|
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 argc = 0;
|
||||||
|
size_t envc = 0;
|
||||||
|
|
||||||
if (proc->proc_args) {
|
if (proc->proc_args) {
|
||||||
argc = fx_array_get_size(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) {
|
if (argc) {
|
||||||
argv = calloc(argc + 1, sizeof(char *));
|
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_array_get_ref(proc->proc_args, i);
|
||||||
fx_value_get_cstr(arg_value, &argv[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);
|
exit(127);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@@ -159,6 +305,12 @@ static fx_status process_kill(struct fx_process_p *proc)
|
|||||||
return FX_SUCCESS;
|
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)
|
static fx_stream *process_get_stdin(struct fx_process_p *proc)
|
||||||
{
|
{
|
||||||
return proc->proc_stdin;
|
return proc->proc_stdin;
|
||||||
@@ -210,25 +362,10 @@ fx_process *fx_process_create(const fx_process_start_info *info)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->proc_args_count > 0) {
|
proc->proc_args = fx_array_ref((fx_array *)info->proc_args);
|
||||||
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_environment
|
||||||
|
= fx_hashtable_ref((fx_hashtable *)info->proc_environment);
|
||||||
proc->proc_redirect_stdin = info->proc_redirect_stdin;
|
proc->proc_redirect_stdin = info->proc_redirect_stdin;
|
||||||
proc->proc_redirect_stdout = info->proc_redirect_stdout;
|
proc->proc_redirect_stdout = info->proc_redirect_stdout;
|
||||||
proc->proc_redirect_stderr = info->proc_redirect_stderr;
|
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;
|
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_status fx_process_start(fx_process *proc)
|
||||||
{
|
{
|
||||||
FX_CLASS_DISPATCH_STATIC_0(
|
FX_CLASS_DISPATCH_STATIC_0(
|
||||||
@@ -261,6 +448,14 @@ fx_status fx_process_kill(fx_process *proc)
|
|||||||
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_stream *fx_process_get_stdin(fx_process *proc)
|
||||||
{
|
{
|
||||||
FX_CLASS_DISPATCH_STATIC_0(
|
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)
|
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 ******************************************************/
|
/*** ITERATOR DEFINITION ******************************************************/
|
||||||
|
|||||||
@@ -9,17 +9,19 @@ int main(int argc, const char **argv)
|
|||||||
const char *exec = argv[1];
|
const char *exec = argv[1];
|
||||||
const char *msg = argv[2];
|
const char *msg = argv[2];
|
||||||
|
|
||||||
const char *args[] = {
|
const fx_value arg_values[] = {
|
||||||
"hello",
|
FX_CSTR("hello"),
|
||||||
"world",
|
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();
|
fx_process *self = fx_process_get_self();
|
||||||
const fx_hashtable *env = fx_process_get_environment(self);
|
const fx_hashtable *env = fx_process_get_environment(self);
|
||||||
|
|
||||||
fx_process_start_info info = {
|
fx_process_start_info info = {
|
||||||
.proc_args = args,
|
.proc_args = args,
|
||||||
.proc_args_count = sizeof args / sizeof args[0],
|
|
||||||
.proc_redirect_stdout = true,
|
.proc_redirect_stdout = true,
|
||||||
.proc_redirect_stdin = true,
|
.proc_redirect_stdin = true,
|
||||||
.proc_file_name = exec,
|
.proc_file_name = exec,
|
||||||
|
|||||||
Reference in New Issue
Block a user