diff --git a/Makefile b/Makefile index d8d4a7cc576..fe2e88a995a 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ makefiles = \ src/nix-instantiate/local.mk \ src/nix-env/local.mk \ src/nix-daemon/local.mk \ + src/nix-collect-garbage/local.mk \ src/download-via-ssh/local.mk \ src/nix-log2xml/local.mk \ src/bsdiff-4.3/local.mk \ diff --git a/scripts/local.mk b/scripts/local.mk index f4c5e8097de..39e1df611c5 100644 --- a/scripts/local.mk +++ b/scripts/local.mk @@ -1,7 +1,6 @@ nix_bin_scripts := \ $(d)/nix-build \ $(d)/nix-channel \ - $(d)/nix-collect-garbage \ $(d)/nix-copy-closure \ $(d)/nix-generate-patches \ $(d)/nix-install-package \ diff --git a/scripts/nix-collect-garbage.in b/scripts/nix-collect-garbage.in deleted file mode 100755 index 55e0ba7a6fa..00000000000 --- a/scripts/nix-collect-garbage.in +++ /dev/null @@ -1,65 +0,0 @@ -#! @perl@ -w @perlFlags@ - -use strict; -use Nix::Config; - -my $profilesDir = "@localstatedir@/nix/profiles"; - - -# Process the command line arguments. -my @args = (); -my $arg; - -my $removeOld = 0; -my $gen; -my $dryRun = 0; - -while ($arg = shift) { - if ($arg eq "--delete-old" || $arg eq "-d") { - $removeOld = 1; - $gen = "old"; - } elsif ($arg eq "--delete-older-than") { - $removeOld = 1; - $gen = shift; - } elsif ($arg eq "--dry-run") { - $dryRun = 1; - } elsif ($arg eq "--help") { - exec "man nix-collect-garbage" or die; - } else { - push @args, $arg; - } -} - - -# If `-d' was specified, remove all old generations of all profiles. -# Of course, this makes rollbacks to before this point in time -# impossible. - -sub removeOldGenerations; -sub removeOldGenerations { - my $dir = shift; - - my $dh; - opendir $dh, $dir or die; - - foreach my $name (sort (readdir $dh)) { - next if $name eq "." || $name eq ".."; - $name = $dir . "/" . $name; - if (-l $name && (readlink($name) =~ /link/)) { - print STDERR "removing old generations of profile $name\n"; - - system("$Nix::Config::binDir/nix-env", "-p", $name, "--delete-generations", $gen, $dryRun ? "--dry-run" : ()); - } - elsif (! -l $name && -d $name) { - removeOldGenerations $name; - } - } - - closedir $dh or die; -} - -removeOldGenerations $profilesDir if $removeOld; - - -# Run the actual garbage collector. -exec "$Nix::Config::binDir/nix-store", "--gc", @args unless $dryRun; diff --git a/src/nix-collect-garbage/local.mk b/src/nix-collect-garbage/local.mk new file mode 100644 index 00000000000..02d14cf6219 --- /dev/null +++ b/src/nix-collect-garbage/local.mk @@ -0,0 +1,7 @@ +programs += nix-collect-garbage + +nix-collect-garbage_DIR := $(d) + +nix-collect-garbage_SOURCES := $(d)/nix-collect-garbage.cc + +nix-collect-garbage_LIBS = libmain libstore libutil libformat diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc new file mode 100644 index 00000000000..568a1fa7daa --- /dev/null +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -0,0 +1,87 @@ +#include "hash.hh" +#include "shared.hh" +#include "globals.hh" + +#include + +using namespace nix; + +std::string gen = ""; +bool dryRun = false; + +void runProgramSimple(Path program, const Strings & args) +{ + checkInterrupt(); + + /* Fork. */ + Pid pid = startProcess([&]() { + Strings args_(args); + args_.push_front(program); + auto cargs = stringsToCharPtrs(args_); + + execv(program.c_str(), (char * *) &cargs[0]); + + throw SysError(format("executing ‘%1%’") % program); + }); + + pid.wait(true); +} + + +/* If `-d' was specified, remove all old generations of all profiles. + * Of course, this makes rollbacks to before this point in time + * impossible. */ + +void removeOldGenerations(std::string dir) +{ + for (auto & i : readDirectory(dir)) { + checkInterrupt(); + + auto path = dir + "/" + i.name; + auto type = getFileType(path); + + if (type == DT_LNK) { + auto link = readLink(path); + if (link.find("link") != string::npos) { + printMsg(lvlInfo, format("removing old generations of profile %1%") % path); + + runProgramSimple(settings.nixBinDir + "/nix-env", Strings{"-p", path, "--delete-generations", gen, dryRun ? "--dry-run" : ""}); + } + } else if (type == DT_DIR) { + removeOldGenerations(path); + } + } +} + +int main(int argc, char * * argv) +{ + bool removeOld = false; + Strings extraArgs; + + return handleExceptions(argv[0], [&]() { + initNix(); + + parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) { + if (*arg == "--help") + showManPage("nix-collect-garbage"); + else if (*arg == "--version") + printVersion("nix-collect-garbage"); + else if (*arg == "--delete-old" || *arg == "-d") removeOld = true; + else if (*arg == "--delete-older-than") { + removeOld = true; + gen = getArg(*arg, arg, end); + } + else if (*arg == "--dry-run") dryRun = true; + else + extraArgs.push_back(*arg); + return true; + }); + + auto profilesDir = settings.nixStateDir + "/profiles"; + if (removeOld) removeOldGenerations(profilesDir); + + // Run the actual garbage collector. + if (!dryRun) runProgramSimple(settings.nixBinDir + "/nix-store", Strings{"--gc"}); + }); +} +