From 9ffa19269054bf1e569fe584c0c51a00205a735d Mon Sep 17 00:00:00 2001 From: t3sserakt Date: Sun, 25 Apr 2021 16:01:49 +0200 Subject: - added testing cmd child management --- src/include/gnunet_child_management_lib.h | 72 ++++++++++ src/util/child_management.c | 219 ++++++++++++++++++++++++++++++ 2 files changed, 291 insertions(+) create mode 100644 src/include/gnunet_child_management_lib.h create mode 100644 src/util/child_management.c diff --git a/src/include/gnunet_child_management_lib.h b/src/include/gnunet_child_management_lib.h new file mode 100644 index 000000000..465f71f0e --- /dev/null +++ b/src/include/gnunet_child_management_lib.h @@ -0,0 +1,72 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file include/anastasis_util_lib.h + * @brief GNUnet child management api + * @author Christian Grothoff + * @author Dominik Meister + * @author Dennis Neufeld + * @author t3sserakt + */ +#ifndef GNUNET_CHILD_MANAGEMENT_LIB_H +#define GNUNET_CHILD_MANAGEMENT_LIB_H + +/** + * Handle for the child management + */ +struct GNUNET_ChildWaitHandle; + +/** + * Defines a GNUNET_ChildCompletedCallback which is sent back + * upon death or completion of a child process. + * + * @param cls handle for the callback + * @param type type of the process + * @param exit_code status code of the process + * +*/ +typedef void +(*GNUNET_ChildCompletedCallback)(void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code); + +/** + * Starts the handling of the child processes. + * Function checks the status of the child process and sends back a + * GNUNET_ChildCompletedCallback upon completion/death of the child. + * + * @param proc child process which is monitored + * @param cb reference to the callback which is called after completion + * @param cb_cls closure for the callback + * @return GNUNET_ChildWaitHandle is returned + */ +struct GNUNET_ChildWaitHandle * +GNUNET_wait_child (struct GNUNET_OS_Process *proc, + GNUNET_ChildCompletedCallback cb, + void *cb_cls); + +/** + * Stop waiting on this child. + */ +void +GNUNET_wait_child_cancel (struct GNUNET_ChildWaitHandle *cwh); + +#endif diff --git a/src/util/child_management.c b/src/util/child_management.c new file mode 100644 index 000000000..7edc33dc1 --- /dev/null +++ b/src/util/child_management.c @@ -0,0 +1,219 @@ +/* + This file is part of GNUnet + Copyright (C) 2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file testing/child_management.c + * @brief Handling of child processes in GNUnet. + * @author Christian Grothoff (ANASTASIS) + * @author Dominik Meister (ANASTASIS) + * @author t3sserakt + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_child_management_lib.h" + + +/** + * Struct which defines a Child Wait handle + */ +struct GNUNET_ChildWaitHandle +{ + /** + * Linked list to the next child + */ + struct GNUNET_ChildWaitHandle *next; + /** + * Linked list to the previous child + */ + struct GNUNET_ChildWaitHandle *prev; + /** + * Child process which is managed + */ + struct GNUNET_OS_Process *proc; + /** + * Callback which is called upon completion/death of the child task + */ + GNUNET_ChildCompletedCallback cb; + /** + * Closure for the handle + */ + void *cb_cls; +}; + + +/** + * Pipe used to communicate shutdown via signal. + */ +static struct GNUNET_DISK_PipeHandle *sigpipe; + +static struct GNUNET_SIGNAL_Context *shc_chld; + +static struct GNUNET_SCHEDULER_Task *sig_task; + +static struct GNUNET_ChildWaitHandle *cwh_head; + +static struct GNUNET_ChildWaitHandle *cwh_tail; + +/** + * Task triggered whenever we receive a SIGCHLD (child + * process died) or when user presses CTRL-C. + * + * @param cls closure, NULL + */ +static void +maint_child_death (void *cls) +{ + char buf[16]; + const struct GNUNET_DISK_FileHandle *pr; + struct GNUNET_ChildWaitHandle *nxt; + + (void) cls; + sig_task = NULL; + /* drain pipe */ + pr = GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_READ); + GNUNET_assert (! GNUNET_DISK_handle_invalid (pr)); + + (void) GNUNET_DISK_file_read (pr, + buf, + sizeof(buf)); + + /* find applicable processes that exited */ + for (struct GNUNET_ChildWaitHandle *cwh = cwh_head; + NULL != cwh; + cwh = nxt) + { + enum GNUNET_OS_ProcessStatusType type; + long unsigned int exit_code = 0; + + nxt = cwh->next; + if (GNUNET_OK == + GNUNET_OS_process_status (cwh->proc, + &type, + &exit_code)) + { + GNUNET_CONTAINER_DLL_remove (cwh_head, + cwh_tail, + cwh); + cwh->cb (cwh->cb_cls, + type, + exit_code); + GNUNET_free (cwh); + } + } + if (NULL == cwh_head) + return; + /* wait for more */ + sig_task = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_READ), + &maint_child_death, + NULL); +} + + +/** + * Signal handler called for SIGCHLD. Triggers the + * respective handler by writing to the trigger pipe. + */ +static void +sighandler_child_death (void) +{ + static char c; + int old_errno = errno; /* back-up errno */ + + GNUNET_break ( + 1 == + GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_WRITE), + &c, + sizeof(c))); + errno = old_errno; /* restore errno */ +} + + +void __attribute__ ((constructor)) +child_management_start () +{ + if (NULL != sigpipe) + return; /* already initialized */ + sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE); + GNUNET_assert (sigpipe != NULL); + shc_chld = + GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); +} + + +struct GNUNET_ChildWaitHandle * +GNUNET_wait_child (struct GNUNET_OS_Process *proc, + GNUNET_ChildCompletedCallback cb, + void *cb_cls) +{ + struct GNUNET_ChildWaitHandle *cwh; + + child_management_start (); + cwh = GNUNET_new (struct GNUNET_ChildWaitHandle); + cwh->proc = proc; + cwh->cb = cb; + cwh->cb_cls = cb_cls; + GNUNET_CONTAINER_DLL_insert (cwh_head, + cwh_tail, + cwh); + if (NULL == sig_task) + { + sig_task = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_DISK_pipe_handle (sigpipe, + GNUNET_DISK_PIPE_END_READ), + &maint_child_death, + NULL); + } + return cwh; +} + + +void +GNUNET_wait_child_cancel (struct GNUNET_ChildWaitHandle *cwh) +{ + GNUNET_CONTAINER_DLL_remove (cwh_head, + cwh_tail, + cwh); + if (NULL == cwh_head) + { + GNUNET_SCHEDULER_cancel (sig_task); + sig_task = NULL; + } + GNUNET_free (cwh); +} + + +/** + * Clean up. + */ +void __attribute__ ((destructor)) +GNUNET_CM_done () +{ + GNUNET_assert (NULL == sig_task); + GNUNET_SIGNAL_handler_uninstall (shc_chld); + shc_chld = NULL; + GNUNET_DISK_pipe_close (sigpipe); + sigpipe = NULL; +} -- cgit v1.2.3