From c15f544356dfebf6d08887e73fa156d4e70e2bbc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 20 Jul 2006 12:17:25 +0000 Subject: [PATCH] * Call find-runtime-roots.pl from the garbage collector to prevent running applications etc. from being garbage collected. --- doc/manual/release-notes.xml | 3 ++ src/libstore/build.cc | 21 +++------ src/libstore/gc.cc | 31 +++++++++++++ src/libutil/util.cc | 84 ++++++++++++++++++++++++++++++++++++ src/libutil/util.hh | 14 ++++++ tests/common.sh.in | 1 + 6 files changed, 138 insertions(+), 16 deletions(-) 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@