about summary refs log tree commit diff
path: root/src/libutil/util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/util.cc')
-rw-r--r--src/libutil/util.cc156
1 files changed, 81 insertions, 75 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index ce16cc30a5c7..52608ac2a016 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 std::unique_ptr<InterruptCallback>(res.release());
+}
+
 }