From a2a4b4fb8f72f12e8299dac35216fe3d61d80370 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 Feb 2023 18:38:54 +0100 Subject: [PATCH] libutil: Provide alternatives to startSignalHandlerThread --- src/libutil/util.cc | 43 +++++++++++++++++++++++++++++++++++++++++-- src/libutil/util.hh | 20 ++++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 2d2bf6dca8e..220a0f71dab 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1699,13 +1699,38 @@ void triggerInterrupt() } static sigset_t savedSignalMask; +static bool savedSignalMaskIsSet = false; + +void setChildSignalMask(sigset_t *sigs) { + assert(sigs); // C style function, but think of sigs as a reference + +#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE + sigemptyset(&savedSignalMask); + // There's no "assign" or "copy" function, so we rely on (math) idempotence + // of the or operator: a or a = a. + sigorset(&savedSignalMask, sigs, sigs); +#else + // Without sigorset, our best bet is to assume that sigset_t is a type that + // can be assigned directly, such as is the case for a sigset_t defined as + // an integer type. + savedSignalMask = *sigs; +#endif + + savedSignalMaskIsSet = true; +} + +void saveSignalMask() { + if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) + throw SysError("querying signal mask"); + + savedSignalMaskIsSet = true; +} void startSignalHandlerThread() { updateWindowSize(); - if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) - throw SysError("querying signal mask"); + saveSignalMask(); sigset_t set; sigemptyset(&set); @@ -1722,6 +1747,20 @@ void startSignalHandlerThread() static void restoreSignals() { + // If startSignalHandlerThread wasn't called, that means we're not running + // in a proper libmain process, but a process that presumably manages its + // own signal handlers. Such a process should call either + // - initNix(), to be a proper libmain process + // - startSignalHandlerThread(), to resemble libmain regarding signal + // handling only + // - saveSignalMask(), for processes that define their own signal handling + // thread + // TODO: Warn about this? Have a default signal mask? The latter depends on + // whether we should generally inherit signal masks from the caller. + // I don't know what the larger unix ecosystem expects from us here. + if (!savedSignalMaskIsSet) + return; + if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) throw SysError("restoring signals"); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 5fd58c5bb37..c90ec6e3656 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -340,7 +340,10 @@ void setStackSize(size_t stackSize); /* Restore the original inherited Unix process context (such as signal - masks, stack size). */ + masks, stack size). + + See startSignalHandlerThread(), saveSignalMask(). + */ void restoreProcessContext(bool restoreMounts = true); /* Save the current mount namespace. Ignored if called more than @@ -643,9 +646,22 @@ class Callback; /* Start a thread that handles various signals. Also block those signals - on the current thread (and thus any threads created by it). */ + on the current thread (and thus any threads created by it). + Saves the signal mask before changing the mask to block those signals. + See saveSignalMask(). */ void startSignalHandlerThread(); +/* Saves the signal mask, which is the signal mask that nix will restore + before creating child processes. + See setChildSignalMask() to set an arbitrary signal mask instead of the + current mask. */ +void saveSignalMask(); + +/* Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't + necessarily match the current thread's mask. + See saveSignalMask() to set the saved mask to the current mask. */ +void setChildSignalMask(sigset_t *sigs); + struct InterruptCallback { virtual ~InterruptCallback() { };