Mercurial > hg
changeset 40118:cd490ac908c0
rust-chg: extract signal handlers from chg/procutil.c
abortmsgerrno() and debugmsg() are removed, and the public interface instead
returns success/error status. Since signal handlers can't propagate errors,
the result of kill() is just ignored.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 24 Sep 2018 22:04:57 +0900 |
parents | dd23eb81a3a3 |
children | e70b616a077b |
files | rust/chg/build.rs rust/chg/src/sighandlers.c |
diffstat | 2 files changed, 178 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/chg/build.rs Sun Oct 07 23:19:49 2018 +0900 +++ b/rust/chg/build.rs Mon Sep 24 22:04:57 2018 +0900 @@ -4,5 +4,6 @@ cc::Build::new() .warnings(true) .file("src/sendfds.c") + .file("src/sighandlers.c") .compile("procutil"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/chg/src/sighandlers.c Mon Sep 24 22:04:57 2018 +0900 @@ -0,0 +1,177 @@ +/* + * Signal handlers for cHg + * + * Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org> + * + * This software may be used and distributed according to the terms of the + * GNU General Public License version 2 or any later version. + */ + +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifdef __GNUC__ +#define UNUSED_ __attribute__((unused)) +#else +#define UNUSED_ +#endif + +static pid_t pagerpid = 0; +static pid_t peerpgid = 0; +static pid_t peerpid = 0; + +static void forwardsignal(int sig) +{ + assert(peerpid > 0); + (void)kill(peerpid, sig); +} + +static void forwardsignaltogroup(int sig) +{ + /* prefer kill(-pgid, sig), fallback to pid if pgid is invalid */ + pid_t killpid = peerpgid > 1 ? -peerpgid : peerpid; + (void)kill(killpid, sig); +} + +static void handlestopsignal(int sig) +{ + sigset_t unblockset, oldset; + struct sigaction sa, oldsa; + if (sigemptyset(&unblockset) < 0) + return; + if (sigaddset(&unblockset, sig) < 0) + return; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_RESTART; + if (sigemptyset(&sa.sa_mask) < 0) + return; + + forwardsignal(sig); + if (raise(sig) < 0) /* resend to self */ + return; + if (sigaction(sig, &sa, &oldsa) < 0) + return; + if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0) + return; + /* resent signal will be handled before sigprocmask() returns */ + if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0) + return; + if (sigaction(sig, &oldsa, NULL) < 0) + return; +} + +static void handlechildsignal(int sig UNUSED_) +{ + if (peerpid == 0 || pagerpid == 0) + return; + /* if pager exits, notify the server with SIGPIPE immediately. + * otherwise the server won't get SIGPIPE if it does not write + * anything. (issue5278) */ + if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid) + kill(peerpid, SIGPIPE); +} + +/* + * Installs signal handlers. + * + * Returns 0 on success, -1 on error and errno is set appropriately. + * Installed handlers wouldn't be cleaned up on error. + */ +int setupsignalhandler(pid_t pid, pid_t pgid) +{ + if (pid <= 0) { + errno = EINVAL; + return -1; + } + peerpid = pid; + peerpgid = (pgid <= 1 ? 0 : pgid); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + /* deadly signals meant to be sent to a process group: + * - SIGHUP: usually generated by the kernel, when termination of a + * process causes that process group to become orphaned + * - SIGINT: usually generated by the terminal */ + sa.sa_handler = forwardsignaltogroup; + sa.sa_flags = SA_RESTART; + if (sigemptyset(&sa.sa_mask) < 0) + return -1; + if (sigaction(SIGHUP, &sa, NULL) < 0) + return -1; + if (sigaction(SIGINT, &sa, NULL) < 0) + return -1; + + /* terminate frontend by double SIGTERM in case of server freeze */ + sa.sa_handler = forwardsignal; + sa.sa_flags |= SA_RESETHAND; + if (sigaction(SIGTERM, &sa, NULL) < 0) + return -1; + + /* notify the worker about window resize events */ + sa.sa_flags = SA_RESTART; + if (sigaction(SIGWINCH, &sa, NULL) < 0) + return -1; + /* forward user-defined signals */ + if (sigaction(SIGUSR1, &sa, NULL) < 0) + return -1; + if (sigaction(SIGUSR2, &sa, NULL) < 0) + return -1; + /* propagate job control requests to worker */ + sa.sa_handler = forwardsignal; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCONT, &sa, NULL) < 0) + return -1; + sa.sa_handler = handlestopsignal; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + return -1; + /* get notified when pager exits */ + sa.sa_handler = handlechildsignal; + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) < 0) + return -1; + + return 0; +} + +/* + * Restores signal handlers to the default, and masks SIGINT. + * + * Returns 0 on success, -1 on error and errno is set appropriately. + */ +int restoresignalhandler(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_RESTART; + if (sigemptyset(&sa.sa_mask) < 0) + return -1; + + if (sigaction(SIGHUP, &sa, NULL) < 0) + return -1; + if (sigaction(SIGTERM, &sa, NULL) < 0) + return -1; + if (sigaction(SIGWINCH, &sa, NULL) < 0) + return -1; + if (sigaction(SIGCONT, &sa, NULL) < 0) + return -1; + if (sigaction(SIGTSTP, &sa, NULL) < 0) + return -1; + if (sigaction(SIGCHLD, &sa, NULL) < 0) + return -1; + + /* ignore Ctrl+C while shutting down to make pager exits cleanly */ + sa.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &sa, NULL) < 0) + return -1; + + peerpid = 0; + return 0; +}