Merge branch 'find-runtime-roots-c++'
This commit is contained in:
		
						commit
						ee3032e4de
					
				
					 4 changed files with 110 additions and 88 deletions
				
			
		
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue