#include #include #include #include #include #include #include #include #include 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; }; 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); fprintf(stderr, "dup2(%d, %d)\n", fd, 0); 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; size_t argc = 0; if (proc->proc_args) { argc = fx_array_get_size(proc->proc_args); } 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]); } } execv(fx_string_get_cstr(proc->proc_exec_path), (char *const *)argv); 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 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_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_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); } 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) { } /*** 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)