/* SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "gcc-compat.h" #include "err.h" #include "log.h" #include "str.h" #include "file.h" #include "sig.h" static int signal_pipe[2]; /* * Initialize the signal subsystem. * * This function creates a pipe, the signal pipe, to deliver pending signals to * the application (Bernstein's trick). It is called at startup, followed by * subsequent calls to install_sighandler() for each signal that should be * caught. * * signal_init() installs a generic signal handler which is used for all * signals simultaneously. When a signal arrives, this generic signal handler * writes the corresponding signal number to the signal pipe so that the * application can test for pending signals by checking the signal pipe for * reading, e.g. by using the select(2) system call. * * This function either succeeds or calls exit(2) to terminate the current * process. On success, the file descriptor of the signal pipe is returned. */ int signal_init(void) { int ret; if (pipe(signal_pipe) < 0) { ret = -ERRNO_TO_DSS_ERROR(errno); goto err_out; } ret = mark_fd_nonblocking(signal_pipe[0]); if (ret < 0) goto err_out; ret = mark_fd_nonblocking(signal_pipe[1]); if (ret < 0) goto err_out; return signal_pipe[0]; err_out: DSS_EMERG_LOG("%s\n", dss_strerror(-ret)); exit(EXIT_FAILURE); } /* Write the signal number to the signal pipe, abort on errors. */ static void generic_signal_handler(int s) { /* * Signal handlers that make system calls must save a copy of errno on * entry to the handler and restore it on exit, to prevent the * possibility of overwriting a errno value that had previously been * set in the main program. */ int save_errno = errno; ssize_t ret = write(signal_pipe[1], &s, sizeof(int)); if (ret == sizeof(int)) { errno = save_errno; return; } if (ret < 0) DSS_EMERG_LOG("%s\n", strerror(errno)); else DSS_EMERG_LOG("short write to signal pipe\n"); exit(EXIT_FAILURE); } /* * If there is a child to reap, return its pid and exit status via the pointer * arguments. * * Returns a negative error code on errors, zero, if there was no child to * reap, one if a child was reaped successfully. Only if the function returns * one, the contents of pid and status are meaningful. */ int reap_child(pid_t *pid, int *status) { *pid = waitpid(-1, status, WNOHANG); if (!*pid) return 0; if (*pid < 0) return -ERRNO_TO_DSS_ERROR(errno); if (WIFEXITED(*status)) DSS_DEBUG_LOG("child %i exited. Exit status: %i\n", (int)*pid, WEXITSTATUS(*status)); else if (WIFSIGNALED(*status)) DSS_DEBUG_LOG("child %i was killed by signal %i\n", (int)*pid, WTERMSIG(*status)); else DSS_WARNING_LOG("child %i terminated abormally\n", (int)*pid); return 1; } /* * Install the generic signal handler for the given signal, return 1 on success * and -E_SIGNAL_SIG_ERR on errors. */ int install_sighandler(int sig) { DSS_DEBUG_LOG("catching signal %d\n", sig); if (signal(sig, &generic_signal_handler) != SIG_ERR) return 1; return -E_SIGNAL_SIG_ERR; } /* * This should be called if the fd for the signal pipe is ready for reading. * It returns the number of the next pending signal. If the read was * interrupted by another signal, the function returns 0. On errors, a negative * error code is returned. */ int next_signal(void) { int s, err; ssize_t r; r = read(signal_pipe[0], &s, sizeof(s)); if (r == sizeof(s)) { DSS_DEBUG_LOG("next signal: %d\n", s); return s; } err = errno; assert(r < 0); if (err == EAGAIN) return 0; DSS_ERROR_LOG("failed to read from signal pipe\n"); return -ERRNO_TO_DSS_ERROR(err); } /* Close the signal pipe. */ void signal_shutdown(void) { close(signal_pipe[1]); }