* 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.
This commit is contained in:
parent
08719c6c97
commit
447089a5f6
13 changed files with 86 additions and 4 deletions
|
@ -227,6 +227,8 @@ Expr evalExpr2(EvalState & state, Expr e)
|
||||||
|
|
||||||
Expr evalExpr(EvalState & state, Expr e)
|
Expr evalExpr(EvalState & state, Expr e)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
startNest(nest, lvlVomit,
|
startNest(nest, lvlVomit,
|
||||||
format("evaluating expression: %1%") % e);
|
format("evaluating expression: %1%") % e);
|
||||||
|
|
||||||
|
|
|
@ -87,6 +87,8 @@ string aterm2String(ATerm t)
|
||||||
|
|
||||||
ATerm bottomupRewrite(TermFun & f, ATerm e)
|
ATerm bottomupRewrite(TermFun & f, ATerm e)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
if (ATgetType(e) == AT_APPL) {
|
if (ATgetType(e) == AT_APPL) {
|
||||||
AFun fun = ATgetAFun(e);
|
AFun fun = ATgetAFun(e);
|
||||||
int arity = ATgetArity(fun);
|
int arity = ATgetArity(fun);
|
||||||
|
@ -149,6 +151,8 @@ Expr makeAttrs(const ATermMap & attrs)
|
||||||
|
|
||||||
Expr substitute(const ATermMap & subs, Expr e)
|
Expr substitute(const ATermMap & subs, Expr e)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
ATMatcher m;
|
ATMatcher m;
|
||||||
string s;
|
string s;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ struct Cleanup : TermFun
|
||||||
|
|
||||||
virtual ATerm operator () (ATerm e)
|
virtual ATerm operator () (ATerm e)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
ATMatcher m;
|
ATMatcher m;
|
||||||
string s;
|
string s;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,12 @@ extern "C" {
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
||||||
|
void sigintHandler(int signo)
|
||||||
|
{
|
||||||
|
_isInterrupted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Initialize and reorder arguments, then call the actual argument
|
/* Initialize and reorder arguments, then call the actual argument
|
||||||
processor. */
|
processor. */
|
||||||
static void initAndRun(int argc, char * * argv)
|
static void initAndRun(int argc, char * * argv)
|
||||||
|
@ -23,6 +29,15 @@ static void initAndRun(int argc, char * * argv)
|
||||||
nixStateDir = (string) NIX_STATE_DIR;
|
nixStateDir = (string) NIX_STATE_DIR;
|
||||||
nixDBPath = (string) NIX_STATE_DIR + "/db";
|
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. */
|
/* Put the arguments in a vector. */
|
||||||
Strings args, remaining;
|
Strings args, remaining;
|
||||||
while (argc--) args.push_back(*argv++);
|
while (argc--) args.push_back(*argv++);
|
||||||
|
|
|
@ -80,6 +80,7 @@ void Transaction::moveTo(Transaction & t)
|
||||||
|
|
||||||
void Database::requireEnv()
|
void Database::requireEnv()
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
if (!env) throw Error("database environment not open");
|
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,
|
bool Database::queryString(const Transaction & txn, TableId table,
|
||||||
const string & key, string & data)
|
const string & key, string & data)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Db * db = getDb(table);
|
Db * db = getDb(table);
|
||||||
|
|
||||||
|
@ -367,6 +370,7 @@ bool Database::queryStrings(const Transaction & txn, TableId table,
|
||||||
void Database::setString(const Transaction & txn, TableId table,
|
void Database::setString(const Transaction & txn, TableId table,
|
||||||
const string & key, const string & data)
|
const string & key, const string & data)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
try {
|
try {
|
||||||
Db * db = getDb(table);
|
Db * db = getDb(table);
|
||||||
Dbt kt((void *) key.c_str(), key.length());
|
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,
|
void Database::delPair(const Transaction & txn, TableId table,
|
||||||
const string & key)
|
const string & key)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
try {
|
try {
|
||||||
Db * db = getDb(table);
|
Db * db = getDb(table);
|
||||||
Dbt kt((void *) key.c_str(), key.length());
|
Dbt kt((void *) key.c_str(), key.length());
|
||||||
|
@ -423,9 +428,11 @@ void Database::enumTable(const Transaction & txn, TableId table,
|
||||||
DestroyDbc destroyDbc(dbc);
|
DestroyDbc destroyDbc(dbc);
|
||||||
|
|
||||||
Dbt kt, dt;
|
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(
|
keys.push_back(
|
||||||
string((char *) kt.get_data(), kt.get_size()));
|
string((char *) kt.get_data(), kt.get_size()));
|
||||||
|
}
|
||||||
|
|
||||||
} catch (DbException e) { rethrow(e); }
|
} catch (DbException e) { rethrow(e); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,8 @@ void runProgram(const string & program,
|
||||||
if (waitpid(pid, &status, 0) != pid)
|
if (waitpid(pid, &status, 0) != pid)
|
||||||
throw Error("unable to wait for child");
|
throw Error("unable to wait for child");
|
||||||
|
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||||
if (keepFailed) {
|
if (keepFailed) {
|
||||||
printMsg(lvlTalkative,
|
printMsg(lvlTalkative,
|
||||||
|
|
|
@ -96,6 +96,7 @@ Path normaliseStoreExpr(const Path & _nePath, PathSet pending)
|
||||||
for (PathSet::iterator i = ne.derivation.inputs.begin();
|
for (PathSet::iterator i = ne.derivation.inputs.begin();
|
||||||
i != ne.derivation.inputs.end(); i++)
|
i != ne.derivation.inputs.end(); i++)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
Path nfPath = normaliseStoreExpr(*i, pending);
|
Path nfPath = normaliseStoreExpr(*i, pending);
|
||||||
realiseClosure(nfPath, pending);
|
realiseClosure(nfPath, pending);
|
||||||
/* !!! nfPath should be a root of the garbage collector while
|
/* !!! 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();
|
for (Paths::iterator j = refPaths.begin();
|
||||||
j != refPaths.end(); j++)
|
j != refPaths.end(); j++)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
Path path = *j;
|
Path path = *j;
|
||||||
elem.refs.insert(path);
|
elem.refs.insert(path);
|
||||||
if (inClosures.find(path) != inClosures.end())
|
if (inClosures.find(path) != inClosures.end())
|
||||||
|
@ -209,6 +211,7 @@ Path normaliseStoreExpr(const Path & _nePath, PathSet pending)
|
||||||
PathSet donePaths;
|
PathSet donePaths;
|
||||||
|
|
||||||
while (!usedPaths.empty()) {
|
while (!usedPaths.empty()) {
|
||||||
|
checkInterrupt();
|
||||||
PathSet::iterator i = usedPaths.begin();
|
PathSet::iterator i = usedPaths.begin();
|
||||||
Path path = *i;
|
Path path = *i;
|
||||||
usedPaths.erase(i);
|
usedPaths.erase(i);
|
||||||
|
@ -291,6 +294,7 @@ void ensurePath(const Path & path, PathSet pending)
|
||||||
for (Paths::iterator i = subPaths.begin();
|
for (Paths::iterator i = subPaths.begin();
|
||||||
i != subPaths.end(); i++)
|
i != subPaths.end(); i++)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
try {
|
try {
|
||||||
normaliseStoreExpr(*i, pending);
|
normaliseStoreExpr(*i, pending);
|
||||||
if (isValidPath(path)) return;
|
if (isValidPath(path)) return;
|
||||||
|
@ -337,6 +341,8 @@ static void requisitesWorker(const Path & nePath,
|
||||||
bool includeExprs, bool includeSuccessors,
|
bool includeExprs, bool includeSuccessors,
|
||||||
PathSet & paths, PathSet & doneSet)
|
PathSet & paths, PathSet & doneSet)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
if (doneSet.find(nePath) != doneSet.end()) return;
|
if (doneSet.find(nePath) != doneSet.end()) return;
|
||||||
doneSet.insert(nePath);
|
doneSet.insert(nePath);
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,14 @@ bool lockFile(int fd, LockType lockType, bool wait)
|
||||||
lock.l_len = 0; /* entire file */
|
lock.l_len = 0; /* entire file */
|
||||||
|
|
||||||
if (wait) {
|
if (wait) {
|
||||||
while (fcntl(fd, F_SETLKW, &lock) != 0)
|
while (fcntl(fd, F_SETLKW, &lock) != 0) {
|
||||||
|
checkInterrupt();
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError(format("acquiring/releasing lock"));
|
throw SysError(format("acquiring/releasing lock"));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
while (fcntl(fd, F_SETLK, &lock) != 0) {
|
while (fcntl(fd, F_SETLK, &lock) != 0) {
|
||||||
|
checkInterrupt();
|
||||||
if (errno == EACCES || errno == EAGAIN) return false;
|
if (errno == EACCES || errno == EAGAIN) return false;
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError(format("acquiring/releasing lock"));
|
throw SysError(format("acquiring/releasing lock"));
|
||||||
|
@ -55,6 +58,7 @@ PathLocks::PathLocks(const PathSet & _paths)
|
||||||
|
|
||||||
/* Acquire the lock for each path. */
|
/* Acquire the lock for each path. */
|
||||||
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
|
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
|
||||||
|
checkInterrupt();
|
||||||
Path path = *i;
|
Path path = *i;
|
||||||
Path lockPath = path + ".lock";
|
Path lockPath = path + ".lock";
|
||||||
|
|
||||||
|
@ -87,6 +91,7 @@ PathLocks::~PathLocks()
|
||||||
close(*i);
|
close(*i);
|
||||||
|
|
||||||
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
|
for (Paths::iterator i = paths.begin(); i != paths.end(); i++) {
|
||||||
|
checkInterrupt();
|
||||||
if (deletePaths) {
|
if (deletePaths) {
|
||||||
/* This is not safe in general! */
|
/* This is not safe in general! */
|
||||||
unlink(i->c_str());
|
unlink(i->c_str());
|
||||||
|
|
|
@ -17,6 +17,7 @@ static void search(const string & s,
|
||||||
for (Strings::iterator i = ids.begin();
|
for (Strings::iterator i = ids.begin();
|
||||||
i != ids.end(); )
|
i != ids.end(); )
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
if (s.find(*i) == string::npos)
|
if (s.find(*i) == string::npos)
|
||||||
i++;
|
i++;
|
||||||
else {
|
else {
|
||||||
|
@ -31,6 +32,8 @@ static void search(const string & s,
|
||||||
void checkPath(const string & path,
|
void checkPath(const string & path,
|
||||||
Strings & ids, Strings & seen)
|
Strings & ids, Strings & seen)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||||
|
|
|
@ -83,6 +83,7 @@ static void dumpContents(const Path & path, unsigned int size,
|
||||||
unsigned int total = 0;
|
unsigned int total = 0;
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
while ((n = read(fd, buf, sizeof(buf)))) {
|
while ((n = read(fd, buf, sizeof(buf)))) {
|
||||||
|
checkInterrupt();
|
||||||
if (n == -1) throw SysError("reading file " + path);
|
if (n == -1) throw SysError("reading file " + path);
|
||||||
total += n;
|
total += n;
|
||||||
sink(buf, n);
|
sink(buf, n);
|
||||||
|
@ -200,6 +201,8 @@ static void restoreEntry(const Path & path, RestoreSource & source)
|
||||||
if (s != "(") throw badArchive("expected open tag");
|
if (s != "(") throw badArchive("expected open tag");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
s = readString(source);
|
s = readString(source);
|
||||||
|
|
||||||
if (s == ")") {
|
if (s == ")") {
|
||||||
|
@ -224,6 +227,7 @@ static void restoreContents(int fd, const Path & path, RestoreSource & source)
|
||||||
unsigned char buf[65536];
|
unsigned char buf[65536];
|
||||||
|
|
||||||
while (left) {
|
while (left) {
|
||||||
|
checkInterrupt();
|
||||||
unsigned int n = sizeof(buf);
|
unsigned int n = sizeof(buf);
|
||||||
if (n > left) n = left;
|
if (n > left) n = left;
|
||||||
source(buf, n);
|
source(buf, n);
|
||||||
|
@ -247,6 +251,8 @@ static void restore(const Path & path, RestoreSource & source)
|
||||||
AutoCloseFD fd;
|
AutoCloseFD fd;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
s = readString(source);
|
s = readString(source);
|
||||||
|
|
||||||
if (s == ")") {
|
if (s == ")") {
|
||||||
|
|
|
@ -132,6 +132,7 @@ Strings readDirectory(const Path & path)
|
||||||
|
|
||||||
struct dirent * dirent;
|
struct dirent * dirent;
|
||||||
while (errno = 0, dirent = readdir(dir)) { /* sic */
|
while (errno = 0, dirent = readdir(dir)) { /* sic */
|
||||||
|
checkInterrupt();
|
||||||
string name = dirent->d_name;
|
string name = dirent->d_name;
|
||||||
if (name == "." || name == "..") continue;
|
if (name == "." || name == "..") continue;
|
||||||
names.push_back(name);
|
names.push_back(name);
|
||||||
|
@ -144,6 +145,8 @@ Strings readDirectory(const Path & path)
|
||||||
|
|
||||||
void deletePath(const Path & path)
|
void deletePath(const Path & path)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
printMsg(lvlVomit, format("deleting path `%1%'") % path);
|
printMsg(lvlVomit, format("deleting path `%1%'") % path);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -170,6 +173,8 @@ void deletePath(const Path & path)
|
||||||
|
|
||||||
void makePathReadOnly(const Path & path)
|
void makePathReadOnly(const Path & path)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st))
|
if (lstat(path.c_str(), &st))
|
||||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||||
|
@ -199,6 +204,7 @@ static Path tempName()
|
||||||
Path createTempDir()
|
Path createTempDir()
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
|
checkInterrupt();
|
||||||
Path tmpDir = tempName();
|
Path tmpDir = tempName();
|
||||||
if (mkdir(tmpDir.c_str(), 0777) == 0) return tmpDir;
|
if (mkdir(tmpDir.c_str(), 0777) == 0) return tmpDir;
|
||||||
if (errno != EEXIST)
|
if (errno != EEXIST)
|
||||||
|
@ -246,6 +252,7 @@ void Nest::open(Verbosity level, const format & f)
|
||||||
|
|
||||||
void printMsg_(Verbosity level, const format & f)
|
void printMsg_(Verbosity level, const format & f)
|
||||||
{
|
{
|
||||||
|
checkInterrupt();
|
||||||
if (level > verbosity) return;
|
if (level > verbosity) return;
|
||||||
string spaces;
|
string spaces;
|
||||||
for (int i = 0; i < nestingLevel; i++)
|
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)
|
void readFull(int fd, unsigned char * buf, size_t count)
|
||||||
{
|
{
|
||||||
while (count) {
|
while (count) {
|
||||||
|
checkInterrupt();
|
||||||
ssize_t res = read(fd, (char *) buf, count);
|
ssize_t res = read(fd, (char *) buf, count);
|
||||||
if (res == -1) throw SysError("reading from file");
|
if (res == -1) throw SysError("reading from file");
|
||||||
if (res == 0) throw Error("unexpected end-of-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)
|
void writeFull(int fd, const unsigned char * buf, size_t count)
|
||||||
{
|
{
|
||||||
while (count) {
|
while (count) {
|
||||||
|
checkInterrupt();
|
||||||
ssize_t res = write(fd, (char *) buf, count);
|
ssize_t res = write(fd, (char *) buf, count);
|
||||||
if (res == -1) throw SysError("writing to file");
|
if (res == -1) throw SysError("writing to file");
|
||||||
count -= res;
|
count -= res;
|
||||||
|
@ -344,3 +353,11 @@ AutoCloseDir::operator DIR *()
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
volatile sig_atomic_t _isInterrupted = 0;
|
||||||
|
|
||||||
|
void _interrupted()
|
||||||
|
{
|
||||||
|
_isInterrupted = 0;
|
||||||
|
throw Error("interrupted by the user");
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#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 */
|
#endif /* !__UTIL_H */
|
||||||
|
|
Loading…
Reference in a new issue