summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2006-07-20 12:17:25 +0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2006-07-20 12:17:25 +0000
commitc15f544356dfebf6d08887e73fa156d4e70e2bbc (patch)
treefad9d05cb72330af8e96538e160d896b54fd0ca0
parentebcccbd3581d34d7fefb975c0255a39a3e39e122 (diff)
downloadguix-c15f544356dfebf6d08887e73fa156d4e70e2bbc.tar.gz
* Call find-runtime-roots.pl from the garbage collector to prevent
  running applications etc. from being garbage collected.

-rw-r--r--doc/manual/release-notes.xml3
-rw-r--r--src/libstore/build.cc21
-rw-r--r--src/libstore/gc.cc31
-rw-r--r--src/libutil/util.cc84
-rw-r--r--src/libutil/util.hh14
-rw-r--r--tests/common.sh.in1
6 files changed, 138 insertions, 16 deletions
diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml
index d101661b72..f5efdad858 100644
--- a/doc/manual/release-notes.xml
+++ b/doc/manual/release-notes.xml
@@ -66,6 +66,9 @@ irreversible.</para></warning>
   <listitem><para>TODO: nix-pack-closure and
   nix-unpack-closure.</para></listitem>
 
+  <listitem><para>TODO: open files etc. are now used as roots of the
+  garbage collector.</para></listitem>
+
 </itemizedlist>
 
 </section>
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 99828a55de..c80b3dfe97 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -12,10 +12,6 @@
 #include <unistd.h>
 #include <errno.h>
 
-#ifdef __CYGWIN__
-#include <windows.h>
-#endif
-
 #include <pwd.h>
 #include <grp.h>
 
@@ -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 484a5f2bec..3831de4408 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 9e3e0bae20..a8ad3fe480 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -13,6 +13,10 @@
 #include <fcntl.h>
 #include <signal.h>
 
+#ifdef __CYGWIN__
+#include <windows.h>
+#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;
+}
+
+
 
 //////////////////////////////////////////////////////////////////////
 
@@ -646,6 +667,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
+}
+
+
+//////////////////////////////////////////////////////////////////////
+
+
 volatile sig_atomic_t _isInterrupted = 0;
 
 void _interrupted()
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 92bdf50d31..fcf995af8a 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 07250ad6ed..f96f28d206 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@