diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rwxr-xr-x | scripts/find-runtime-roots.pl.in | 79 | ||||
-rw-r--r-- | scripts/local.mk | 2 | ||||
-rw-r--r-- | src/libstore/gc.cc | 116 |
4 files changed, 111 insertions, 89 deletions
diff --git a/.gitignore b/.gitignore index 178783d22d36..a524e9b6a29b 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ Makefile.config # /src/libstore/ /src/libstore/schema.sql.hh +/src/libstore/sandbox-defaults.sb # /src/nix-env/ /src/nix-env/nix-env @@ -94,6 +95,8 @@ Makefile.config /misc/systemd/nix-daemon.socket /misc/upstart/nix-daemon.conf +inst/ + *.a *.o *.so diff --git a/scripts/find-runtime-roots.pl.in b/scripts/find-runtime-roots.pl.in deleted file mode 100755 index e1a2dde556b6..000000000000 --- a/scripts/find-runtime-roots.pl.in +++ /dev/null @@ -1,79 +0,0 @@ -#! @perl@ -w @perlFlags@ - -use strict; -use Nix::Utils; -use Nix::Config; - - -sub readProc { - return unless -d "/proc"; - - opendir DIR, "/proc" or return; - - foreach my $name (readdir DIR) { - next unless $name =~ /^\d+$/; - - my $process = "/proc/$name"; - - #print STDERR "=== $process\n"; - - my $target; - print "$target\n" if $target = readlink "$process/exe"; - print "$target\n" if $target = readlink "$process/cwd"; - - if (opendir FDS, "$process/fd") { - foreach my $name (readdir FDS) { - $target = readlink "$process/fd/$name"; - print "$target\n" if $target && substr($target, 0, 1) eq "/"; - } - closedir FDS; - } - - if (open MAP, "<$process/maps") { - while (<MAP>) { - next unless /^ \s* \S+ \s+ \S+ \s+ \S+ \s+ \S+ \s+ \S+ \s+ (\/\S+) \s* $/x; - print "$1\n"; - } - close MAP; - } - - # Get all store paths that appear in the environment of this process. - eval { - my $env = Nix::Utils::readFile "$process/environ"; - my @matches = $env =~ /\Q$Nix::Config::storeDir\E\/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*/g; - print "$_\n" foreach @matches; - } - } - - closedir DIR; -} - - -sub lsof { - return unless open LSOF, "lsof -n -w -F n 2> /dev/null |"; - - while (<LSOF>) { - next unless /^n (\/ .*)$/x; - print $1, "\n"; - } - - close LSOF; -} - - -readProc; -lsof; - - -sub printFile { - my ($fn) = @_; - if (-e $fn) { - print Nix::Utils::readFile($fn), "\n"; - } -} - - -# This is rather NixOS-specific, so it probably shouldn't be here. -printFile "/proc/sys/kernel/modprobe"; -printFile "/proc/sys/kernel/fbsplash"; -printFile "/proc/sys/kernel/poweroff_cmd"; diff --git a/scripts/local.mk b/scripts/local.mk index 726f23f39135..edaf44cc492d 100644 --- a/scripts/local.mk +++ b/scripts/local.mk @@ -9,7 +9,6 @@ bin-scripts += $(nix_bin_scripts) nix_noinst_scripts := \ $(d)/build-remote.pl \ - $(d)/find-runtime-roots.pl \ $(d)/nix-http-export.cgi \ $(d)/nix-profile.sh \ $(d)/nix-reduce-build @@ -23,7 +22,6 @@ noinst-scripts += $(nix_noinst_scripts) profiledir = $(sysconfdir)/profile.d $(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644)) -$(eval $(call install-program-in, $(d)/find-runtime-roots.pl, $(libexecdir)/nix)) $(eval $(call install-program-in, $(d)/build-remote.pl, $(libexecdir)/nix)) ifeq ($(OS), Darwin) $(eval $(call install-program-in, $(d)/resolve-system-dependencies.pl, $(libexecdir)/nix)) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 9324508cc87c..2eab7de0d8bf 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -5,13 +5,14 @@ #include <functional> #include <queue> #include <algorithm> +#include <regex> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> - +#include <climits> namespace nix { @@ -330,18 +331,117 @@ Roots LocalStore::findRoots() } -void LocalStore::findRuntimeRoots(PathSet & roots) +static void readProcLink(const string & file, StringSet & paths) { - Path rootFinder = getEnv("NIX_ROOT_FINDER", - settings.nixLibexecDir + "/nix/find-runtime-roots.pl"); + /* 64 is the starting buffer size gnu readlink uses... */ + auto bufsiz = ssize_t{64}; +try_again: + char buf[bufsiz]; + auto res = readlink(file.c_str(), buf, bufsiz); + if (res == -1) { + if (errno == ENOENT || errno == EACCES) + return; + throw SysError("reading symlink"); + } + if (res == bufsiz) { + if (SSIZE_MAX / 2 < bufsiz) + throw Error("stupidly long symlink"); + bufsiz *= 2; + goto try_again; + } + if (res > 0 && buf[0] == '/') + paths.emplace(static_cast<char *>(buf), res); + return; +} - if (rootFinder.empty()) return; +static string quoteRegexChars(const string & raw) +{ + static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])"); + return std::regex_replace(raw, specialRegex, R"(\$&)"); +} - debug(format("executing ‘%1%’ to find additional roots") % rootFinder); +static void readFileRoots(const char * path, StringSet & paths) +{ + try { + paths.emplace(readFile(path)); + } catch (SysError & e) { + if (e.errNo != ENOENT && e.errNo != EACCES) + throw; + } +} - string result = runProgram(rootFinder); +void LocalStore::findRuntimeRoots(PathSet & roots) +{ + StringSet paths; + auto procDir = AutoCloseDir{opendir("/proc")}; + if (procDir) { + struct dirent * ent; + auto digitsRegex = std::regex(R"(^\d+$)"); + auto mapRegex = std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); + auto storePathRegex = std::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); + while (errno = 0, ent = readdir(procDir)) { + checkInterrupt(); + if (std::regex_match(ent->d_name, digitsRegex)) { + readProcLink((format("/proc/%1%/exe") % ent->d_name).str(), paths); + readProcLink((format("/proc/%1%/cwd") % ent->d_name).str(), paths); + + auto fdStr = (format("/proc/%1%/fd") % ent->d_name).str(); + auto fdDir = AutoCloseDir(opendir(fdStr.c_str())); + if (!fdDir) { + if (errno == ENOENT || errno == EACCES) + continue; + throw SysError(format("opening %1%") % fdStr); + } + struct dirent * fd_ent; + while (errno = 0, fd_ent = readdir(fdDir)) { + if (fd_ent->d_name[0] != '.') { + readProcLink((format("%1%/%2%") % fdStr % fd_ent->d_name).str(), paths); + } + } + if (errno) + throw SysError(format("iterating /proc/%1%/fd") % ent->d_name); + fdDir.close(); + + auto mapLines = + tokenizeString<std::vector<string>>(readFile((format("/proc/%1%/maps") % ent->d_name).str(), true), "\n"); + for (const auto& line : mapLines) { + auto match = std::smatch{}; + if (std::regex_match(line, match, mapRegex)) + paths.emplace(match[1]); + } + + try { + auto envString = readFile((format("/proc/%1%/environ") % ent->d_name).str(), true); + auto env_end = std::sregex_iterator{}; + for (auto i = std::sregex_iterator{envString.begin(), envString.end(), storePathRegex}; i != env_end; ++i) + paths.emplace(i->str()); + } catch (SysError & e) { + if (errno == ENOENT || errno == EACCES) + continue; + throw; + } + } + } + if (errno) + throw SysError("iterating /proc"); + } + + try { + auto lsofRegex = std::regex(R"(^n(/.*)$)"); + auto lsofLines = + tokenizeString<std::vector<string>>(runProgram("lsof", true, { "-n", "-w", "-F", "n" }), "\n"); + for (const auto & line : lsofLines) { + auto match = std::smatch{}; + if (std::regex_match(line, match, lsofRegex)) + paths.emplace(match[1]); + } + } catch (ExecError & e) { + /* lsof not installed, lsof failed */ + } - StringSet paths = tokenizeString<StringSet>(result, "\n"); + readFileRoots("/proc/sys/kernel/modprobe", paths); + readFileRoots("/proc/sys/kernel/fbsplash", paths); + readFileRoots("/proc/sys/kernel/poweroff_cmd", paths); for (auto & i : paths) if (isInStore(i)) { |