diff --git a/assemblies/fx.diagnostics/CMakeLists.txt b/assemblies/fx.diagnostics/CMakeLists.txt new file mode 100644 index 0000000..1d09468 --- /dev/null +++ b/assemblies/fx.diagnostics/CMakeLists.txt @@ -0,0 +1,4 @@ +add_fx_assembly( + NAME fx.diagnostics + NAMESPACES fx.diagnostics + DEPENDENCIES fx.runtime fx.collections fx.io) diff --git a/assemblies/fx.diagnostics/assembly.c b/assemblies/fx.diagnostics/assembly.c new file mode 100644 index 0000000..a3cc2b8 --- /dev/null +++ b/assemblies/fx.diagnostics/assembly.c @@ -0,0 +1,7 @@ +#include +#include + +FX_ASSEMBLY_BEGIN(fx_diagnostics) + FX_ASSEMBLY_NAME("fx.diagnostics"); + FX_ASSEMBLY_VERSION(1, 0, 0, 0); +FX_ASSEMBLY_END(fx_diagnostics) diff --git a/fx.diagnostics/CMakeLists.txt b/fx.diagnostics/CMakeLists.txt new file mode 100644 index 0000000..4dcf129 --- /dev/null +++ b/fx.diagnostics/CMakeLists.txt @@ -0,0 +1 @@ +export_fx_namespace_details(fx.diagnostics) diff --git a/fx.diagnostics/include/fx/diagnostics/process.h b/fx.diagnostics/include/fx/diagnostics/process.h new file mode 100644 index 0000000..8ddf4c4 --- /dev/null +++ b/fx.diagnostics/include/fx/diagnostics/process.h @@ -0,0 +1,41 @@ +#ifndef FX_DIAGNOSTICS_PROCESS_H_ +#define FX_DIAGNOSTICS_PROCESS_H_ + +#include +#include + +FX_DECLS_BEGIN; + +#define FX_DIAGNOSTICS_TYPE_PROCESS (fx_process_get_type()) + +FX_DECLARE_TYPE(fx_process); + +FX_TYPE_CLASS_DECLARATION_BEGIN(fx_process) +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; +} fx_process_start_info; + +FX_API fx_type_id fx_process_get_type(void); + +FX_API fx_process *fx_process_create(const fx_process_start_info *info); + +FX_API fx_status fx_process_start(fx_process *proc); +FX_API fx_status fx_process_wait(fx_process *proc, int *out_result); +FX_API fx_status fx_process_kill(fx_process *proc); + +FX_API fx_stream *fx_process_get_stdin(fx_process *proc); +FX_API fx_stream *fx_process_get_stdout(fx_process *proc); +FX_API fx_stream *fx_process_get_stderr(fx_process *proc); + +FX_API void fx_process_close_stdin(fx_process *proc); +FX_API void fx_process_close_stdout(fx_process *proc); +FX_API void fx_process_close_stderr(fx_process *proc); + +FX_DECLS_END; + +#endif diff --git a/fx.diagnostics/sys/apple/process.c b/fx.diagnostics/sys/apple/process.c new file mode 100644 index 0000000..3a99d2e --- /dev/null +++ b/fx.diagnostics/sys/apple/process.c @@ -0,0 +1,358 @@ +#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); + fprintf(stderr, "dup2(%d, %d)\n", fd, 1); + dup2(fd, 1); + close(fd); + } + + if (proc->proc_redirect_stderr) { + fx_iostream_unref(err_r); + fd = fx_iostream_steal_os_handle(err_w); + fprintf(stderr, "dup2(%d, %d)\n", fd, 2); + dup2(fd, 2); + close(fd); + } + + const char *args[] = { + "cowsay", + NULL, + }; + printf("exec '%s'\n", fx_string_get_cstr(proc->proc_exec_path)); + execv(fx_string_get_cstr(proc->proc_exec_path), (char *const *)args); + 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) diff --git a/fx.diagnostics/test/redirect.c b/fx.diagnostics/test/redirect.c new file mode 100644 index 0000000..5681f00 --- /dev/null +++ b/fx.diagnostics/test/redirect.c @@ -0,0 +1,44 @@ +#include + +int main(int argc, const char **argv) +{ + if (argc < 3) { + return -1; + } + + const char *exec = argv[1]; + const char *msg = argv[2]; + + fx_process_start_info info = { + .proc_args = NULL, + .proc_args_count = 0, + .proc_redirect_stdin = true, + .proc_file_name = exec, + }; + + fx_process *process = fx_process_create(&info); + if (!process) { + fprintf(stderr, "Process creation failed\n"); + return -1; + } + + fx_status status = fx_process_start(process); + if (!FX_OK(status)) { + fprintf(stderr, + "Process creation failed: %s\n", + fx_status_description(status)); + return -1; + } + +#if 1 + fx_stream *in = fx_process_get_stdin(process); + fx_stream_write_cstr(in, "Hello, world!\n", NULL); + fx_process_close_stdin(process); +#endif + + int result; + fx_process_wait(process, &result); + fx_process_unref(process); + + return 0; +}