1 : #include "globals.hh"
2 : #include "misc.hh"
3 : #include "pathlocks.hh"
4 : #include "local-store.hh"
5 :
6 : #include <boost/shared_ptr.hpp>
7 :
8 : #include <functional>
9 : #include <queue>
10 :
11 : #include <sys/types.h>
12 : #include <sys/stat.h>
13 : #include <errno.h>
14 : #include <fcntl.h>
15 : #include <unistd.h>
16 :
17 : #ifdef __CYGWIN__
18 : #include <windows.h>
19 : #include <sys/cygwin.h>
20 : #endif
21 :
22 :
23 : namespace nix {
24 :
25 :
26 1704 : static string gcLockName = "gc.lock";
27 1704 : static string tempRootsDir = "temproots";
28 1704 : static string gcRootsDir = "gcroots";
29 :
30 : static const int defaultGcLevel = 1000;
31 :
32 :
33 : /* Acquire the global GC lock. This is used to prevent new Nix
34 : processes from starting after the temporary root files have been
35 : read. To be precise: when they try to create a new temporary root
36 : file, they will block until the garbage collector has finished /
37 : yielded the GC lock. */
38 227 : static int openGCLock(LockType lockType)
39 : {
40 : Path fnGCLock = (format("%1%/%2%")
41 227 : % nixStateDir % gcLockName).str();
42 :
43 229 : debug(format("acquiring global GC lock `%1%'") % fnGCLock);
44 :
45 227 : AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
46 227 : if (fdGCLock == -1)
47 0 : throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
48 :
49 227 : if (!lockFile(fdGCLock, lockType, false)) {
50 0 : printMsg(lvlError, format("waiting for the big garbage collector lock..."));
51 0 : lockFile(fdGCLock, lockType, true);
52 : }
53 :
54 : /* !!! Restrict read permission on the GC root. Otherwise any
55 : process that can open the file for reading can DoS the
56 : collector. */
57 :
58 227 : return fdGCLock.borrow();
59 : }
60 :
61 :
62 79 : void createSymlink(const Path & link, const Path & target, bool careful)
63 : {
64 : /* Create directories up to `gcRoot'. */
65 79 : createDirs(dirOf(link));
66 :
67 : /* !!! shouldn't removing and creating the symlink be atomic? */
68 :
69 : /* Remove the old symlink. */
70 79 : if (pathExists(link)) {
71 18 : if (careful && (!isLink(link) || !isInStore(readLink(link))))
72 0 : throw Error(format("cannot create symlink `%1%'; already exists") % link);
73 18 : unlink(link.c_str());
74 : }
75 :
76 : /* And create the new one. */
77 79 : if (symlink(target.c_str(), link.c_str()) == -1)
78 : throw SysError(format("symlinking `%1%' to `%2%'")
79 0 : % link % target);
80 79 : }
81 :
82 :
83 61 : void LocalStore::syncWithGC()
84 : {
85 61 : AutoCloseFD fdGCLock = openGCLock(ltRead);
86 61 : }
87 :
88 :
89 18 : void LocalStore::addIndirectRoot(const Path & path)
90 : {
91 18 : string hash = printHash32(hashString(htSHA1, path));
92 : Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
93 18 : % nixStateDir % gcRootsDir % hash).str());
94 18 : createSymlink(realRoot, path, false);
95 18 : }
96 :
97 :
98 : Path addPermRoot(const Path & _storePath, const Path & _gcRoot,
99 61 : bool indirect, bool allowOutsideRootsDir)
100 : {
101 61 : Path storePath(canonPath(_storePath));
102 61 : Path gcRoot(canonPath(_gcRoot));
103 61 : assertStorePath(storePath);
104 :
105 61 : if (isInStore(gcRoot))
106 : throw Error(format(
107 : "creating a garbage collector root (%1%) in the Nix store is forbidden "
108 0 : "(are you running nix-build inside the store?)") % gcRoot);
109 :
110 61 : if (indirect) {
111 18 : createSymlink(gcRoot, storePath, true);
112 18 : store->addIndirectRoot(gcRoot);
113 : }
114 :
115 : else {
116 43 : if (!allowOutsideRootsDir) {
117 0 : Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
118 :
119 0 : if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
120 : throw Error(format(
121 : "path `%1%' is not a valid garbage collector root; "
122 : "it's not in the directory `%2%'")
123 0 : % gcRoot % rootsDir);
124 : }
125 :
126 43 : createSymlink(gcRoot, storePath, false);
127 : }
128 :
129 : /* Check that the root can be found by the garbage collector.
130 : !!! This can be very slow on machines that have many roots.
131 : Instead of reading all the roots, it would be more efficient to
132 : check if the root is in a directory in or linked from the
133 : gcroots directory. */
134 61 : if (queryBoolSetting("gc-check-reachability", true)) {
135 61 : Roots roots = store->findRoots();
136 61 : if (roots.find(gcRoot) == roots.end())
137 0 : printMsg(lvlError,
138 : format(
139 : "warning: `%1%' is not in a directory where the garbage collector looks for roots; "
140 : "therefore, `%2%' might be removed by the garbage collector")
141 : % gcRoot % storePath);
142 : }
143 :
144 : /* Grab the global GC root, causing us to block while a GC is in
145 : progress. This prevents the set of permanent roots from
146 : increasing while a GC is in progress. */
147 61 : store->syncWithGC();
148 :
149 61 : return gcRoot;
150 : }
151 :
152 :
153 : /* The file to which we write our temporary roots. */
154 1151 : static Path fnTempRoots;
155 1151 : static AutoCloseFD fdTempRoots;
156 :
157 :
158 1060 : void LocalStore::addTempRoot(const Path & path)
159 : {
160 : /* Create the temporary roots file for this process. */
161 1060 : if (fdTempRoots == -1) {
162 :
163 0 : while (1) {
164 145 : Path dir = (format("%1%/%2%") % nixStateDir % tempRootsDir).str();
165 290 : createDirs(dir);
166 :
167 : fnTempRoots = (format("%1%/%2%")
168 145 : % dir % getpid()).str();
169 :
170 145 : AutoCloseFD fdGCLock = openGCLock(ltRead);
171 :
172 145 : if (pathExists(fnTempRoots))
173 : /* It *must* be stale, since there can be no two
174 : processes with the same pid. */
175 0 : deletePath(fnTempRoots);
176 :
177 145 : fdTempRoots = openLockFile(fnTempRoots, true);
178 :
179 145 : fdGCLock.close();
180 :
181 : /* Note that on Cygwin a lot of the following complexity
182 : is unnecessary, since we cannot delete open lock
183 : files. If we have the lock file open, then it's valid;
184 : if we can delete it, then it wasn't in use any more.
185 :
186 : Also note that on Cygwin we cannot "upgrade" a lock
187 : from a read lock to a write lock. */
188 :
189 : #ifndef __CYGWIN__
190 145 : debug(format("acquiring read lock on `%1%'") % fnTempRoots);
191 145 : lockFile(fdTempRoots, ltRead, true);
192 :
193 : /* Check whether the garbage collector didn't get in our
194 : way. */
195 : struct stat st;
196 145 : if (fstat(fdTempRoots, &st) == -1)
197 0 : throw SysError(format("statting `%1%'") % fnTempRoots);
198 290 : if (st.st_size == 0) break;
199 :
200 : /* The garbage collector deleted this file before we could
201 : get a lock. (It won't delete the file after we get a
202 : lock.) Try again. */
203 :
204 : #else
205 : break;
206 : #endif
207 : }
208 :
209 : }
210 :
211 : /* Upgrade the lock to a write lock. This will cause us to block
212 : if the garbage collector is holding our lock. */
213 1060 : debug(format("acquiring write lock on `%1%'") % fnTempRoots);
214 1060 : lockFile(fdTempRoots, ltWrite, true);
215 :
216 1060 : string s = path + '\0';
217 1060 : writeFull(fdTempRoots, (const unsigned char *) s.c_str(), s.size());
218 :
219 : #ifndef __CYGWIN__
220 : /* Downgrade to a read lock. */
221 1060 : debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
222 1060 : lockFile(fdTempRoots, ltRead, true);
223 : #else
224 : debug(format("releasing write lock on `%1%'") % fnTempRoots);
225 : lockFile(fdTempRoots, ltNone, true);
226 : #endif
227 1060 : }
228 :
229 :
230 639 : void removeTempRoots()
231 : {
232 639 : if (fdTempRoots != -1) {
233 145 : fdTempRoots.close();
234 145 : unlink(fnTempRoots.c_str());
235 : }
236 639 : }
237 :
238 :
239 : typedef boost::shared_ptr<AutoCloseFD> FDPtr;
240 : typedef list<FDPtr> FDs;
241 :
242 :
243 19 : static void readTempRoots(PathSet & tempRoots, FDs & fds)
244 : {
245 : /* Read the `temproots' directory for per-process temporary root
246 : files. */
247 : Strings tempRootFiles = readDirectory(
248 19 : (format("%1%/%2%") % nixStateDir % tempRootsDir).str());
249 :
250 40 : for (Strings::iterator i = tempRootFiles.begin();
251 : i != tempRootFiles.end(); ++i)
252 : {
253 1 : Path path = (format("%1%/%2%/%3%") % nixStateDir % tempRootsDir % *i).str();
254 :
255 1 : debug(format("reading temporary root file `%1%'") % path);
256 :
257 : #ifdef __CYGWIN__
258 : /* On Cygwin we just try to delete the lock file. */
259 : char win32Path[MAX_PATH];
260 : cygwin_conv_to_full_win32_path(path.c_str(), win32Path);
261 : if (DeleteFile(win32Path)) {
262 : printMsg(lvlError, format("removed stale temporary roots file `%1%'")
263 : % path);
264 : continue;
265 : } else
266 : debug(format("delete of `%1%' failed: %2%") % path % GetLastError());
267 : #endif
268 :
269 1 : FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
270 1 : if (*fd == -1) {
271 : /* It's okay if the file has disappeared. */
272 0 : if (errno == ENOENT) continue;
273 0 : throw SysError(format("opening temporary roots file `%1%'") % path);
274 : }
275 :
276 : /* This should work, but doesn't, for some reason. */
277 : //FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
278 : //if (*fd == -1) continue;
279 :
280 : #ifndef __CYGWIN__
281 : /* Try to acquire a write lock without blocking. This can
282 : only succeed if the owning process has died. In that case
283 : we don't care about its temporary roots. */
284 1 : if (lockFile(*fd, ltWrite, false)) {
285 0 : printMsg(lvlError, format("removing stale temporary roots file `%1%'")
286 : % path);
287 0 : unlink(path.c_str());
288 0 : writeFull(*fd, (const unsigned char *) "d", 1);
289 : continue;
290 : }
291 : #endif
292 :
293 : /* Acquire a read lock. This will prevent the owning process
294 : from upgrading to a write lock, therefore it will block in
295 : addTempRoot(). */
296 1 : debug(format("waiting for read lock on `%1%'") % path);
297 1 : lockFile(*fd, ltRead, true);
298 :
299 : /* Read the entire file. */
300 1 : string contents = readFile(*fd);
301 :
302 : /* Extract the roots. */
303 1 : string::size_type pos = 0, end;
304 :
305 10 : while ((end = contents.find((char) 0, pos)) != string::npos) {
306 9 : Path root(contents, pos, end - pos);
307 9 : debug(format("got temporary root `%1%'") % root);
308 9 : assertStorePath(root);
309 9 : tempRoots.insert(root);
310 9 : pos = end + 1;
311 : }
312 :
313 1 : fds.push_back(fd); /* keep open */
314 19 : }
315 19 : }
316 :
317 :
318 : static void findRoots(const Path & path, bool recurseSymlinks,
319 908 : bool deleteStale, Roots & roots)
320 : {
321 : try {
322 :
323 : struct stat st;
324 908 : if (lstat(path.c_str(), &st) == -1)
325 0 : throw SysError(format("statting `%1%'") % path);
326 :
327 908 : printMsg(lvlVomit, format("looking at `%1%'") % path);
328 :
329 908 : if (S_ISDIR(st.st_mode)) {
330 217 : Strings names = readDirectory(path);
331 869 : for (Strings::iterator i = names.begin(); i != names.end(); ++i)
332 869 : findRoots(path + "/" + *i, recurseSymlinks, deleteStale, roots);
333 : }
334 :
335 691 : else if (S_ISLNK(st.st_mode)) {
336 648 : Path target = absPath(readLink(path), dirOf(path));
337 :
338 648 : if (isInStore(target)) {
339 400 : debug(format("found root `%1%' in `%2%'")
340 : % target % path);
341 400 : Path storePath = toStorePath(target);
342 400 : if (store->isValidPath(storePath))
343 295 : roots[path] = storePath;
344 : else
345 105 : printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'")
346 : % path % storePath);
347 : }
348 :
349 248 : else if (recurseSymlinks) {
350 185 : if (pathExists(target))
351 174 : findRoots(target, false, deleteStale, roots);
352 11 : else if (deleteStale) {
353 3 : printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
354 : /* Note that we only delete when recursing, i.e.,
355 : when we are still in the `gcroots' tree. We
356 : never delete stuff outside that tree. */
357 3 : unlink(path.c_str());
358 : }
359 648 : }
360 : }
361 :
362 : }
363 :
364 0 : catch (SysError & e) {
365 : /* We only ignore permanent failures. */
366 0 : if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
367 0 : printMsg(lvlInfo, format("cannot read potential root `%1%'") % path);
368 : else
369 0 : throw;
370 : }
371 908 : }
372 :
373 :
374 82 : static Roots findRoots(bool deleteStale)
375 : {
376 82 : Roots roots;
377 82 : Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str());
378 82 : findRoots(rootsDir, true, deleteStale, roots);
379 82 : return roots;
380 : }
381 :
382 :
383 61 : Roots LocalStore::findRoots()
384 : {
385 61 : return nix::findRoots(false);
386 : }
387 :
388 :
389 21 : static void addAdditionalRoots(PathSet & roots)
390 : {
391 : Path rootFinder = getEnv("NIX_ROOT_FINDER",
392 21 : nixLibexecDir + "/nix/find-runtime-roots.pl");
393 :
394 62 : if (rootFinder.empty()) return;
395 :
396 1 : debug(format("executing `%1%' to find additional roots") % rootFinder);
397 :
398 1 : string result = runProgram(rootFinder);
399 :
400 2 : Strings paths = tokenizeString(result, "\n");
401 :
402 253 : for (Strings::iterator i = paths.begin(); i != paths.end(); ++i) {
403 251 : if (isInStore(*i)) {
404 1 : Path path = toStorePath(*i);
405 1 : if (roots.find(path) == roots.end() && store->isValidPath(path)) {
406 1 : debug(format("found additional root `%1%'") % path);
407 1 : roots.insert(path);
408 1 : }
409 : }
410 1 : }
411 : }
412 :
413 :
414 : static void dfsVisit(const PathSet & paths, const Path & path,
415 48 : PathSet & visited, Paths & sorted)
416 : {
417 48 : if (visited.find(path) != visited.end()) return;
418 36 : visited.insert(path);
419 :
420 36 : PathSet references;
421 36 : if (store->isValidPath(path))
422 36 : store->queryReferences(path, references);
423 :
424 63 : for (PathSet::iterator i = references.begin();
425 : i != references.end(); ++i)
426 : /* Don't traverse into paths that don't exist. That can
427 : happen due to substitutes for non-existent paths. */
428 27 : if (*i != path && paths.find(*i) != paths.end())
429 12 : dfsVisit(paths, *i, visited, sorted);
430 :
431 36 : sorted.push_front(path);
432 : }
433 :
434 :
435 27 : Paths topoSortPaths(const PathSet & paths)
436 : {
437 27 : Paths sorted;
438 27 : PathSet visited;
439 63 : for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i)
440 36 : dfsVisit(paths, *i, visited, sorted);
441 27 : return sorted;
442 : }
443 :
444 :
445 0 : static time_t lastFileAccessTime(const Path & path)
446 : {
447 0 : checkInterrupt();
448 :
449 : struct stat st;
450 0 : if (lstat(path.c_str(), &st) == -1)
451 0 : throw SysError(format("statting `%1%'") % path);
452 :
453 0 : if (S_ISDIR(st.st_mode)) {
454 0 : time_t last = 0;
455 0 : Strings names = readDirectory(path);
456 0 : for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
457 0 : time_t t = lastFileAccessTime(path + "/" + *i);
458 0 : if (t > last) last = t;
459 : }
460 0 : return last;
461 : }
462 :
463 0 : else if (S_ISLNK(st.st_mode)) return 0;
464 :
465 0 : else return st.st_atime;
466 : }
467 :
468 :
469 : struct GCLimitReached { };
470 :
471 :
472 : void LocalStore::gcPath(const GCOptions & options, GCResults & results,
473 2676 : const Path & path)
474 : {
475 2676 : results.paths.insert(path);
476 :
477 2676 : if (!pathExists(path)) return;
478 :
479 : #ifndef __CYGWIN__
480 2676 : AutoCloseFD fdLock;
481 :
482 : /* Only delete a lock file if we can acquire a write lock on it.
483 : That means that it's either stale, or the process that created
484 : it hasn't locked it yet. In the latter case the other process
485 : will detect that we deleted the lock, and retry (see
486 : pathlocks.cc). */
487 2676 : if (path.size() >= 5 && string(path, path.size() - 5) == ".lock") {
488 2 : fdLock = openLockFile(path, false);
489 2 : if (fdLock != -1 && !lockFile(fdLock, ltWrite, false)) {
490 1 : debug(format("skipping active lock `%1%'") % path);
491 1 : return;
492 : }
493 : }
494 : #endif
495 :
496 : /* Okay, it's safe to delete. */
497 : unsigned long long bytesFreed, blocksFreed;
498 2675 : deleteFromStore(path, bytesFreed, blocksFreed);
499 2675 : results.bytesFreed += bytesFreed;
500 2675 : results.blocksFreed += blocksFreed;
501 :
502 2675 : if (results.bytesFreed > options.maxFreed) {
503 0 : printMsg(lvlInfo, format("deleted more than %1% bytes; stopping") % options.maxFreed);
504 0 : throw GCLimitReached();
505 : }
506 :
507 2675 : if (options.maxLinks) {
508 : struct stat st;
509 0 : if (stat(nixStore.c_str(), &st) == -1)
510 0 : throw SysError(format("statting `%1%'") % nixStore);
511 0 : if (st.st_nlink < options.maxLinks) {
512 0 : printMsg(lvlInfo, format("link count on the store has dropped below %1%; stopping") % options.maxLinks);
513 0 : throw GCLimitReached();
514 : }
515 : }
516 :
517 : #ifndef __CYGWIN__
518 2675 : if (fdLock != -1)
519 : /* Write token to stale (deleted) lock file. */
520 1 : writeFull(fdLock, (const unsigned char *) "d", 1);
521 : #endif
522 : }
523 :
524 :
525 : void LocalStore::gcPathRecursive(const GCOptions & options,
526 3737 : GCResults & results, PathSet & done, const Path & path)
527 : {
528 3737 : if (done.find(path) != done.end()) return;
529 2676 : done.insert(path);
530 :
531 2676 : startNest(nest, lvlDebug, format("looking at `%1%'") % path);
532 :
533 : /* Delete all the referrers first. They must be garbage too,
534 : since if they were live, then the current path would also be
535 : live. Note that deleteFromStore() below still makes sure that
536 : the referrer set has become empty, just in case. (However that
537 : doesn't guard against deleting top-level paths that are only
538 : reachable from GC roots.) */
539 2676 : PathSet referrers;
540 2676 : if (isValidPath(path))
541 2673 : queryReferrers(path, referrers);
542 3741 : foreach (PathSet::iterator, i, referrers)
543 1065 : if (*i != path) gcPathRecursive(options, results, done, *i);
544 :
545 2676 : printMsg(lvlInfo, format("deleting `%1%'") % path);
546 :
547 2676 : gcPath(options, results, path);
548 : }
549 :
550 :
551 : struct CachingAtimeComparator : public std::binary_function<Path, Path, bool>
552 0 : {
553 : std::map<Path, time_t> cache;
554 :
555 0 : time_t lookup(const Path & p)
556 : {
557 0 : std::map<Path, time_t>::iterator i = cache.find(p);
558 0 : if (i != cache.end()) return i->second;
559 0 : debug(format("computing atime of `%1%'") % p);
560 0 : cache[p] = lastFileAccessTime(p);
561 0 : assert(cache.find(p) != cache.end());
562 0 : return cache[p];
563 : }
564 :
565 0 : bool operator () (const Path & p1, const Path & p2)
566 : {
567 0 : return lookup(p2) < lookup(p1);
568 : }
569 : };
570 :
571 :
572 0 : string showTime(const string & format, time_t t)
573 : {
574 : char s[128];
575 0 : strftime(s, sizeof s, format.c_str(), localtime(&t));
576 0 : return string(s);
577 : }
578 :
579 :
580 21 : void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
581 : {
582 : bool gcKeepOutputs =
583 21 : queryBoolSetting("gc-keep-outputs", false);
584 : bool gcKeepDerivations =
585 42 : queryBoolSetting("gc-keep-derivations", true);
586 : int gcKeepOutputsThreshold =
587 42 : queryIntSetting ("gc-keep-outputs-threshold", defaultGcLevel);
588 :
589 : /* Acquire the global GC root. This prevents
590 : a) New roots from being added.
591 : b) Processes from creating new temporary root files. */
592 21 : AutoCloseFD fdGCLock = openGCLock(ltWrite);
593 :
594 : /* Find the roots. Since we've grabbed the GC lock, the set of
595 : permanent roots cannot increase now. */
596 42 : printMsg(lvlError, format("finding garbage collector roots..."));
597 21 : Roots rootMap = options.ignoreLiveness ? Roots() : nix::findRoots(true);
598 :
599 21 : PathSet roots;
600 65 : for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i)
601 44 : roots.insert(i->second);
602 :
603 : /* Add additional roots returned by the program specified by the
604 : NIX_ROOT_FINDER environment variable. This is typically used
605 : to add running programs to the set of roots (to prevent them
606 : from being garbage collected). */
607 21 : if (!options.ignoreLiveness)
608 21 : addAdditionalRoots(roots);
609 :
610 21 : if (options.action == GCOptions::gcReturnRoots) {
611 1 : results.paths = roots;
612 5 : return;
613 : }
614 :
615 : /* Determine the live paths which is just the closure of the
616 : roots under the `references' relation. */
617 20 : printMsg(lvlError, format("computing live paths..."));
618 20 : PathSet livePaths;
619 56 : for (PathSet::const_iterator i = roots.begin(); i != roots.end(); ++i)
620 36 : computeFSClosure(canonPath(*i), livePaths);
621 :
622 20 : if (gcKeepDerivations) {
623 0 : for (PathSet::iterator i = livePaths.begin();
624 : i != livePaths.end(); ++i)
625 : {
626 : /* Note that the deriver need not be valid (e.g., if we
627 : previously ran the collector with `gcKeepDerivations'
628 : turned off). */
629 0 : Path deriver = queryDeriver(*i);
630 0 : if (deriver != "" && isValidPath(deriver))
631 0 : computeFSClosure(deriver, livePaths);
632 : }
633 : }
634 :
635 20 : if (gcKeepOutputs) {
636 : /* Hmz, identical to storePathRequisites in nix-store. */
637 0 : for (PathSet::iterator i = livePaths.begin();
638 : i != livePaths.end(); ++i)
639 0 : if (isDerivation(*i)) {
640 0 : Derivation drv = derivationFromPath(*i);
641 :
642 0 : string gcLevelStr = drv.env["__gcLevel"];
643 : int gcLevel;
644 0 : if (!string2Int(gcLevelStr, gcLevel))
645 0 : gcLevel = defaultGcLevel;
646 :
647 0 : if (gcLevel >= gcKeepOutputsThreshold)
648 0 : for (DerivationOutputs::iterator j = drv.outputs.begin();
649 : j != drv.outputs.end(); ++j)
650 0 : if (isValidPath(j->second.path))
651 0 : computeFSClosure(j->second.path, livePaths);
652 : }
653 : }
654 :
655 20 : if (options.action == GCOptions::gcReturnLive) {
656 1 : results.paths = livePaths;
657 : return;
658 : }
659 :
660 : /* Read the temporary roots. This acquires read locks on all
661 : per-process temporary root files. So after this point no paths
662 : can be added to the set of temporary roots. */
663 19 : PathSet tempRoots;
664 19 : FDs fds;
665 19 : readTempRoots(tempRoots, fds);
666 :
667 : /* Close the temporary roots. Note that we *cannot* do this in
668 : readTempRoots(), because there we may not have all locks yet,
669 : meaning that an invalid path can become valid (and thus add to
670 : the references graph) after we have added it to the closure
671 : (and computeFSClosure() assumes that the presence of a path
672 : means that it has already been closed). */
673 19 : PathSet tempRootsClosed;
674 28 : for (PathSet::iterator i = tempRoots.begin(); i != tempRoots.end(); ++i)
675 9 : if (isValidPath(*i))
676 8 : computeFSClosure(*i, tempRootsClosed);
677 : else
678 1 : tempRootsClosed.insert(*i);
679 :
680 : /* After this point the set of roots or temporary roots cannot
681 : increase, since we hold locks on everything. So everything
682 : that is not currently in in `livePaths' or `tempRootsClosed'
683 : can be deleted. */
684 :
685 : /* Read the Nix store directory to find all currently existing
686 : paths and filter out all live paths. */
687 19 : printMsg(lvlError, format("reading the Nix store..."));
688 19 : PathSet storePaths;
689 :
690 19 : if (options.action != GCOptions::gcDeleteSpecific) {
691 16 : Paths entries = readDirectory(nixStore);
692 2861 : foreach (Paths::iterator, i, entries) {
693 2845 : Path path = canonPath(nixStore + "/" + *i);
694 2845 : if (livePaths.find(path) == livePaths.end() &&
695 : tempRootsClosed.find(path) == tempRootsClosed.end())
696 2762 : storePaths.insert(path);
697 16 : }
698 : }
699 :
700 : else {
701 4 : foreach (PathSet::iterator, i, options.pathsToDelete) {
702 3 : assertStorePath(*i);
703 3 : storePaths.insert(*i);
704 3 : if (livePaths.find(*i) != livePaths.end())
705 2 : throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
706 1 : if (tempRootsClosed.find(*i) != tempRootsClosed.end())
707 0 : throw Error(format("cannot delete path `%1%' since it is temporarily in use") % *i);
708 : }
709 : }
710 :
711 17 : if (options.action == GCOptions::gcReturnDead) {
712 3 : results.paths.insert(storePaths.begin(), storePaths.end());
713 : return;
714 : }
715 :
716 : /* Delete all dead store paths (or until one of the stop
717 : conditions is reached). */
718 :
719 14 : PathSet done;
720 : try {
721 :
722 14 : if (!options.useAtime) {
723 : /* Delete the paths, respecting the partial ordering
724 : determined by the references graph. */
725 14 : printMsg(lvlError, format("deleting garbage..."));
726 2690 : foreach (PathSet::iterator, i, storePaths)
727 2676 : gcPathRecursive(options, results, done, *i);
728 : }
729 :
730 : else {
731 :
732 : /* Delete in order of ascending last access time, still
733 : maintaining the partial ordering of the reference
734 : graph. Note that we can't use a topological sort for
735 : this because that takes time O(V+E), and in this case
736 : E=O(V^2) (i.e. the graph is dense because of the edges
737 : due to the atime ordering). So instead we put all
738 : deletable paths in a priority queue (ordered by atime),
739 : and after deleting a path, add additional paths that
740 : have become deletable to the priority queue. */
741 :
742 0 : CachingAtimeComparator atimeComp;
743 :
744 : /* Create a priority queue that orders paths by ascending
745 : atime. This is why C++ needs type inferencing... */
746 : std::priority_queue<Path, vector<Path>, binary_function_ref_adapter<CachingAtimeComparator> > prioQueue =
747 0 : std::priority_queue<Path, vector<Path>, binary_function_ref_adapter<CachingAtimeComparator> >(binary_function_ref_adapter<CachingAtimeComparator>(&atimeComp));
748 :
749 : /* Initially put the paths that are invalid or have no
750 : referrers into the priority queue. */
751 0 : printMsg(lvlError, format("finding deletable paths..."));
752 0 : foreach (PathSet::iterator, i, storePaths) {
753 0 : checkInterrupt();
754 : /* We can safely delete a path if it's invalid or
755 : it has no referrers. Note that all the invalid
756 : paths will be deleted in the first round. */
757 0 : if (isValidPath(*i)) {
758 0 : if (queryReferrersNoSelf(*i).empty()) prioQueue.push(*i);
759 0 : } else prioQueue.push(*i);
760 : }
761 :
762 0 : debug(format("%1% initially deletable paths") % prioQueue.size());
763 :
764 : /* Now delete everything in the order of the priority
765 : queue until nothing is left. */
766 0 : printMsg(lvlError, format("deleting garbage..."));
767 0 : while (!prioQueue.empty()) {
768 0 : checkInterrupt();
769 0 : Path path = prioQueue.top(); prioQueue.pop();
770 :
771 0 : if (options.maxAtime != (time_t) -1 &&
772 : atimeComp.lookup(path) > options.maxAtime)
773 0 : continue;
774 :
775 0 : printMsg(lvlInfo, format("deleting `%1%' (last accessed %2%)") % path % showTime("%F %H:%M:%S", atimeComp.lookup(path)));
776 :
777 0 : PathSet references;
778 0 : if (isValidPath(path)) references = queryReferencesNoSelf(path);
779 :
780 0 : gcPath(options, results, path);
781 :
782 : /* For each reference of the current path, see if the
783 : reference has now become deletable (i.e. is in the
784 : set of dead paths and has no referrers left). If
785 : so add it to the priority queue. */
786 0 : foreach (PathSet::iterator, i, references) {
787 0 : if (storePaths.find(*i) != storePaths.end() &&
788 : queryReferrersNoSelf(*i).empty())
789 : {
790 0 : debug(format("path `%1%' has become deletable") % *i);
791 0 : prioQueue.push(*i);
792 : }
793 : }
794 0 : }
795 :
796 : }
797 :
798 0 : } catch (GCLimitReached & e) {
799 14 : }
800 : }
801 :
802 0 :
803 1106 : }
|