forked from lix-project/lix
Merge "util.hh: split out signals stuff" into main
This commit is contained in:
commit
50c401b4c1
32 changed files with 310 additions and 251 deletions
|
@ -40,6 +40,7 @@ extern "C" {
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "markdown.hh"
|
#include "markdown.hh"
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "print.hh"
|
#include "print.hh"
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "print-ambiguous.hh"
|
#include "print-ambiguous.hh"
|
||||||
#include "print.hh"
|
#include "print.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "ansicolor.hh"
|
#include "ansicolor.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "english.hh"
|
#include "english.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "value-to-json.hh"
|
#include "value-to-json.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "xml-writer.hh"
|
#include "xml-writer.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "loggers.hh"
|
#include "loggers.hh"
|
||||||
#include "progress-bar.hh"
|
#include "progress-bar.hh"
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "nar-info-disk-cache.hh"
|
#include "nar-info-disk-cache.hh"
|
||||||
#include "nar-accessor.hh"
|
#include "nar-accessor.hh"
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "worker.hh"
|
#include "worker.hh"
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
#include "nar-info.hh"
|
#include "nar-info.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
#include "drv-output-substitution-goal.hh"
|
#include "drv-output-substitution-goal.hh"
|
||||||
#include "local-derivation-goal.hh"
|
#include "local-derivation-goal.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "hook-instance.hh"
|
#include "hook-instance.hh"
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "s3.hh"
|
#include "s3.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "compression.hh"
|
#include "compression.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "topo-sort.hh"
|
#include "topo-sort.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "compression.hh"
|
#include "compression.hh"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
#include "gc-store.hh"
|
#include "gc-store.hh"
|
||||||
#include "remote-fs-accessor.hh"
|
#include "remote-fs-accessor.hh"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "sqlite.hh"
|
#include "sqlite.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "url.hh"
|
#include "url.hh"
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "remote-store.hh"
|
#include "remote-store.hh"
|
||||||
|
#include "signals.hh"
|
||||||
// FIXME this should not be here, see TODO below on
|
// FIXME this should not be here, see TODO below on
|
||||||
// `addMultipleToStore`.
|
// `addMultipleToStore`.
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "tarfile.hh"
|
#include "tarfile.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
|
|
||||||
#include <archive.h>
|
#include <archive.h>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
184
src/libutil/signals.cc
Normal file
184
src/libutil/signals.cc
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
#include "signals.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::atomic<bool> _isInterrupted = false;
|
||||||
|
|
||||||
|
static thread_local bool interruptThrown = false;
|
||||||
|
thread_local std::function<bool()> interruptCheck;
|
||||||
|
|
||||||
|
void setInterruptThrown()
|
||||||
|
{
|
||||||
|
interruptThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _interrupted()
|
||||||
|
{
|
||||||
|
/* Block user interrupts while an exception is being handled.
|
||||||
|
Throwing an exception while another exception is being handled
|
||||||
|
kills the program! */
|
||||||
|
if (!interruptThrown && !std::uncaught_exceptions()) {
|
||||||
|
interruptThrown = true;
|
||||||
|
throw Interrupted("interrupted by the user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/* We keep track of interrupt callbacks using integer tokens, so we can iterate
|
||||||
|
safely without having to lock the data structure while executing arbitrary
|
||||||
|
functions.
|
||||||
|
*/
|
||||||
|
struct InterruptCallbacks {
|
||||||
|
typedef int64_t Token;
|
||||||
|
|
||||||
|
/* We use unique tokens so that we can't accidentally delete the wrong
|
||||||
|
handler because of an erroneous double delete. */
|
||||||
|
Token nextToken = 0;
|
||||||
|
|
||||||
|
/* Used as a list, see InterruptCallbacks comment. */
|
||||||
|
std::map<Token, std::function<void()>> callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Sync<InterruptCallbacks> _interruptCallbacks;
|
||||||
|
|
||||||
|
static void signalHandlerThread(sigset_t set)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
int signal = 0;
|
||||||
|
sigwait(&set, &signal);
|
||||||
|
|
||||||
|
if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP)
|
||||||
|
triggerInterrupt();
|
||||||
|
|
||||||
|
else if (signal == SIGWINCH) {
|
||||||
|
updateWindowSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerInterrupt()
|
||||||
|
{
|
||||||
|
_isInterrupted = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
InterruptCallbacks::Token i = 0;
|
||||||
|
while (true) {
|
||||||
|
std::function<void()> callback;
|
||||||
|
{
|
||||||
|
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||||
|
auto lb = interruptCallbacks->callbacks.lower_bound(i);
|
||||||
|
if (lb == interruptCallbacks->callbacks.end())
|
||||||
|
break;
|
||||||
|
|
||||||
|
callback = lb->second;
|
||||||
|
i = lb->first + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
callback();
|
||||||
|
} catch (...) {
|
||||||
|
ignoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
saveSignalMask();
|
||||||
|
|
||||||
|
sigset_t set;
|
||||||
|
sigemptyset(&set);
|
||||||
|
sigaddset(&set, SIGINT);
|
||||||
|
sigaddset(&set, SIGTERM);
|
||||||
|
sigaddset(&set, SIGHUP);
|
||||||
|
sigaddset(&set, SIGPIPE);
|
||||||
|
sigaddset(&set, SIGWINCH);
|
||||||
|
if (pthread_sigmask(SIG_BLOCK, &set, nullptr))
|
||||||
|
throw SysError("blocking signals");
|
||||||
|
|
||||||
|
std::thread(signalHandlerThread, set).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RAII helper to automatically deregister a callback. */
|
||||||
|
struct InterruptCallbackImpl : InterruptCallback
|
||||||
|
{
|
||||||
|
InterruptCallbacks::Token token;
|
||||||
|
~InterruptCallbackImpl() override
|
||||||
|
{
|
||||||
|
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||||
|
interruptCallbacks->callbacks.erase(token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
auto interruptCallbacks(_interruptCallbacks.lock());
|
||||||
|
auto token = interruptCallbacks->nextToken++;
|
||||||
|
interruptCallbacks->callbacks.emplace(token, callback);
|
||||||
|
|
||||||
|
auto res = std::make_unique<InterruptCallbackImpl>();
|
||||||
|
res->token = token;
|
||||||
|
|
||||||
|
return std::unique_ptr<InterruptCallback>(res.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
93
src/libutil/signals.hh
Normal file
93
src/libutil/signals.hh
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#pragma once
|
||||||
|
/// @file
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "error.hh"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <functional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* User interruption. */
|
||||||
|
|
||||||
|
extern std::atomic<bool> _isInterrupted;
|
||||||
|
|
||||||
|
extern thread_local std::function<bool()> interruptCheck;
|
||||||
|
|
||||||
|
void setInterruptThrown();
|
||||||
|
|
||||||
|
void _interrupted();
|
||||||
|
|
||||||
|
void inline checkInterrupt()
|
||||||
|
{
|
||||||
|
if (_isInterrupted || (interruptCheck && interruptCheck()))
|
||||||
|
_interrupted();
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeError(Interrupted, BaseError);
|
||||||
|
|
||||||
|
void restoreSignals();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a thread that handles various signals. Also block those signals
|
||||||
|
* 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() { };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a function that gets called on SIGINT (in a non-signal
|
||||||
|
* context).
|
||||||
|
*/
|
||||||
|
std::unique_ptr<InterruptCallback> createInterruptCallback(
|
||||||
|
std::function<void()> callback);
|
||||||
|
|
||||||
|
void triggerInterrupt();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A RAII class that causes the current thread to receive SIGUSR1 when
|
||||||
|
* the signal handler thread receives SIGINT. That is, this allows
|
||||||
|
* SIGINT to be multiplexed to multiple threads.
|
||||||
|
*/
|
||||||
|
struct ReceiveInterrupts
|
||||||
|
{
|
||||||
|
pthread_t target;
|
||||||
|
std::unique_ptr<InterruptCallback> callback;
|
||||||
|
|
||||||
|
ReceiveInterrupts()
|
||||||
|
: target(pthread_self())
|
||||||
|
, callback(createInterruptCallback([&]() { pthread_kill(target, SIGUSR1); }))
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -1,4 +1,5 @@
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "serialise.hh"
|
#include "serialise.hh"
|
||||||
#include "cgroup.hh"
|
#include "cgroup.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
@ -1361,31 +1362,6 @@ void closeOnExec(int fd)
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
std::atomic<bool> _isInterrupted = false;
|
|
||||||
|
|
||||||
static thread_local bool interruptThrown = false;
|
|
||||||
thread_local std::function<bool()> interruptCheck;
|
|
||||||
|
|
||||||
void setInterruptThrown()
|
|
||||||
{
|
|
||||||
interruptThrown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _interrupted()
|
|
||||||
{
|
|
||||||
/* Block user interrupts while an exception is being handled.
|
|
||||||
Throwing an exception while another exception is being handled
|
|
||||||
kills the program! */
|
|
||||||
if (!interruptThrown && !std::uncaught_exceptions()) {
|
|
||||||
interruptThrown = true;
|
|
||||||
throw Interrupted("interrupted by the user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
template<class C> C tokenizeString(std::string_view s, std::string_view separators)
|
||||||
{
|
{
|
||||||
C result;
|
C result;
|
||||||
|
@ -1717,7 +1693,7 @@ std::pair<std::string_view, std::string_view> getLine(std::string_view s)
|
||||||
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
||||||
|
|
||||||
|
|
||||||
static void updateWindowSize()
|
void updateWindowSize()
|
||||||
{
|
{
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
if (ioctl(2, TIOCGWINSZ, &ws) == 0) {
|
if (ioctl(2, TIOCGWINSZ, &ws) == 0) {
|
||||||
|
@ -1734,133 +1710,6 @@ std::pair<unsigned short, unsigned short> getWindowSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We keep track of interrupt callbacks using integer tokens, so we can iterate
|
|
||||||
safely without having to lock the data structure while executing arbitrary
|
|
||||||
functions.
|
|
||||||
*/
|
|
||||||
struct InterruptCallbacks {
|
|
||||||
typedef int64_t Token;
|
|
||||||
|
|
||||||
/* We use unique tokens so that we can't accidentally delete the wrong
|
|
||||||
handler because of an erroneous double delete. */
|
|
||||||
Token nextToken = 0;
|
|
||||||
|
|
||||||
/* Used as a list, see InterruptCallbacks comment. */
|
|
||||||
std::map<Token, std::function<void()>> callbacks;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Sync<InterruptCallbacks> _interruptCallbacks;
|
|
||||||
|
|
||||||
static void signalHandlerThread(sigset_t set)
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
int signal = 0;
|
|
||||||
sigwait(&set, &signal);
|
|
||||||
|
|
||||||
if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP)
|
|
||||||
triggerInterrupt();
|
|
||||||
|
|
||||||
else if (signal == SIGWINCH) {
|
|
||||||
updateWindowSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void triggerInterrupt()
|
|
||||||
{
|
|
||||||
_isInterrupted = true;
|
|
||||||
|
|
||||||
{
|
|
||||||
InterruptCallbacks::Token i = 0;
|
|
||||||
while (true) {
|
|
||||||
std::function<void()> callback;
|
|
||||||
{
|
|
||||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
|
||||||
auto lb = interruptCallbacks->callbacks.lower_bound(i);
|
|
||||||
if (lb == interruptCallbacks->callbacks.end())
|
|
||||||
break;
|
|
||||||
|
|
||||||
callback = lb->second;
|
|
||||||
i = lb->first + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
callback();
|
|
||||||
} catch (...) {
|
|
||||||
ignoreException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
saveSignalMask();
|
|
||||||
|
|
||||||
sigset_t set;
|
|
||||||
sigemptyset(&set);
|
|
||||||
sigaddset(&set, SIGINT);
|
|
||||||
sigaddset(&set, SIGTERM);
|
|
||||||
sigaddset(&set, SIGHUP);
|
|
||||||
sigaddset(&set, SIGPIPE);
|
|
||||||
sigaddset(&set, SIGWINCH);
|
|
||||||
if (pthread_sigmask(SIG_BLOCK, &set, nullptr))
|
|
||||||
throw SysError("blocking signals");
|
|
||||||
|
|
||||||
std::thread(signalHandlerThread, set).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
rlim_t savedStackSize = 0;
|
rlim_t savedStackSize = 0;
|
||||||
|
|
||||||
void setStackSize(rlim_t stackSize)
|
void setStackSize(rlim_t stackSize)
|
||||||
|
@ -1951,30 +1800,6 @@ void restoreProcessContext(bool restoreMounts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RAII helper to automatically deregister a callback. */
|
|
||||||
struct InterruptCallbackImpl : InterruptCallback
|
|
||||||
{
|
|
||||||
InterruptCallbacks::Token token;
|
|
||||||
~InterruptCallbackImpl() override
|
|
||||||
{
|
|
||||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
|
||||||
interruptCallbacks->callbacks.erase(token);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback)
|
|
||||||
{
|
|
||||||
auto interruptCallbacks(_interruptCallbacks.lock());
|
|
||||||
auto token = interruptCallbacks->nextToken++;
|
|
||||||
interruptCallbacks->callbacks.emplace(token, callback);
|
|
||||||
|
|
||||||
auto res = std::make_unique<InterruptCallbackImpl>();
|
|
||||||
res->token = token;
|
|
||||||
|
|
||||||
return std::unique_ptr<InterruptCallback>(res.release());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AutoCloseFD createUnixDomainSocket()
|
AutoCloseFD createUnixDomainSocket()
|
||||||
{
|
{
|
||||||
AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM
|
AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM
|
||||||
|
|
|
@ -516,25 +516,6 @@ void closeMostFDs(const std::set<int> & exceptions);
|
||||||
void closeOnExec(int fd);
|
void closeOnExec(int fd);
|
||||||
|
|
||||||
|
|
||||||
/* User interruption. */
|
|
||||||
|
|
||||||
extern std::atomic<bool> _isInterrupted;
|
|
||||||
|
|
||||||
extern thread_local std::function<bool()> interruptCheck;
|
|
||||||
|
|
||||||
void setInterruptThrown();
|
|
||||||
|
|
||||||
void _interrupted();
|
|
||||||
|
|
||||||
void inline checkInterrupt()
|
|
||||||
{
|
|
||||||
if (_isInterrupted || (interruptCheck && interruptCheck()))
|
|
||||||
_interrupted();
|
|
||||||
}
|
|
||||||
|
|
||||||
MakeError(Interrupted, BaseError);
|
|
||||||
|
|
||||||
|
|
||||||
MakeError(FormatError, Error);
|
MakeError(FormatError, Error);
|
||||||
|
|
||||||
|
|
||||||
|
@ -829,61 +810,6 @@ template<typename T>
|
||||||
class Callback;
|
class Callback;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a thread that handles various signals. Also block those signals
|
|
||||||
* 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() { };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a function that gets called on SIGINT (in a non-signal
|
|
||||||
* context).
|
|
||||||
*/
|
|
||||||
std::unique_ptr<InterruptCallback> createInterruptCallback(
|
|
||||||
std::function<void()> callback);
|
|
||||||
|
|
||||||
void triggerInterrupt();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A RAII class that causes the current thread to receive SIGUSR1 when
|
|
||||||
* the signal handler thread receives SIGINT. That is, this allows
|
|
||||||
* SIGINT to be multiplexed to multiple threads.
|
|
||||||
*/
|
|
||||||
struct ReceiveInterrupts
|
|
||||||
{
|
|
||||||
pthread_t target;
|
|
||||||
std::unique_ptr<InterruptCallback> callback;
|
|
||||||
|
|
||||||
ReceiveInterrupts()
|
|
||||||
: target(pthread_self())
|
|
||||||
, callback(createInterruptCallback([&]() { pthread_kill(target, SIGUSR1); }))
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A RAII helper that increments a counter on construction and
|
* A RAII helper that increments a counter on construction and
|
||||||
* decrements it on destruction.
|
* decrements it on destruction.
|
||||||
|
@ -903,6 +829,8 @@ struct MaintainCount
|
||||||
*/
|
*/
|
||||||
std::pair<unsigned short, unsigned short> getWindowSize();
|
std::pair<unsigned short, unsigned short> getWindowSize();
|
||||||
|
|
||||||
|
void updateWindowSize();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used in various places.
|
* Used in various places.
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "daemon.hh"
|
#include "daemon.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
|
#include "signals.hh"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
|
#include "signals.hh"
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
Loading…
Reference in a new issue