about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2004-01-15T20·23+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2004-01-15T20·23+0000
commit447089a5f699f085661287dec4b3d88219f67068 (patch)
treee460c615181db3e6b6f8b48e4e934b372ecac043
parent08719c6c97e25fb362eeb7463d8b764ecefc53cb (diff)
* Catch SIGINT to terminate cleanly when the user tries to interrupt
  Nix.  This is to prevent Berkeley DB from becoming wedged.

  Unfortunately it is not possible to throw C++ exceptions from a
  signal handler.  In fact, you can't do much of anything except
  change variables of type `volatile sig_atomic_t'.  So we set an
  interrupt flag in the signal handler and check it at various
  strategic locations in the code (by calling checkInterrupt()).
  Since this is unlikely to cover all cases (e.g., (semi-)infinite
  loops), sometimes SIGTERM may now be required to kill Nix.

-rw-r--r--src/libexpr/eval.cc2
-rw-r--r--src/libexpr/nixexpr.cc4
-rw-r--r--src/libexpr/parser.cc2
-rw-r--r--src/libmain/shared.cc15
-rw-r--r--src/libstore/db.cc9
-rw-r--r--src/libstore/exec.cc4
-rw-r--r--src/libstore/normalise.cc6
-rw-r--r--src/libstore/pathlocks.cc7
-rw-r--r--src/libstore/references.cc3
-rw-r--r--src/libstore/store.cc2
-rw-r--r--src/libutil/archive.cc6
-rw-r--r--src/libutil/util.cc17
-rw-r--r--src/libutil/util.hh13
13 files changed, 86 insertions, 4 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index f6634e8921a5..0470deee9c0c 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -227,6 +227,8 @@ Expr evalExpr2(EvalState & state, Expr e)
 
 Expr evalExpr(EvalState & state, Expr e)
 {
+    checkInterrupt();
+    
     startNest(nest, lvlVomit,
         format("evaluating expression: %1%") % e);
 
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index 816b39dc1ae3..dd0f5d58af27 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -87,6 +87,8 @@ string aterm2String(ATerm t)
 
 ATerm bottomupRewrite(TermFun & f, ATerm e)
 {
+    checkInterrupt();
+
     if (ATgetType(e) == AT_APPL) {
         AFun fun = ATgetAFun(e);
         int arity = ATgetArity(fun);
@@ -149,6 +151,8 @@ Expr makeAttrs(const ATermMap & attrs)
 
 Expr substitute(const ATermMap & subs, Expr e)
 {
+    checkInterrupt();
+
     ATMatcher m;
     string s;
 
diff --git a/src/libexpr/parser.cc b/src/libexpr/parser.cc
index b9e79e13d5f1..83b656342ff8 100644
--- a/src/libexpr/parser.cc
+++ b/src/libexpr/parser.cc
@@ -26,6 +26,8 @@ struct Cleanup : TermFun
 
     virtual ATerm operator () (ATerm e)
     {
+        checkInterrupt();
+        
         ATMatcher m;
         string s;
 
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index 24bedb3fb63b..17d4dda6703a 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -12,6 +12,12 @@ extern "C" {
 #include "config.h"
 
 
+void sigintHandler(int signo)
+{
+    _isInterrupted = 1;
+}
+
+
 /* Initialize and reorder arguments, then call the actual argument
    processor. */
 static void initAndRun(int argc, char * * argv)
@@ -23,6 +29,15 @@ static void initAndRun(int argc, char * * argv)
     nixStateDir = (string) NIX_STATE_DIR;
     nixDBPath = (string) NIX_STATE_DIR + "/db";
 
+    /* Catch SIGINT. */
+    struct sigaction act, oact;
+    act.sa_handler = sigintHandler;
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    if (sigaction(SIGINT, &act, &oact))
+        throw SysError("installing handler for SIGINT");
+    printMsg(lvlError, "SIG HANDLER INSTALLED");
+
     /* Put the arguments in a vector. */
     Strings args, remaining;
     while (argc--) args.push_back(*argv++);
diff --git a/src/libstore/db.cc b/src/libstore/db.cc
index c89d6b197d7c..d2a0026384b0 100644
--- a/src/libstore/db.cc
+++ b/src/libstore/db.cc
@@ -80,6 +80,7 @@ void Transaction::moveTo(Transaction & t)
 
 void Database::requireEnv()
 {
+    checkInterrupt();
     if (!env) throw Error("database environment not open");
 }
 
@@ -310,6 +311,8 @@ TableId Database::openTable(const string & tableName)
 bool Database::queryString(const Transaction & txn, TableId table, 
     const string & key, string & data)
 {
+    checkInterrupt();
+
     try {
         Db * db = getDb(table);
 
@@ -367,6 +370,7 @@ bool Database::queryStrings(const Transaction & txn, TableId table,
 void Database::setString(const Transaction & txn, TableId table,
     const string & key, const string & data)
 {
+    checkInterrupt();
     try {
         Db * db = getDb(table);
         Dbt kt((void *) key.c_str(), key.length());
@@ -402,6 +406,7 @@ void Database::setStrings(const Transaction & txn, TableId table,
 void Database::delPair(const Transaction & txn, TableId table,
     const string & key)
 {
+    checkInterrupt();
     try {
         Db * db = getDb(table);
         Dbt kt((void *) key.c_str(), key.length());
@@ -423,9 +428,11 @@ void Database::enumTable(const Transaction & txn, TableId table,
         DestroyDbc destroyDbc(dbc);
 
         Dbt kt, dt;
-        while (dbc->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND)
+        while (dbc->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND) {
+            checkInterrupt();
             keys.push_back(
                 string((char *) kt.get_data(), kt.get_size()));
+        }
 
     } catch (DbException e) { rethrow(e); }
 }
diff --git a/src/libstore/exec.cc b/src/libstore/exec.cc
index b25423b4449a..01577143dc9e 100644
--- a/src/libstore/exec.cc
+++ b/src/libstore/exec.cc
@@ -108,7 +108,9 @@ void runProgram(const string & program,
     int status;
     if (waitpid(pid, &status, 0) != pid)
         throw Error("unable to wait for child");
-    
+
+    checkInterrupt();
+
     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 	if (keepFailed) {
 	    printMsg(lvlTalkative, 
diff --git a/src/libstore/normalise.cc b/src/libstore/normalise.cc
index 7ef45e292352..51f90207ead3 100644
--- a/src/libstore/normalise.cc
+++ b/src/libstore/normalise.cc
@@ -96,6 +96,7 @@ Path normaliseStoreExpr(const Path & _nePath, PathSet pending)
     for (PathSet::iterator i = ne.derivation.inputs.begin();
          i != ne.derivation.inputs.end(); i++)
     {
+        checkInterrupt();
         Path nfPath = normaliseStoreExpr(*i, pending);
         realiseClosure(nfPath, pending);
         /* !!! nfPath should be a root of the garbage collector while
@@ -193,6 +194,7 @@ Path normaliseStoreExpr(const Path & _nePath, PathSet pending)
         for (Paths::iterator j = refPaths.begin();
 	     j != refPaths.end(); j++)
 	{
+            checkInterrupt();
 	    Path path = *j;
 	    elem.refs.insert(path);
             if (inClosures.find(path) != inClosures.end())
@@ -209,6 +211,7 @@ Path normaliseStoreExpr(const Path & _nePath, PathSet pending)
     PathSet donePaths;
 
     while (!usedPaths.empty()) {
+        checkInterrupt();
 	PathSet::iterator i = usedPaths.begin();
 	Path path = *i;
 	usedPaths.erase(i);
@@ -291,6 +294,7 @@ void ensurePath(const Path & path, PathSet pending)
     for (Paths::iterator i = subPaths.begin(); 
          i != subPaths.end(); i++)
     {
+        checkInterrupt();
         try {
             normaliseStoreExpr(*i, pending);
             if (isValidPath(path)) return;
@@ -337,6 +341,8 @@ static void requisitesWorker(const Path & nePath,
     bool includeExprs, bool includeSuccessors,
     PathSet & paths, PathSet & doneSet)
 {
+    checkInterrupt();
+    
     if (doneSet.find(nePath) != doneSet.end()) return;
     doneSet.insert(nePath);
 
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 321e965bb34b..d4f980c6487c 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -19,11 +19,14 @@ bool lockFile(int fd, LockType lockType, bool wait)
     lock.l_len = 0; /* entire file */
 
     if (wait) {
-        while (fcntl(fd, F_SETLKW, &lock) != 0)
+        while (fcntl(fd, F_SETLKW, &lock) != 0) {
+            checkInterrupt();
             if (errno != EINTR)
                 throw SysError(format("acquiring/releasing lock"));
+        }
     } else {
         while (fcntl(fd, F_SETLK, &lock) != 0) {
+            checkInterrupt();
             if (errno == EACCES || errno == EAGAIN) return false;
             if (errno != EINTR) 
                 throw SysError(format("acquiring/releasing lock"));
@@ -55,6 +58,7 @@ PathLocks::PathLocks(const PathSet & _paths)
     
     /* Acquire the lock for each path. */
     for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
+        checkInterrupt();
         Path path = *i;
         Path lockPath = path + ".lock";
 
@@ -87,6 +91,7 @@ PathLocks::~PathLocks()
         close(*i);
 
     for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
+        checkInterrupt();
         if (deletePaths) {
             /* This is not safe in general! */
             unlink(i->c_str());
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index 2daf4d4f4d0a..9b20b980a1b2 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -17,6 +17,7 @@ static void search(const string & s,
     for (Strings::iterator i = ids.begin();
          i != ids.end(); )
     {
+        checkInterrupt();
         if (s.find(*i) == string::npos)
             i++;
         else {
@@ -31,6 +32,8 @@ static void search(const string & s,
 void checkPath(const string & path,
     Strings & ids, Strings & seen)
 {
+    checkInterrupt();
+    
     struct stat st;
     if (lstat(path.c_str(), &st))
         throw SysError(format("getting attributes of path `%1%'") % path);
diff --git a/src/libstore/store.cc b/src/libstore/store.cc
index c1d95ab8c5b9..4cd77796ea36 100644
--- a/src/libstore/store.cc
+++ b/src/libstore/store.cc
@@ -133,7 +133,7 @@ void copyPath(const Path & src, const Path & dst)
             source.fd = fds[0];
             restorePath(dst, source);
             _exit(0);
-        }  catch (exception & e) {
+        } catch (exception & e) {
             cerr << "error: " << e.what() << endl;
         }
         _exit(1);        
diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc
index 90a039164b58..2b8fb2f10adb 100644
--- a/src/libutil/archive.cc
+++ b/src/libutil/archive.cc
@@ -83,6 +83,7 @@ static void dumpContents(const Path & path, unsigned int size,
     unsigned int total = 0;
     ssize_t n;
     while ((n = read(fd, buf, sizeof(buf)))) {
+        checkInterrupt();
         if (n == -1) throw SysError("reading file " + path);
         total += n;
         sink(buf, n);
@@ -200,6 +201,8 @@ static void restoreEntry(const Path & path, RestoreSource & source)
     if (s != "(") throw badArchive("expected open tag");
 
     while (1) {
+        checkInterrupt();
+
         s = readString(source);
 
         if (s == ")") {
@@ -224,6 +227,7 @@ static void restoreContents(int fd, const Path & path, RestoreSource & source)
     unsigned char buf[65536];
 
     while (left) {
+        checkInterrupt();
         unsigned int n = sizeof(buf);
         if (n > left) n = left;
         source(buf, n);
@@ -247,6 +251,8 @@ static void restore(const Path & path, RestoreSource & source)
     AutoCloseFD fd;
 
     while (1) {
+        checkInterrupt();
+
         s = readString(source);
 
         if (s == ")") {
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 28e276a329b3..5c8b7279c42d 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -132,6 +132,7 @@ Strings readDirectory(const Path & path)
 
     struct dirent * dirent;
     while (errno = 0, dirent = readdir(dir)) { /* sic */
+        checkInterrupt();
         string name = dirent->d_name;
         if (name == "." || name == "..") continue;
         names.push_back(name);
@@ -144,6 +145,8 @@ Strings readDirectory(const Path & path)
 
 void deletePath(const Path & path)
 {
+    checkInterrupt();
+
     printMsg(lvlVomit, format("deleting path `%1%'") % path);
 
     struct stat st;
@@ -170,6 +173,8 @@ void deletePath(const Path & path)
 
 void makePathReadOnly(const Path & path)
 {
+    checkInterrupt();
+
     struct stat st;
     if (lstat(path.c_str(), &st))
 	throw SysError(format("getting attributes of path `%1%'") % path);
@@ -199,6 +204,7 @@ static Path tempName()
 Path createTempDir()
 {
     while (1) {
+        checkInterrupt();
 	Path tmpDir = tempName();
 	if (mkdir(tmpDir.c_str(), 0777) == 0) return tmpDir;
 	if (errno != EEXIST)
@@ -246,6 +252,7 @@ void Nest::open(Verbosity level, const format & f)
 
 void printMsg_(Verbosity level, const format & f)
 {
+    checkInterrupt();
     if (level > verbosity) return;
     string spaces;
     for (int i = 0; i < nestingLevel; i++)
@@ -257,6 +264,7 @@ void printMsg_(Verbosity level, const format & f)
 void readFull(int fd, unsigned char * buf, size_t count)
 {
     while (count) {
+        checkInterrupt();
         ssize_t res = read(fd, (char *) buf, count);
         if (res == -1) throw SysError("reading from file");
         if (res == 0) throw Error("unexpected end-of-file");
@@ -269,6 +277,7 @@ void readFull(int fd, unsigned char * buf, size_t count)
 void writeFull(int fd, const unsigned char * buf, size_t count)
 {
     while (count) {
+        checkInterrupt();
         ssize_t res = write(fd, (char *) buf, count);
         if (res == -1) throw SysError("writing to file");
         count -= res;
@@ -344,3 +353,11 @@ AutoCloseDir::operator DIR *()
     return dir;
 }
 
+
+volatile sig_atomic_t _isInterrupted = 0;
+
+void _interrupted()
+{
+    _isInterrupted = 0;
+    throw Error("interrupted by the user");
+}
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 5d27ac1bddb4..34fff003b88f 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <dirent.h>
 #include <unistd.h>
+#include <signal.h>
 
 #include <boost/format.hpp>
 
@@ -179,4 +180,16 @@ public:
 };
 
 
+/* User interruption. */
+
+extern volatile sig_atomic_t _isInterrupted;
+
+void _interrupted();
+
+void inline checkInterrupt()
+{
+    if (_isInterrupted) _interrupted();
+}
+
+
 #endif /* !__UTIL_H */