diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml
index d101661b7..f5efdad85 100644
--- a/doc/manual/release-notes.xml
+++ b/doc/manual/release-notes.xml
@@ -66,6 +66,9 @@ irreversible.
TODO: nix-pack-closure and
nix-unpack-closure.
+ TODO: open files etc. are now used as roots of the
+ garbage collector.
+
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 99828a55d..c80b3dfe9 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -12,10 +12,6 @@
#include
#include
-#ifdef __CYGWIN__
-#include
-#endif
-
#include
#include
@@ -321,13 +317,6 @@ const char * * strings2CharPtrs(const Strings & ss)
}
-/* Hack for Cygwin: _exit() doesn't seem to work quite right, since
- some Berkeley DB code appears to be called when a child exits
- through _exit() (e.g., because execve() failed). So call the
- Windows API directly. */
-#ifdef __CYGWIN__
-#define _exit(n) ExitProcess(n)
-#endif
//////////////////////////////////////////////////////////////////////
@@ -460,9 +449,9 @@ static void killUser(uid_t uid)
} catch (exception & e) {
cerr << format("build error: %1%\n") % e.what();
- _exit(1);
+ quickExit(1);
}
- _exit(0);
+ quickExit(0);
}
/* parent */
@@ -944,7 +933,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
} catch (exception & e) {
cerr << format("build error: %1%\n") % e.what();
}
- _exit(1);
+ quickExit(1);
}
/* parent */
@@ -1340,7 +1329,7 @@ void DerivationGoal::startBuilder()
} catch (exception & e) {
cerr << format("build error: %1%\n") % e.what();
}
- _exit(1);
+ quickExit(1);
}
@@ -1779,7 +1768,7 @@ void SubstitutionGoal::tryToRun()
} catch (exception & e) {
cerr << format("substitute error: %1%\n") % e.what();
}
- _exit(1);
+ quickExit(1);
}
/* parent */
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 484a5f2be..3831de440 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -316,6 +316,31 @@ static void findRoots(const Path & path, bool recurseSymlinks,
}
+static void addAdditionalRoots(PathSet & roots)
+{
+ Path rootFinder = getEnv("NIX_ROOT_FINDER",
+ "/nix/libexec/nix/find-runtime-roots.pl"); /* !!! */
+
+ if (rootFinder.empty()) return;
+
+ printMsg(lvlDebug, format("executing `%1%' to find additional roots") % rootFinder);
+
+ string result = runProgram(rootFinder);
+
+ Strings paths = tokenizeString(result, "\n");
+
+ for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) {
+ if (isInStore(*i)) {
+ Path path = toStorePath(*i);
+ if (roots.find(path) == roots.end()) {
+ debug(format("found additional root `%1%'") % path);
+ roots.insert(path);
+ }
+ }
+ }
+}
+
+
static void dfsVisit(const PathSet & paths, const Path & path,
PathSet & visited, Paths & sorted)
{
@@ -370,6 +395,12 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete,
if (!ignoreLiveness)
findRoots(rootsDir, true, roots);
+ /* Add additional roots returned by the program specified by the
+ NIX_ROOT_FINDER environment variable. This is typically used
+ to add running programs to the set of roots (to prevent them
+ from being garbage collected). */
+ addAdditionalRoots(roots);
+
if (action == gcReturnRoots) {
result = roots;
return;
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 9e3e0bae2..a8ad3fe48 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -13,6 +13,10 @@
#include
#include
+#ifdef __CYGWIN__
+#include
+#endif
+
#include "util.hh"
@@ -434,6 +438,23 @@ void writeFull(int fd, const unsigned char * buf, size_t count)
}
+string drainFD(int fd)
+{
+ string result;
+ unsigned char buffer[4096];
+ while (1) {
+ ssize_t rd = read(fd, buffer, sizeof buffer);
+ if (rd == -1) {
+ if (errno != EINTR)
+ throw SysError("reading from file");
+ }
+ else if (rd == 0) break;
+ else result.append((char *) buffer, rd);
+ }
+ return result;
+}
+
+
//////////////////////////////////////////////////////////////////////
@@ -643,6 +664,69 @@ void Pid::setSeparatePG(bool separatePG)
+//////////////////////////////////////////////////////////////////////
+
+
+string runProgram(Path program)
+{
+ /* Create a pipe. */
+ Pipe pipe;
+ pipe.create();
+
+ /* Fork. */
+ Pid pid;
+ pid = fork();
+ switch (pid) {
+
+ case -1:
+ throw SysError("unable to fork");
+
+ case 0: /* child */
+ try {
+ pipe.readSide.close();
+
+ if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
+ throw SysError("dupping from-hook write side");
+
+ execl(program.c_str(), program.c_str(), (char *) 0);
+ throw SysError(format("executing `%1%'") % program);
+
+ } catch (exception & e) {
+ cerr << "error: " << e.what() << endl;
+ }
+ quickExit(1);
+ }
+
+ /* Parent. */
+
+ pipe.writeSide.close();
+
+ string result = drainFD(pipe.readSide);
+
+ /* Wait for the child to finish. */
+ int status = pid.wait(true);
+ if (!statusOk(status))
+ throw Error(format("program `%1% %2%")
+ % program % statusToString(status));
+
+ return result;
+}
+
+
+void quickExit(int status)
+{
+#ifdef __CYGWIN__
+ /* Hack for Cygwin: _exit() doesn't seem to work quite right,
+ since some Berkeley DB code appears to be called when a child
+ exits through _exit() (e.g., because execve() failed). So call
+ the Windows API directly. */
+ ExitProcess(status);
+#else
+ _exit(status);
+#endif
+}
+
+
//////////////////////////////////////////////////////////////////////
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 92bdf50d3..fcf995af8 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -185,6 +185,11 @@ void readFull(int fd, unsigned char * buf, size_t count);
void writeFull(int fd, const unsigned char * buf, size_t count);
+/* Read a file descriptor until EOF occurs. */
+string drainFD(int fd);
+
+
+
/* Automatic cleanup of resources. */
class AutoDelete
@@ -249,6 +254,15 @@ public:
};
+/* Run a program and return its stdout in a string (i.e., like the
+ shell backtick operator). */
+string runProgram(Path program);
+
+/* Wrapper around _exit() on Unix and ExitProcess() on Windows. (On
+ Cygwin, _exit() doesn't seem to do the right thing.) */
+void quickExit(int status);
+
+
/* User interruption. */
extern volatile sig_atomic_t _isInterrupted;
diff --git a/tests/common.sh.in b/tests/common.sh.in
index 07250ad6e..f96f28d20 100644
--- a/tests/common.sh.in
+++ b/tests/common.sh.in
@@ -13,6 +13,7 @@ export NIX_DB_DIR=$TEST_ROOT/db
export NIX_CONF_DIR=$TEST_ROOT/etc
export NIX_BIN_DIR=$TEST_ROOT/bin
export NIX_LIBEXEC_DIR=$TEST_ROOT/bin
+export NIX_ROOT_FINDER=
export SHARED=$TEST_ROOT/shared
export REAL_BIN_DIR=@bindir@