2004-08-25 11:43:49 +00:00
|
|
|
#include "globals.hh"
|
2005-01-19 16:39:47 +00:00
|
|
|
#include "gc.hh"
|
2005-01-27 15:21:29 +00:00
|
|
|
#include "build.hh"
|
2004-08-25 11:43:49 +00:00
|
|
|
|
2004-08-25 16:54:08 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
2005-01-27 15:21:29 +00:00
|
|
|
void collectGarbage(const PathSet & roots, GCAction action,
|
|
|
|
PathSet & result)
|
|
|
|
{
|
|
|
|
result.clear();
|
|
|
|
|
|
|
|
/* !!! TODO: Acquire an exclusive lock on the gcroots directory.
|
|
|
|
This prevents the set of live paths from increasing after this
|
|
|
|
point. */
|
|
|
|
|
|
|
|
/* Determine the live paths which is just the closure of the
|
|
|
|
roots under the `references' relation. */
|
|
|
|
PathSet livePaths;
|
|
|
|
for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
|
|
|
|
computeFSClosure(canonPath(*i), livePaths);
|
|
|
|
|
|
|
|
if (action == gcReturnLive) {
|
|
|
|
result = livePaths;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* !!! TODO: Try to acquire (without blocking) exclusive locks on
|
|
|
|
the files in the `pending' directory. Delete all files for
|
|
|
|
which we managed to acquire such a lock (since if we could get
|
|
|
|
such a lock, that means that the process that owned the file
|
|
|
|
has died). */
|
|
|
|
|
|
|
|
/* !!! TODO: Acquire shared locks on all files in the pending
|
|
|
|
directories. This prevents the set of pending paths from
|
|
|
|
increasing while we are garbage-collecting. Read the set of
|
|
|
|
pending paths from those files. */
|
|
|
|
|
|
|
|
/* Read the Nix store directory to find all currently existing
|
|
|
|
paths. */
|
|
|
|
Strings storeNames = readDirectory(nixStore);
|
|
|
|
|
|
|
|
for (Strings::iterator i = storeNames.begin(); i != storeNames.end(); ++i) {
|
|
|
|
Path path = canonPath(nixStore + "/" + *i);
|
|
|
|
|
|
|
|
if (livePaths.find(path) != livePaths.end()) {
|
|
|
|
debug(format("live path `%1%'") % path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(format("dead path `%1%'") % path);
|
|
|
|
result.insert(path);
|
|
|
|
|
|
|
|
if (action == gcDeleteDead) {
|
|
|
|
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
|
|
|
deleteFromStore(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-01-19 11:16:11 +00:00
|
|
|
#if 0
|
2004-08-25 11:43:49 +00:00
|
|
|
void followLivePaths(Path nePath, PathSet & live)
|
|
|
|
{
|
|
|
|
/* Just to be sure, canonicalise the path. It is important to do
|
|
|
|
this here and in findDeadPath() to ensure that a live path is
|
|
|
|
not mistaken for a dead path due to some non-canonical
|
|
|
|
representation. */
|
|
|
|
nePath = canonPath(nePath);
|
|
|
|
|
|
|
|
if (live.find(nePath) != live.end()) return;
|
|
|
|
live.insert(nePath);
|
|
|
|
|
|
|
|
startNest(nest, lvlDebug, format("following `%1%'") % nePath);
|
|
|
|
assertStorePath(nePath);
|
|
|
|
|
|
|
|
if (isValidPath(nePath)) {
|
|
|
|
|
|
|
|
/* !!! should make sure that no substitutes are used */
|
|
|
|
StoreExpr ne = storeExprFromPath(nePath);
|
|
|
|
|
|
|
|
/* !!! painfully similar to requisitesWorker() */
|
|
|
|
if (ne.type == StoreExpr::neClosure)
|
|
|
|
for (ClosureElems::iterator i = ne.closure.elems.begin();
|
|
|
|
i != ne.closure.elems.end(); ++i)
|
|
|
|
{
|
|
|
|
Path p = canonPath(i->first);
|
|
|
|
if (live.find(p) == live.end()) {
|
|
|
|
debug(format("found live `%1%'") % p);
|
|
|
|
assertStorePath(p);
|
|
|
|
live.insert(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (ne.type == StoreExpr::neDerivation)
|
|
|
|
for (PathSet::iterator i = ne.derivation.inputs.begin();
|
|
|
|
i != ne.derivation.inputs.end(); ++i)
|
|
|
|
followLivePaths(*i, live);
|
|
|
|
|
|
|
|
else abort();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Path nfPath;
|
|
|
|
if (querySuccessor(nePath, nfPath))
|
|
|
|
followLivePaths(nfPath, live);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PathSet findLivePaths(const Paths & roots)
|
|
|
|
{
|
|
|
|
PathSet live;
|
|
|
|
|
|
|
|
startNest(nest, lvlDebug, "finding live paths");
|
|
|
|
|
|
|
|
for (Paths::const_iterator i = roots.begin(); i != roots.end(); ++i)
|
|
|
|
followLivePaths(*i, live);
|
|
|
|
|
|
|
|
return live;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-25 16:54:08 +00:00
|
|
|
PathSet findDeadPaths(const PathSet & live, time_t minAge)
|
2004-08-25 11:43:49 +00:00
|
|
|
{
|
|
|
|
PathSet dead;
|
|
|
|
|
|
|
|
startNest(nest, lvlDebug, "finding dead paths");
|
|
|
|
|
2004-08-25 16:54:08 +00:00
|
|
|
time_t now = time(0);
|
|
|
|
|
2004-08-25 11:43:49 +00:00
|
|
|
Strings storeNames = readDirectory(nixStore);
|
|
|
|
|
|
|
|
for (Strings::iterator i = storeNames.begin(); i != storeNames.end(); ++i) {
|
|
|
|
Path p = canonPath(nixStore + "/" + *i);
|
2004-08-25 16:54:08 +00:00
|
|
|
|
|
|
|
if (minAge > 0) {
|
|
|
|
struct stat st;
|
|
|
|
if (lstat(p.c_str(), &st) != 0)
|
|
|
|
throw SysError(format("obtaining information about `%1%'") % p);
|
|
|
|
if (st.st_atime + minAge >= now) continue;
|
|
|
|
}
|
|
|
|
|
2004-08-25 11:43:49 +00:00
|
|
|
if (live.find(p) == live.end()) {
|
|
|
|
debug(format("dead path `%1%'") % p);
|
|
|
|
dead.insert(p);
|
|
|
|
} else
|
|
|
|
debug(format("live path `%1%'") % p);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dead;
|
|
|
|
}
|
2005-01-19 11:16:11 +00:00
|
|
|
#endif
|