diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/hash.cc | 8 | ||||
-rw-r--r-- | src/libutil/logging.hh | 1 | ||||
-rw-r--r-- | src/libutil/serialise.cc | 8 | ||||
-rw-r--r-- | src/libutil/util.cc | 156 | ||||
-rw-r--r-- | src/libutil/util.hh | 64 |
5 files changed, 125 insertions, 112 deletions
diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 81aced0fde16..aa50fceb9e3e 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -165,7 +165,13 @@ Hash parseHash32(HashType ht, const string & s) unsigned int i = b / 8; unsigned int j = b % 8; hash.hash[i] |= digit << j; - if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j); + + if (i < hash.hashSize - 1) { + hash.hash[i + 1] |= digit >> (8 - j); + } else { + if (digit >> (8 - j)) + throw BadHash(format("invalid base-32 hash ‘%1%’") % s); + } } return hash; diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index ba99a81c3826..3e6c4b54853c 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -79,6 +79,7 @@ extern Verbosity verbosity; /* suppress msgs > this */ #define printError(args...) printMsg(lvlError, args) #define printInfo(args...) printMsg(lvlInfo, args) #define debug(args...) printMsg(lvlDebug, args) +#define vomit(args...) printMsg(lvlVomit, args) void warnOnce(bool & haveWarned, const FormatOrString & fs); diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 24c6d107359e..a68f7a0fa8ee 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -3,6 +3,7 @@ #include <cstring> #include <cerrno> +#include <memory> namespace nix { @@ -236,11 +237,10 @@ size_t readString(unsigned char * buf, size_t max, Source & source) string readString(Source & source) { size_t len = readInt(source); - unsigned char * buf = new unsigned char[len]; - AutoDeleteArray<unsigned char> d(buf); - source(buf, len); + auto buf = std::make_unique<unsigned char[]>(len); + source(buf.get(), len); readPadding(len, source); - return string((char *) buf, len); + return string((char *) buf.get(), len); } Source & operator >> (Source & in, string & s) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index ce16cc30a5c7..e9457582810a 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -2,14 +2,16 @@ #include "util.hh" #include "affinity.hh" +#include "sync.hh" -#include <iostream> +#include <cctype> #include <cerrno> #include <cstdio> #include <cstdlib> -#include <sstream> #include <cstring> -#include <cctype> +#include <iostream> +#include <sstream> +#include <thread> #include <sys/wait.h> #include <unistd.h> @@ -234,11 +236,11 @@ DirEntries readDirectory(const Path & path) DirEntries entries; entries.reserve(64); - AutoCloseDir dir = opendir(path.c_str()); + AutoCloseDir dir(opendir(path.c_str())); if (!dir) throw SysError(format("opening directory ‘%1%’") % path); struct dirent * dirent; - while (errno = 0, dirent = readdir(dir)) { /* sic */ + while (errno = 0, dirent = readdir(dir.get())) { /* sic */ checkInterrupt(); string name = dirent->d_name; if (name == "." || name == "..") continue; @@ -272,11 +274,10 @@ string readFile(int fd) if (fstat(fd, &st) == -1) throw SysError("statting file"); - unsigned char * buf = new unsigned char[st.st_size]; - AutoDeleteArray<unsigned char> d(buf); - readFull(fd, buf, st.st_size); + auto buf = std::make_unique<unsigned char[]>(st.st_size); + readFull(fd, buf.get(), st.st_size); - return string((char *) buf, st.st_size); + return string((char *) buf.get(), st.st_size); } @@ -646,69 +647,26 @@ void Pipe::create() ////////////////////////////////////////////////////////////////////// -AutoCloseDir::AutoCloseDir() -{ - dir = 0; -} - - -AutoCloseDir::AutoCloseDir(DIR * dir) -{ - this->dir = dir; -} - - -AutoCloseDir::~AutoCloseDir() -{ - close(); -} - - -void AutoCloseDir::operator =(DIR * dir) -{ - this->dir = dir; -} - - -AutoCloseDir::operator DIR *() -{ - return dir; -} - - -void AutoCloseDir::close() -{ - if (dir) { - closedir(dir); - dir = 0; - } -} - - -////////////////////////////////////////////////////////////////////// - - Pid::Pid() - : pid(-1), separatePG(false), killSignal(SIGKILL) { } Pid::Pid(pid_t pid) - : pid(pid), separatePG(false), killSignal(SIGKILL) + : pid(pid) { } Pid::~Pid() { - kill(); + if (pid != -1) kill(); } void Pid::operator =(pid_t pid) { - if (this->pid != pid) kill(); + if (this->pid != -1 && this->pid != pid) kill(); this->pid = pid; killSignal = SIGKILL; // reset signal to default } @@ -720,9 +678,9 @@ Pid::operator pid_t() } -void Pid::kill(bool quiet) +int Pid::kill(bool quiet) { - if (pid == -1 || pid == 0) return; + assert(pid != -1); if (!quiet) printError(format("killing process %1%") % pid); @@ -733,32 +691,20 @@ void Pid::kill(bool quiet) if (::kill(separatePG ? -pid : pid, killSignal) != 0) printError((SysError(format("killing process %1%") % pid).msg())); - /* Wait until the child dies, disregarding the exit status. */ - int status; - while (waitpid(pid, &status, 0) == -1) { - checkInterrupt(); - if (errno != EINTR) { - printError( - (SysError(format("waiting for process %1%") % pid).msg())); - break; - } - } - - pid = -1; + return wait(); } -int Pid::wait(bool block) +int Pid::wait() { assert(pid != -1); while (1) { int status; - int res = waitpid(pid, &status, block ? 0 : WNOHANG); + int res = waitpid(pid, &status, 0); if (res == pid) { pid = -1; return status; } - if (res == 0 && !block) return -1; if (errno != EINTR) throw SysError("cannot get child exit status"); checkInterrupt(); @@ -823,7 +769,7 @@ void killUser(uid_t uid) _exit(0); }, options); - int status = pid.wait(true); + int status = pid.wait(); if (status != 0) throw Error(format("cannot kill processes for uid ‘%1%’: %2%") % uid % statusToString(status)); @@ -934,7 +880,7 @@ string runProgram(Path program, bool searchPath, const Strings & args, string result = drainFD(out.readSide.get()); /* Wait for the child to finish. */ - int status = pid.wait(true); + int status = pid.wait(); if (!statusOk(status)) throw ExecError(status, format("program ‘%1%’ %2%") % program % statusToString(status)); @@ -976,7 +922,7 @@ void restoreSIGPIPE() ////////////////////////////////////////////////////////////////////// -volatile sig_atomic_t _isInterrupted = 0; +bool _isInterrupted = false; thread_local bool interruptThrown = false; @@ -1243,4 +1189,64 @@ void callFailure(const std::function<void(std::exception_ptr exc)> & failure, st } +static Sync<std::list<std::function<void()>>> _interruptCallbacks; + +static void signalHandlerThread(sigset_t set) +{ + while (true) { + int signal = 0; + sigwait(&set, &signal); + + if (signal == SIGINT || signal == SIGTERM || signal == SIGHUP) { + _isInterrupted = 1; + + { + auto interruptCallbacks(_interruptCallbacks.lock()); + for (auto & callback : *interruptCallbacks) { + try { + callback(); + } catch (...) { + ignoreException(); + } + } + } + } + } +} + +void startSignalHandlerThread() +{ + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGTERM); + sigaddset(&set, SIGHUP); + if (pthread_sigmask(SIG_BLOCK, &set, nullptr)) + throw SysError("blocking signals"); + + std::thread(signalHandlerThread, set).detach(); +} + +/* RAII helper to automatically deregister a callback. */ +struct InterruptCallbackImpl : InterruptCallback +{ + std::list<std::function<void()>>::iterator it; + ~InterruptCallbackImpl() override + { + _interruptCallbacks.lock()->erase(it); + } +}; + +std::unique_ptr<InterruptCallback> createInterruptCallback(std::function<void()> callback) +{ + auto interruptCallbacks(_interruptCallbacks.lock()); + interruptCallbacks->push_back(callback); + + auto res = std::make_unique<InterruptCallbackImpl>(); + res->it = interruptCallbacks->end(); + res->it--; + + return res; +} + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 2e48034ae86c..b68d48582b34 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -139,18 +139,6 @@ string drainFD(int fd); /* Automatic cleanup of resources. */ -template <class T> -struct AutoDeleteArray -{ - T * p; - AutoDeleteArray(T * p) : p(p) { } - ~AutoDeleteArray() - { - delete [] p; - } -}; - - class AutoDelete { Path path; @@ -192,32 +180,30 @@ public: }; -class AutoCloseDir +struct DIRDeleter { - DIR * dir; -public: - AutoCloseDir(); - AutoCloseDir(DIR * dir); - ~AutoCloseDir(); - void operator =(DIR * dir); - operator DIR *(); - void close(); + void operator()(DIR * dir) const { + closedir(dir); + } }; +typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir; + class Pid { - pid_t pid; - bool separatePG; - int killSignal; + pid_t pid = -1; + bool separatePG = false; + int killSignal = SIGKILL; public: Pid(); Pid(pid_t pid); ~Pid(); void operator =(pid_t pid); operator pid_t(); - void kill(bool quiet = false); - int wait(bool block); + int kill(bool quiet = false); + int wait(); + void setSeparatePG(bool separatePG); void setKillSignal(int signal); pid_t release(); @@ -233,11 +219,10 @@ void killUser(uid_t uid); pid to the caller. */ struct ProcessOptions { - string errorPrefix; - bool dieWithParent; - bool runExitHandlers; - bool allowVfork; - ProcessOptions() : errorPrefix("error: "), dieWithParent(true), runExitHandlers(false), allowVfork(true) { }; + string errorPrefix = "error: "; + bool dieWithParent = true; + bool runExitHandlers = false; + bool allowVfork = true; }; pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions()); @@ -278,7 +263,7 @@ void restoreSIGPIPE(); /* User interruption. */ -extern volatile sig_atomic_t _isInterrupted; +extern bool _isInterrupted; extern thread_local bool interruptThrown; @@ -434,4 +419,19 @@ void callSuccess( } +/* Start a thread that handles various signals. Also block those signals + on the current thread (and thus any threads created by it). */ +void startSignalHandlerThread(); + +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); + + } |