Files
fx/fx.diagnostics/sys/linux/process.c
T

494 lines
11 KiB
C

#define _POSIX_C_SOURCE 200809L
#include <fx/collections/array.h>
#include <fx/diagnostics/process.h>
#include <fx/global.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);
FX_TYPE_CLASS_DECLARATION_BEGIN(fx_process_iterator)
FX_TYPE_CLASS_DECLARATION_END(fx_process_iterator)
struct fx_process_p {
fx_string *proc_exec_path;
fx_array *proc_args;
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 {
};
/*** PRIVATE FUNCTIONS ********************************************************/
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;
if (proc->proc_redirect_stdin) {
status = fx_pipe_create(&in_r, &in_w);
if (!FX_OK(status)) {
goto cleanup;
}
}
if (proc->proc_redirect_stdout) {
status = fx_pipe_create(&out_r, &out_w);
if (!FX_OK(status)) {
goto cleanup;
}
}
if (proc->proc_redirect_stderr) {
status = fx_pipe_create(&err_r, &err_w);
if (!FX_OK(status)) {
goto cleanup;
}
}
pid_t pid = fork();
if (pid < 0) {
status = FX_ERR_INVALID_ARGUMENT;
goto cleanup;
}
if (pid > 0) {
proc->proc_id = pid;
proc->proc_stdin = fx_iostream_ref(in_w);
proc->proc_stdout = fx_iostream_ref(out_r);
proc->proc_stderr = fx_iostream_ref(err_r);
status = FX_SUCCESS;
goto cleanup;
}
int fd = -1;
if (proc->proc_redirect_stdin) {
fx_iostream_unref(in_w);
fd = fx_iostream_steal_os_handle(in_r);
int err = dup2(fd, 0);
close(fd);
}
if (proc->proc_redirect_stdout) {
fx_iostream_unref(out_r);
fd = fx_iostream_steal_os_handle(out_w);
dup2(fd, 1);
close(fd);
}
if (proc->proc_redirect_stderr) {
fx_iostream_unref(err_r);
fd = fx_iostream_steal_os_handle(err_w);
dup2(fd, 2);
close(fd);
}
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 *));
for (size_t i = 0; i < argc; i++) {
const fx_value *arg_value
= fx_array_get_ref(proc->proc_args, i);
fx_value_get_cstr(arg_value, &argv[i]);
}
} else {
argv = default_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);
}
fprintf(stderr, "argc=%zu, argv=%p\n", argc, argv);
fprintf(stderr, "envc=%zu, envp=%p\n", envc, envp);
execve(fx_string_get_cstr(proc->proc_exec_path),
(char *const *)argv,
(char *const *)envp);
exit(127);
cleanup:
fx_iostream_unref(in_r);
fx_iostream_unref(in_w);
fx_iostream_unref(out_r);
fx_iostream_unref(out_w);
fx_iostream_unref(err_r);
fx_iostream_unref(err_w);
return status;
}
static fx_status process_wait(struct fx_process_p *proc, int *out_result)
{
if (proc->proc_id == 0) {
return FX_ERR_BAD_STATE;
}
int status = 0;
do {
int err = waitpid(proc->proc_id, &status, WUNTRACED);
if (err != 0) {
return FX_ERR_BAD_STATE;
}
} while (!WIFEXITED(status));
proc->proc_id = 0;
*out_result = WEXITSTATUS(status);
return FX_ERR_NOT_SUPPORTED;
}
static fx_status process_kill(struct fx_process_p *proc)
{
if (proc->proc_id == 0) {
return FX_ERR_BAD_STATE;
}
int err = kill(proc->proc_id, SIGTERM);
if (err != 0) {
return FX_ERR_BAD_STATE;
}
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;
}
static fx_stream *process_get_stdout(struct fx_process_p *proc)
{
return proc->proc_stdout;
}
static fx_stream *process_get_stderr(struct fx_process_p *proc)
{
return proc->proc_stderr;
}
static void process_close_stdin(struct fx_process_p *proc)
{
fx_iostream_unref(proc->proc_stdin);
proc->proc_stdin = NULL;
}
static void process_close_stdout(struct fx_process_p *proc)
{
fx_iostream_unref(proc->proc_stdout);
proc->proc_stdout = NULL;
}
static void process_close_stderr(struct fx_process_p *proc)
{
fx_iostream_unref(proc->proc_stderr);
proc->proc_stderr = NULL;
}
/*** PUBLIC FUNCTIONS *********************************************************/
fx_process *fx_process_create(const fx_process_start_info *info)
{
fx_process *out = fx_object_create(FX_DIAGNOSTICS_TYPE_PROCESS);
if (!out) {
return NULL;
}
struct fx_process_p *proc
= fx_object_get_private(out, FX_DIAGNOSTICS_TYPE_PROCESS);
proc->proc_exec_path = fx_string_create_from_cstr(info->proc_file_name);
if (!proc->proc_exec_path) {
fx_process_unref(out);
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_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;
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(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_start,
proc);
}
fx_status fx_process_wait(fx_process *proc, int *out_result)
{
FX_CLASS_DISPATCH_STATIC(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_wait,
proc,
out_result);
}
fx_status fx_process_kill(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_kill,
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(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_get_stdin,
proc);
}
fx_stream *fx_process_get_stdout(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_get_stdout,
proc);
}
fx_stream *fx_process_get_stderr(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_get_stderr,
proc);
}
void fx_process_close_stdin(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_V0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_close_stdin,
proc);
}
void fx_process_close_stdout(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_V0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_close_stdout,
proc);
}
void fx_process_close_stderr(fx_process *proc)
{
FX_CLASS_DISPATCH_STATIC_V0(
FX_DIAGNOSTICS_TYPE_PROCESS,
process_close_stderr,
proc);
}
/*** VIRTUAL FUNCTIONS ********************************************************/
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
* ******************************************************/
static enum fx_status process_iterator_move_next(const fx_iterator *obj)
{
return FX_ERR_NO_DATA;
}
static const fx_value *process_iterator_get_value(const fx_iterator *obj)
{
return NULL;
}
/*** CLASS DEFINITION
* *********************************************************/
FX_TYPE_CLASS_BEGIN(fx_process)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_CLASS_END(fx_process)
FX_TYPE_DEFINITION_BEGIN(fx_process)
FX_TYPE_ID(0x7334594f, 0xa1c3, 0x4715, 0xab69, 0x68a709e1b64b);
FX_TYPE_NAME("fx.diagnostics.process");
FX_TYPE_CLASS(fx_process_class);
FX_TYPE_INSTANCE_PRIVATE(struct fx_process_p);
FX_TYPE_INSTANCE_INIT(process_init);
FX_TYPE_INSTANCE_FINI(process_fini);
FX_TYPE_DEFINITION_END(fx_process)
FX_TYPE_CLASS_BEGIN(fx_process_iterator)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
FX_INTERFACE_ENTRY(to_string) = NULL;
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterator, FX_TYPE_ITERATOR)
FX_INTERFACE_ENTRY(it_move_next) = process_iterator_move_next;
FX_INTERFACE_ENTRY(it_get_value) = process_iterator_get_value;
FX_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR)
FX_TYPE_CLASS_END(fx_process_iterator)
FX_TYPE_DEFINITION_BEGIN(fx_process_iterator)
FX_TYPE_ID(0x67eb13b6, 0x25d1, 0x424a, 0xb136, 0x871a07829089);
FX_TYPE_NAME("fx.diagnostics.process.iterator");
FX_TYPE_EXTENDS(FX_TYPE_ITERATOR);
FX_TYPE_CLASS(fx_process_iterator_class);
FX_TYPE_INSTANCE_PRIVATE(struct fx_process_iterator_p);
FX_TYPE_DEFINITION_END(fx_process_iterator)