diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f6634e8921..0470deee9c 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 816b39dc1a..dd0f5d58af 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 b9e79e13d5..83b656342f 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 24bedb3fb6..17d4dda670 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 c89d6b197d..d2a0026384 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 b25423b444..01577143dc 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 7ef45e2923..51f90207ea 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 321e965bb3..d4f980c648 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 2daf4d4f4d..9b20b980a1 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 c1d95ab8c5..4cd77796ea 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 90a039164b..2b8fb2f10a 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 28e276a329..5c8b7279c4 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 5d27ac1bdd..34fff003b8 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -179,4 +180,16 @@ public: }; +/* User interruption. */ + +extern volatile sig_atomic_t _isInterrupted; + +void _interrupted(); + +void inline checkInterrupt() +{ + if (_isInterrupted) _interrupted(); +} + + #endif /* !__UTIL_H */