forked from lix-project/lix
Merge branch 'find-runtime-roots-c++'
This commit is contained in:
commit
ee3032e4de
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -60,6 +60,7 @@ Makefile.config
|
||||||
|
|
||||||
# /src/libstore/
|
# /src/libstore/
|
||||||
/src/libstore/schema.sql.hh
|
/src/libstore/schema.sql.hh
|
||||||
|
/src/libstore/sandbox-defaults.sb
|
||||||
|
|
||||||
# /src/nix-env/
|
# /src/nix-env/
|
||||||
/src/nix-env/nix-env
|
/src/nix-env/nix-env
|
||||||
|
@ -94,6 +95,8 @@ Makefile.config
|
||||||
/misc/systemd/nix-daemon.socket
|
/misc/systemd/nix-daemon.socket
|
||||||
/misc/upstart/nix-daemon.conf
|
/misc/upstart/nix-daemon.conf
|
||||||
|
|
||||||
|
inst/
|
||||||
|
|
||||||
*.a
|
*.a
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
|
|
|
@ -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";
|
|
|
@ -9,7 +9,6 @@ bin-scripts += $(nix_bin_scripts)
|
||||||
|
|
||||||
nix_noinst_scripts := \
|
nix_noinst_scripts := \
|
||||||
$(d)/build-remote.pl \
|
$(d)/build-remote.pl \
|
||||||
$(d)/find-runtime-roots.pl \
|
|
||||||
$(d)/nix-http-export.cgi \
|
$(d)/nix-http-export.cgi \
|
||||||
$(d)/nix-profile.sh \
|
$(d)/nix-profile.sh \
|
||||||
$(d)/nix-reduce-build
|
$(d)/nix-reduce-build
|
||||||
|
@ -23,7 +22,6 @@ noinst-scripts += $(nix_noinst_scripts)
|
||||||
profiledir = $(sysconfdir)/profile.d
|
profiledir = $(sysconfdir)/profile.d
|
||||||
|
|
||||||
$(eval $(call install-file-as, $(d)/nix-profile.sh, $(profiledir)/nix.sh, 0644))
|
$(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))
|
$(eval $(call install-program-in, $(d)/build-remote.pl, $(libexecdir)/nix))
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
$(eval $(call install-program-in, $(d)/resolve-system-dependencies.pl, $(libexecdir)/nix))
|
$(eval $(call install-program-in, $(d)/resolve-system-dependencies.pl, $(libexecdir)/nix))
|
||||||
|
|
|
@ -5,13 +5,14 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -330,18 +331,117 @@ Roots LocalStore::findRoots()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void readProcLink(const string & file, StringSet & paths)
|
||||||
|
{
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string quoteRegexChars(const string & raw)
|
||||||
|
{
|
||||||
|
static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])");
|
||||||
|
return std::regex_replace(raw, specialRegex, R"(\$&)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readFileRoots(const char * path, StringSet & paths)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
paths.emplace(readFile(path));
|
||||||
|
} catch (SysError & e) {
|
||||||
|
if (e.errNo != ENOENT && e.errNo != EACCES)
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LocalStore::findRuntimeRoots(PathSet & roots)
|
void LocalStore::findRuntimeRoots(PathSet & roots)
|
||||||
{
|
{
|
||||||
Path rootFinder = getEnv("NIX_ROOT_FINDER",
|
StringSet paths;
|
||||||
settings.nixLibexecDir + "/nix/find-runtime-roots.pl");
|
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);
|
||||||
|
|
||||||
if (rootFinder.empty()) return;
|
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();
|
||||||
|
|
||||||
debug(format("executing ‘%1%’ to find additional roots") % rootFinder);
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
string result = runProgram(rootFinder);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
StringSet paths = tokenizeString<StringSet>(result, "\n");
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
readFileRoots("/proc/sys/kernel/modprobe", paths);
|
||||||
|
readFileRoots("/proc/sys/kernel/fbsplash", paths);
|
||||||
|
readFileRoots("/proc/sys/kernel/poweroff_cmd", paths);
|
||||||
|
|
||||||
for (auto & i : paths)
|
for (auto & i : paths)
|
||||||
if (isInStore(i)) {
|
if (isInStore(i)) {
|
||||||
|
|
Loading…
Reference in a new issue