diff options
Diffstat (limited to 'src/libnetdata/spawn_server/spawn_popen.c')
-rw-r--r-- | src/libnetdata/spawn_server/spawn_popen.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/libnetdata/spawn_server/spawn_popen.c b/src/libnetdata/spawn_server/spawn_popen.c new file mode 100644 index 000000000..f354b1f2a --- /dev/null +++ b/src/libnetdata/spawn_server/spawn_popen.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "spawn_popen.h" + +SPAWN_SERVER *netdata_main_spawn_server = NULL; +static SPINLOCK netdata_main_spawn_server_spinlock = NETDATA_SPINLOCK_INITIALIZER; + +bool netdata_main_spawn_server_init(const char *name, int argc, const char **argv) { + if(netdata_main_spawn_server == NULL) { + spinlock_lock(&netdata_main_spawn_server_spinlock); + if(netdata_main_spawn_server == NULL) + netdata_main_spawn_server = spawn_server_create(SPAWN_SERVER_OPTION_EXEC, name, NULL, argc, argv); + spinlock_unlock(&netdata_main_spawn_server_spinlock); + } + + return netdata_main_spawn_server != NULL; +} + +void netdata_main_spawn_server_cleanup(void) { + if(netdata_main_spawn_server) { + spinlock_lock(&netdata_main_spawn_server_spinlock); + if(netdata_main_spawn_server) { + spawn_server_destroy(netdata_main_spawn_server); + netdata_main_spawn_server = NULL; + } + spinlock_unlock(&netdata_main_spawn_server_spinlock); + } +} + +POPEN_INSTANCE *spawn_popen_run_argv(const char **argv) { + netdata_main_spawn_server_init(NULL, 0, NULL); + + SPAWN_INSTANCE *si = spawn_server_exec(netdata_main_spawn_server, nd_log_collectors_fd(), + 0, argv, NULL, 0, SPAWN_INSTANCE_TYPE_EXEC); + + if(si == NULL) return NULL; + + POPEN_INSTANCE *pi = mallocz(sizeof(*pi)); + pi->si = si; + pi->child_stdin_fp = fdopen(spawn_server_instance_write_fd(si), "w"); + pi->child_stdout_fp = fdopen(spawn_server_instance_read_fd(si), "r"); + + if(!pi->child_stdin_fp) { + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot open FILE on child's stdin on fd %d.", spawn_server_instance_write_fd(si)); + goto cleanup; + } + + if(!pi->child_stdout_fp) { + nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot open FILE on child's stdout on fd %d.", spawn_server_instance_read_fd(si)); + goto cleanup; + } + + return pi; + +cleanup: + if(pi->child_stdin_fp) { fclose(pi->child_stdin_fp); spawn_server_instance_write_fd(si); } + if(pi->child_stdout_fp) { fclose(pi->child_stdout_fp); spawn_server_instance_read_fd_unset(si); } + spawn_server_exec_kill(netdata_main_spawn_server, si); + freez(pi); + return NULL; +} + +POPEN_INSTANCE *spawn_popen_run_variadic(const char *cmd, ...) { + va_list args; + va_list args_copy; + int argc = 0; + + // Start processing variadic arguments + va_start(args, cmd); + + // Make a copy of args to count the number of arguments + va_copy(args_copy, args); + while (va_arg(args_copy, char *) != NULL) argc++; + va_end(args_copy); + + // Allocate memory for argv array (+2 for cmd and NULL terminator) + const char *argv[argc + 2]; + + // Populate the argv array + argv[0] = cmd; + + for (int i = 1; i <= argc; i++) + argv[i] = va_arg(args, const char *); + + argv[argc + 1] = NULL; // NULL-terminate the array + + // End processing variadic arguments + va_end(args); + + return spawn_popen_run_argv(argv); +} + +POPEN_INSTANCE *spawn_popen_run(const char *cmd) { + if(!cmd || !*cmd) return NULL; + + const char *argv[] = { + "/bin/sh", + "-c", + cmd, + NULL + }; + return spawn_popen_run_argv(argv); +} + +static int spawn_popen_status_rc(int status) { + if(WIFEXITED(status)) + return WEXITSTATUS(status); + + if(WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + switch(sig) { + case SIGTERM: + case SIGPIPE: + return 0; + + default: + return -1; + } + } + + return -1; +} + +int spawn_popen_wait(POPEN_INSTANCE *pi) { + if(!pi) return -1; + + fclose(pi->child_stdin_fp); pi->child_stdin_fp = NULL; spawn_server_instance_write_fd_unset(pi->si); + fclose(pi->child_stdout_fp); pi->child_stdout_fp = NULL; spawn_server_instance_read_fd_unset(pi->si); + int status = spawn_server_exec_wait(netdata_main_spawn_server, pi->si); + freez(pi); + return spawn_popen_status_rc(status); +} + +int spawn_popen_kill(POPEN_INSTANCE *pi) { + if(!pi) return -1; + + fclose(pi->child_stdin_fp); pi->child_stdin_fp = NULL; spawn_server_instance_write_fd_unset(pi->si); + fclose(pi->child_stdout_fp); pi->child_stdout_fp = NULL; spawn_server_instance_read_fd_unset(pi->si); + int status = spawn_server_exec_kill(netdata_main_spawn_server, pi->si); + freez(pi); + return spawn_popen_status_rc(status); +} |