about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/nix-collect-garbage.xml11
-rw-r--r--doc/manual/nix-store.xml12
-rwxr-xr-xscripts/nix-collect-garbage.in16
-rw-r--r--src/libstore/gc.cc17
-rw-r--r--src/libstore/gc.hh6
-rw-r--r--src/nix-store/main.cc25
6 files changed, 70 insertions, 17 deletions
diff --git a/doc/manual/nix-collect-garbage.xml b/doc/manual/nix-collect-garbage.xml
index adc6c1e730a7..1de50408e01e 100644
--- a/doc/manual/nix-collect-garbage.xml
+++ b/doc/manual/nix-collect-garbage.xml
@@ -11,6 +11,7 @@
         <arg choice='plain'><option>--print-live</option></arg>
         <arg choice='plain'><option>--print-dead</option></arg>
       </group>
+      <arg><option>--min-age</option> <replaceable>age</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -56,6 +57,16 @@
         </listitem>
       </varlistentry>
       
+      <varlistentry>
+        <term><option>--min-age</option> <replaceable>age</replaceable></term>
+        <listitem>
+          <para>
+            This option corresponds to the <option>--min-age</option>
+            option in <command>nix-store <option>--gc</option></command>.
+          </para>
+        </listitem>
+      </varlistentry>
+      
     </variablelist>
 
   </refsection>
diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml
index febe02fd4ada..522de60d3696 100644
--- a/doc/manual/nix-store.xml
+++ b/doc/manual/nix-store.xml
@@ -147,6 +147,7 @@
           <arg choice='plain'><option>--print-dead</option></arg>
           <arg choice='plain'><option>--delete</option></arg>
         </group>
+        <arg><option>--min-age</option> <replaceable>age</replaceable></arg>
       </cmdsynopsis>
     </refsection>
 
@@ -207,6 +208,17 @@
         Each line should contain exactly one store path.
       </para>
 
+      <para>
+        The option <option>--min-age</option> specifies a minimum time
+        in hours that an unreachable store path must not have been
+        used before it is considered dead.  The default is 0 (consider
+        all unreachable store paths dead).  Whether a store path has
+        been used is determined by looking at its access time
+        (<literal>atime</literal>), so this does not work if the store
+        is located on a file system that has the
+        <literal>noatime</literal> option set.
+      </para>
+
       <warning>
         <para>
           You generally will want to use the command
diff --git a/scripts/nix-collect-garbage.in b/scripts/nix-collect-garbage.in
index 539979cbb6c0..44bcc16bbca0 100755
--- a/scripts/nix-collect-garbage.in
+++ b/scripts/nix-collect-garbage.in
@@ -9,16 +9,24 @@ my $storeDir = "@storedir@";
 my %alive;
 
 my $gcOper = "--delete";
-my $keepSuccessors = 1;
+my $minAge = 0;
 
 my @roots = ();
 
 
 # Parse the command line.
-foreach my $arg (@ARGV) {
+for (my $i = 0; $i < scalar @ARGV; $i++) {
+    my $arg = $ARGV[$i];
     if ($arg eq "--delete" || $arg eq "--print-live" || $arg eq "--print-dead") {
         $gcOper = $arg;
-    } else { die "unknown argument `$arg'" };
+    }
+    elsif ($arg eq "--min-age") {
+        $i++;
+        $minAge = undef;
+        $minAge = $ARGV[$i];
+        die "invalid minimum age" unless defined $minAge && $minAge =~ /^\d*$/;
+    }
+    else { die "unknown argument `$arg'" };
 }
 
 
@@ -69,7 +77,7 @@ findRoots 1, $rootsDir;
 
 
 # Run the collector with the roots we found.
-my $pid = open2(">&1", \*WRITE, "@bindir@/nix-store --gc $gcOper")
+my $pid = open2(">&1", \*WRITE, "@bindir@/nix-store --gc $gcOper --min-age $minAge")
     or die "cannot run `nix-store --gc'";
 
 foreach my $root (@roots) {
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index aed7c2294b04..9af957693f0c 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -2,6 +2,11 @@
 #include "globals.hh"
 
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
 void followLivePaths(Path nePath, PathSet & live)
 {
     /* Just to be sure, canonicalise the path.  It is important to do
@@ -62,16 +67,26 @@ PathSet findLivePaths(const Paths & roots)
 }
 
 
-PathSet findDeadPaths(const PathSet & live)
+PathSet findDeadPaths(const PathSet & live, time_t minAge)
 {
     PathSet dead;
 
     startNest(nest, lvlDebug, "finding dead paths");
 
+    time_t now = time(0);
+
     Strings storeNames = readDirectory(nixStore);
 
     for (Strings::iterator i = storeNames.begin(); i != storeNames.end(); ++i) {
         Path p = canonPath(nixStore + "/" + *i);
+
+        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;
+        }
+        
         if (live.find(p) == live.end()) {
             debug(format("dead path `%1%'") % p);
             dead.insert(p);
diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh
index 997057ba9b7b..1ada419da439 100644
--- a/src/libstore/gc.hh
+++ b/src/libstore/gc.hh
@@ -17,8 +17,10 @@ PathSet findLivePaths(const Paths & roots);
 
 /* Given a set of "live" store paths, determine the set of "dead"
    store paths (which are simply all store paths that are not in the
-   live set). */
-PathSet findDeadPaths(const PathSet & live);
+   live set).  The value `minAge' specifies the minimum age in seconds
+   for an unreachable file to be considered dead (0 meaning that any
+   unreachable file is dead). */
+PathSet findDeadPaths(const PathSet & live, time_t minAge);
 
 
 #endif /* !__GC_H */
diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc
index 7bc8565d293d..e9948c7cf8ac 100644
--- a/src/nix-store/main.cc
+++ b/src/nix-store/main.cc
@@ -212,17 +212,22 @@ static void opIsValid(Strings opFlags, Strings opArgs)
 
 static void opGC(Strings opFlags, Strings opArgs)
 {
-    if (opFlags.size() != 1) throw UsageError("missing flag");
-    if (!opArgs.empty())
-        throw UsageError("no arguments expected");
-
     /* Do what? */
-    string flag = opFlags.front();
     enum { soPrintLive, soPrintDead, soDelete } subOp;
-    if (flag == "--print-live") subOp = soPrintLive;
-    else if (flag == "--print-dead") subOp = soPrintDead;
-    else if (flag == "--delete") subOp = soDelete;
-    else throw UsageError(format("bad sub-operation `%1%' in GC") % flag);
+    time_t minAge = 0;
+    for (Strings::iterator i = opFlags.begin();
+         i != opFlags.end(); ++i)
+        if (*i == "--print-live") subOp = soPrintLive;
+        else if (*i == "--print-dead") subOp = soPrintDead;
+        else if (*i == "--delete") subOp = soDelete;
+        else if (*i == "--min-age") {
+            if (opArgs.size() == 0)
+                throw UsageError("`--min-age' requires an argument");
+            istringstream st(opArgs.front());
+            st >> minAge;
+            if (!st) throw Error("number expected");
+        }
+        else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
         
     Paths roots;
     while (1) {
@@ -240,7 +245,7 @@ static void opGC(Strings opFlags, Strings opArgs)
         return;
     }
 
-    PathSet dead = findDeadPaths(live);
+    PathSet dead = findDeadPaths(live, minAge * 3600);
 
     if (subOp == soPrintDead) {
         for (PathSet::iterator i = dead.begin(); i != dead.end(); ++i)