1 : #include <iostream>
2 : #include <algorithm>
3 :
4 : #include "globals.hh"
5 : #include "misc.hh"
6 : #include "archive.hh"
7 : #include "shared.hh"
8 : #include "dotgraph.hh"
9 : #include "local-store.hh"
10 : #include "util.hh"
11 : #include "help.txt.hh"
12 :
13 :
14 : using namespace nix;
15 : using std::cin;
16 : using std::cout;
17 :
18 :
19 : typedef void (* Operation) (Strings opFlags, Strings opArgs);
20 :
21 :
22 1 : void printHelp()
23 : {
24 1 : cout << string((char *) helpText, sizeof helpText);
25 1 : }
26 :
27 :
28 414 : static Path gcRoot;
29 : static int rootNr = 0;
30 : static bool indirectRoot = false;
31 :
32 :
33 4 : LocalStore & ensureLocalStore()
34 : {
35 4 : LocalStore * store2(dynamic_cast<LocalStore *>(store.get()));
36 4 : if (!store2) throw Error("you don't have sufficient rights to use --verify");
37 4 : return *store2;
38 : }
39 :
40 :
41 1 : static Path useDeriver(Path path)
42 : {
43 1 : if (!isDerivation(path)) {
44 0 : path = store->queryDeriver(path);
45 0 : if (path == "")
46 0 : throw Error(format("deriver of path `%1%' is not known") % path);
47 : }
48 1 : return path;
49 : }
50 :
51 :
52 : /* Realisation the given path. For a derivation that means build it;
53 : for other paths it means ensure their validity. */
54 47 : static Path realisePath(const Path & path)
55 : {
56 47 : if (isDerivation(path)) {
57 46 : PathSet paths;
58 46 : paths.insert(path);
59 46 : store->buildDerivations(paths);
60 46 : Path outPath = findOutput(derivationFromPath(path), "out");
61 :
62 92 : if (gcRoot == "")
63 38 : printGCWarning();
64 : else
65 : outPath = addPermRoot(outPath,
66 : makeRootName(gcRoot, rootNr),
67 8 : indirectRoot);
68 :
69 46 : return outPath;
70 : } else {
71 1 : store->ensurePath(path);
72 1 : return path;
73 : }
74 : }
75 :
76 :
77 : /* Realise the given paths. */
78 34 : static void opRealise(Strings opFlags, Strings opArgs)
79 : {
80 34 : bool dryRun = false;
81 :
82 68 : foreach (Strings::iterator, i, opFlags)
83 0 : if (*i == "--dry-run") dryRun = true;
84 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
85 :
86 162 : foreach (Strings::iterator, i, opArgs)
87 47 : *i = followLinksToStorePath(*i);
88 :
89 34 : printMissing(PathSet(opArgs.begin(), opArgs.end()));
90 :
91 34 : if (dryRun) return;
92 :
93 : /* Build all derivations at the same time to exploit parallelism. */
94 34 : PathSet drvPaths;
95 81 : foreach (Strings::iterator, i, opArgs)
96 47 : if (isDerivation(*i)) drvPaths.insert(*i);
97 34 : store->buildDerivations(drvPaths);
98 :
99 73 : foreach (Strings::iterator, i,opArgs)
100 73 : cout << format("%1%\n") % realisePath(*i);
101 : }
102 :
103 :
104 : /* Add files to the Nix store and print the resulting paths. */
105 5 : static void opAdd(Strings opFlags, Strings opArgs)
106 : {
107 5 : if (!opFlags.empty()) throw UsageError("unknown flag");
108 :
109 20 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
110 5 : cout << format("%1%\n") % store->addToStore(*i);
111 5 : }
112 :
113 :
114 : /* Preload the output of a fixed-output derivation into the Nix
115 : store. */
116 16 : static void opAddFixed(Strings opFlags, Strings opArgs)
117 : {
118 16 : bool recursive = false;
119 :
120 32 : for (Strings::iterator i = opFlags.begin();
121 : i != opFlags.end(); ++i)
122 0 : if (*i == "--recursive") recursive = true;
123 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
124 :
125 16 : if (opArgs.empty())
126 0 : throw UsageError("first argument must be hash algorithm");
127 :
128 16 : string hashAlgo = opArgs.front();
129 16 : opArgs.pop_front();
130 :
131 32 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
132 32 : cout << format("%1%\n") % store->addToStore(*i, true, recursive, hashAlgo);
133 16 : }
134 :
135 :
136 12 : static Hash parseHash16or32(HashType ht, const string & s)
137 : {
138 : return s.size() == Hash(ht).hashSize * 2
139 : ? parseHash(ht, s)
140 12 : : parseHash32(ht, s);
141 : }
142 :
143 :
144 : /* Hack to support caching in `nix-prefetch-url'. */
145 12 : static void opPrintFixedPath(Strings opFlags, Strings opArgs)
146 : {
147 12 : bool recursive = false;
148 :
149 24 : for (Strings::iterator i = opFlags.begin();
150 : i != opFlags.end(); ++i)
151 0 : if (*i == "--recursive") recursive = true;
152 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
153 :
154 12 : Strings::iterator i = opArgs.begin();
155 12 : string hashAlgo = *i++;
156 12 : string hash = *i++;
157 12 : string name = *i++;
158 :
159 : cout << format("%1%\n") %
160 : makeFixedOutputPath(recursive, hashAlgo,
161 12 : parseHash16or32(parseHashType(hashAlgo), hash), name);
162 12 : }
163 :
164 :
165 : /* Place in `paths' the set of paths that are required to `realise'
166 : the given store path, i.e., all paths necessary for valid
167 : deployment of the path. For a derivation, this is the union of
168 : requisites of the inputs, plus the derivation; for other store
169 : paths, it is the set of paths in the FS closure of the path. If
170 : `includeOutputs' is true, include the requisites of the output
171 : paths of derivations as well.
172 :
173 : Note that this function can be used to implement three different
174 : deployment policies:
175 :
176 : - Source deployment (when called on a derivation).
177 : - Binary deployment (when called on an output path).
178 : - Source/binary deployment (when called on a derivation with
179 : `includeOutputs' set to true).
180 : */
181 : static void storePathRequisites(const Path & storePath,
182 4 : bool includeOutputs, PathSet & paths)
183 : {
184 4 : computeFSClosure(storePath, paths);
185 :
186 4 : if (includeOutputs) {
187 10 : for (PathSet::iterator i = paths.begin();
188 : i != paths.end(); ++i)
189 9 : if (isDerivation(*i)) {
190 3 : Derivation drv = derivationFromPath(*i);
191 6 : for (DerivationOutputs::iterator j = drv.outputs.begin();
192 : j != drv.outputs.end(); ++j)
193 3 : if (store->isValidPath(j->second.path))
194 6 : computeFSClosure(j->second.path, paths);
195 : }
196 : }
197 4 : }
198 :
199 :
200 22 : static Path maybeUseOutput(const Path & storePath, bool useOutput, bool forceRealise)
201 : {
202 22 : if (forceRealise) realisePath(storePath);
203 22 : if (useOutput && isDerivation(storePath)) {
204 1 : Derivation drv = derivationFromPath(storePath);
205 1 : return findOutput(drv, "out");
206 : }
207 21 : else return storePath;
208 : }
209 :
210 :
211 : /* Some code to print a tree representation of a derivation dependency
212 : graph. Topological sorting is used to keep the tree relatively
213 : flat. */
214 :
215 621 : const string treeConn = "+---";
216 621 : const string treeLine = "| ";
217 621 : const string treeNull = " ";
218 :
219 :
220 : static void printTree(const Path & path,
221 9 : const string & firstPad, const string & tailPad, PathSet & done)
222 : {
223 9 : if (done.find(path) != done.end()) {
224 1 : cout << format("%1%%2% [...]\n") % firstPad % path;
225 1 : return;
226 : }
227 8 : done.insert(path);
228 :
229 8 : cout << format("%1%%2%\n") % firstPad % path;
230 :
231 8 : PathSet references;
232 8 : store->queryReferences(path, references);
233 :
234 : #if 0
235 : for (PathSet::iterator i = drv.inputSrcs.begin();
236 : i != drv.inputSrcs.end(); ++i)
237 : cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
238 : #endif
239 :
240 : /* Topologically sort under the relation A < B iff A \in
241 : closure(B). That is, if derivation A is an (possibly indirect)
242 : input of B, then A is printed first. This has the effect of
243 : flattening the tree, preventing deeply nested structures. */
244 8 : Paths sorted = topoSortPaths(references);
245 8 : reverse(sorted.begin(), sorted.end());
246 :
247 15 : for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) {
248 7 : Paths::iterator j = i; ++j;
249 : printTree(*i, tailPad + treeConn,
250 : j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
251 7 : done);
252 8 : }
253 : }
254 :
255 :
256 : /* Perform various sorts of queries. */
257 48 : static void opQuery(Strings opFlags, Strings opArgs)
258 : {
259 : enum { qOutputs, qRequisites, qReferences, qReferrers
260 : , qReferrersClosure, qDeriver, qBinding, qHash
261 48 : , qTree, qGraph, qResolve } query = qOutputs;
262 48 : bool useOutput = false;
263 48 : bool includeOutputs = false;
264 48 : bool forceRealise = false;
265 48 : string bindingName;
266 :
267 93 : for (Strings::iterator i = opFlags.begin();
268 : i != opFlags.end(); ++i)
269 45 : if (*i == "--outputs") query = qOutputs;
270 45 : else if (*i == "--requisites" || *i == "-R") query = qRequisites;
271 41 : else if (*i == "--references") query = qReferences;
272 27 : else if (*i == "--referrers" || *i == "--referers") query = qReferrers;
273 27 : else if (*i == "--referrers-closure" || *i == "--referers-closure") query = qReferrersClosure;
274 26 : else if (*i == "--deriver" || *i == "-d") query = qDeriver;
275 13 : else if (*i == "--binding" || *i == "-b") {
276 1 : if (opArgs.size() == 0)
277 0 : throw UsageError("expected binding name");
278 1 : bindingName = opArgs.front();
279 1 : opArgs.pop_front();
280 1 : query = qBinding;
281 : }
282 12 : else if (*i == "--hash") query = qHash;
283 11 : else if (*i == "--tree") query = qTree;
284 9 : else if (*i == "--graph") query = qGraph;
285 7 : else if (*i == "--resolve") query = qResolve;
286 7 : else if (*i == "--use-output" || *i == "-u") useOutput = true;
287 5 : else if (*i == "--force-realise" || *i == "-f") forceRealise = true;
288 1 : else if (*i == "--include-outputs") includeOutputs = true;
289 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
290 :
291 48 : switch (query) {
292 :
293 : case qOutputs: {
294 20 : for (Strings::iterator i = opArgs.begin();
295 : i != opArgs.end(); ++i)
296 : {
297 10 : *i = followLinksToStorePath(*i);
298 10 : if (forceRealise) realisePath(*i);
299 10 : Derivation drv = derivationFromPath(*i);
300 10 : cout << format("%1%\n") % findOutput(drv, "out");
301 : }
302 10 : break;
303 : }
304 :
305 : case qRequisites:
306 : case qReferences:
307 : case qReferrers:
308 : case qReferrersClosure: {
309 19 : PathSet paths;
310 38 : for (Strings::iterator i = opArgs.begin();
311 : i != opArgs.end(); ++i)
312 : {
313 19 : Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
314 19 : if (query == qRequisites)
315 4 : storePathRequisites(path, includeOutputs, paths);
316 15 : else if (query == qReferences) store->queryReferences(path, paths);
317 1 : else if (query == qReferrers) store->queryReferrers(path, paths);
318 1 : else if (query == qReferrersClosure) computeFSClosure(path, paths, true);
319 : }
320 19 : Paths sorted = topoSortPaths(paths);
321 48 : for (Paths::reverse_iterator i = sorted.rbegin();
322 : i != sorted.rend(); ++i)
323 29 : cout << format("%s\n") % *i;
324 19 : break;
325 : }
326 :
327 : case qDeriver:
328 26 : for (Strings::iterator i = opArgs.begin();
329 : i != opArgs.end(); ++i)
330 : {
331 13 : Path deriver = store->queryDeriver(followLinksToStorePath(*i));
332 : cout << format("%1%\n") %
333 13 : (deriver == "" ? "unknown-deriver" : deriver);
334 : }
335 13 : break;
336 :
337 : case qBinding:
338 2 : for (Strings::iterator i = opArgs.begin();
339 : i != opArgs.end(); ++i)
340 : {
341 1 : Path path = useDeriver(followLinksToStorePath(*i));
342 1 : Derivation drv = derivationFromPath(path);
343 1 : StringPairs::iterator j = drv.env.find(bindingName);
344 1 : if (j == drv.env.end())
345 : throw Error(format("derivation `%1%' has no environment binding named `%2%'")
346 0 : % path % bindingName);
347 1 : cout << format("%1%\n") % j->second;
348 : }
349 1 : break;
350 :
351 : case qHash:
352 2 : for (Strings::iterator i = opArgs.begin();
353 : i != opArgs.end(); ++i)
354 : {
355 1 : Path path = maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise);
356 1 : Hash hash = store->queryPathHash(path);
357 1 : assert(hash.type == htSHA256);
358 1 : cout << format("sha256:%1%\n") % printHash32(hash);
359 : }
360 1 : break;
361 :
362 : case qTree: {
363 2 : PathSet done;
364 8 : for (Strings::iterator i = opArgs.begin();
365 : i != opArgs.end(); ++i)
366 2 : printTree(followLinksToStorePath(*i), "", "", done);
367 2 : break;
368 : }
369 :
370 : case qGraph: {
371 2 : PathSet roots;
372 4 : for (Strings::iterator i = opArgs.begin();
373 : i != opArgs.end(); ++i)
374 2 : roots.insert(maybeUseOutput(followLinksToStorePath(*i), useOutput, forceRealise));
375 2 : printDotGraph(roots);
376 2 : break;
377 : }
378 :
379 : case qResolve: {
380 0 : for (Strings::iterator i = opArgs.begin();
381 : i != opArgs.end(); ++i)
382 0 : cout << format("%1%\n") % followLinksToStorePath(*i);
383 0 : break;
384 : }
385 :
386 : default:
387 0 : abort();
388 48 : }
389 48 : }
390 :
391 :
392 0 : static void opReadLog(Strings opFlags, Strings opArgs)
393 : {
394 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
395 :
396 0 : for (Strings::iterator i = opArgs.begin();
397 : i != opArgs.end(); ++i)
398 : {
399 0 : Path path = useDeriver(followLinksToStorePath(*i));
400 :
401 : Path logPath = (format("%1%/%2%/%3%") %
402 0 : nixLogDir % drvsLogDir % baseNameOf(path)).str();
403 :
404 0 : if (!pathExists(logPath))
405 0 : throw Error(format("build log of derivation `%1%' is not available") % path);
406 :
407 : /* !!! Make this run in O(1) memory. */
408 0 : string log = readFile(logPath);
409 0 : writeFull(STDOUT_FILENO, (const unsigned char *) log.c_str(), log.size());
410 : }
411 0 : }
412 :
413 :
414 0 : static void opDumpDB(Strings opFlags, Strings opArgs)
415 : {
416 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
417 0 : if (!opArgs.empty())
418 0 : throw UsageError("no arguments expected");
419 0 : PathSet validPaths = store->queryValidPaths();
420 0 : foreach (PathSet::iterator, i, validPaths) {
421 0 : cout << makeValidityRegistration(singleton<PathSet>(*i), true, true);
422 0 : }
423 0 : }
424 :
425 :
426 3 : static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
427 : {
428 3 : ValidPathInfos infos;
429 :
430 5001 : while (1) {
431 5004 : ValidPathInfo info = decodeValidPathInfo(cin, hashGiven);
432 5004 : if (info.path == "") break;
433 5001 : if (!store->isValidPath(info.path) || reregister) {
434 : /* !!! races */
435 5001 : if (canonicalise)
436 5001 : canonicalisePathMetaData(info.path);
437 5001 : if (!hashGiven)
438 5001 : info.hash = hashPath(htSHA256, info.path);
439 5001 : infos.push_back(info);
440 : }
441 : }
442 :
443 6 : ensureLocalStore().registerValidPaths(infos);
444 3 : }
445 :
446 :
447 0 : static void opLoadDB(Strings opFlags, Strings opArgs)
448 : {
449 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
450 0 : if (!opArgs.empty())
451 0 : throw UsageError("no arguments expected");
452 0 : registerValidity(true, true, false);
453 0 : }
454 :
455 :
456 3 : static void opRegisterValidity(Strings opFlags, Strings opArgs)
457 : {
458 3 : bool reregister = false; // !!! maybe this should be the default
459 3 : bool hashGiven = false;
460 :
461 4 : for (Strings::iterator i = opFlags.begin();
462 : i != opFlags.end(); ++i)
463 1 : if (*i == "--reregister") reregister = true;
464 0 : else if (*i == "--hash-given") hashGiven = true;
465 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
466 :
467 3 : if (!opArgs.empty()) throw UsageError("no arguments expected");
468 :
469 3 : registerValidity(reregister, hashGiven, true);
470 3 : }
471 :
472 :
473 24 : static void opCheckValidity(Strings opFlags, Strings opArgs)
474 : {
475 24 : bool printInvalid = false;
476 :
477 48 : for (Strings::iterator i = opFlags.begin();
478 : i != opFlags.end(); ++i)
479 0 : if (*i == "--print-invalid") printInvalid = true;
480 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
481 :
482 24 : for (Strings::iterator i = opArgs.begin();
483 : i != opArgs.end(); ++i)
484 : {
485 24 : Path path = followLinksToStorePath(*i);
486 24 : if (!store->isValidPath(path))
487 24 : if (printInvalid)
488 0 : cout << format("%1%\n") % path;
489 : else
490 24 : throw Error(format("path `%1%' is not valid") % path);
491 : }
492 0 : }
493 :
494 :
495 16 : static string showBytes(unsigned long long bytes, unsigned long long blocks)
496 : {
497 : return (format("%d bytes (%.2f MiB, %d blocks)")
498 16 : % bytes % (bytes / (1024.0 * 1024.0)) % blocks).str();
499 : }
500 :
501 :
502 : struct PrintFreed
503 : {
504 : bool show;
505 : const GCResults & results;
506 21 : PrintFreed(bool show, const GCResults & results)
507 21 : : show(show), results(results) { }
508 21 : ~PrintFreed()
509 : {
510 21 : if (show)
511 : cout << format("%1% store paths deleted, %2% freed\n")
512 : % results.paths.size()
513 16 : % showBytes(results.bytesFreed, results.blocksFreed);
514 21 : }
515 : };
516 :
517 :
518 18 : static void opGC(Strings opFlags, Strings opArgs)
519 : {
520 18 : GCOptions options;
521 18 : options.action = GCOptions::gcDeleteDead;
522 :
523 18 : GCResults results;
524 :
525 : /* Do what? */
526 23 : foreach (Strings::iterator, i, opFlags)
527 5 : if (*i == "--print-roots") options.action = GCOptions::gcReturnRoots;
528 4 : else if (*i == "--print-live") options.action = GCOptions::gcReturnLive;
529 3 : else if (*i == "--print-dead") options.action = GCOptions::gcReturnDead;
530 0 : else if (*i == "--delete") options.action = GCOptions::gcDeleteDead;
531 0 : else if (*i == "--max-freed") options.maxFreed = getIntArg(*i, i, opFlags.end());
532 0 : else if (*i == "--max-links") options.maxLinks = getIntArg(*i, i, opFlags.end());
533 0 : else if (*i == "--use-atime") options.useAtime = true;
534 0 : else if (*i == "--max-atime") {
535 0 : options.useAtime = true;
536 0 : options.maxAtime = getIntArg(*i, i, opFlags.end());
537 : }
538 0 : else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
539 :
540 18 : if (!opArgs.empty()) throw UsageError("no arguments expected");
541 :
542 18 : PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
543 18 : store->collectGarbage(options, results);
544 :
545 18 : if (options.action != GCOptions::gcDeleteDead)
546 95 : foreach (PathSet::iterator, i, results.paths)
547 108 : cout << *i << std::endl;
548 18 : }
549 :
550 :
551 : /* Remove paths from the Nix store if possible (i.e., if they do not
552 : have any remaining referrers and are not reachable from any GC
553 : roots). */
554 3 : static void opDelete(Strings opFlags, Strings opArgs)
555 : {
556 3 : GCOptions options;
557 3 : options.action = GCOptions::gcDeleteSpecific;
558 :
559 6 : foreach (Strings::iterator, i, opFlags)
560 0 : if (*i == "--ignore-liveness") options.ignoreLiveness = true;
561 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
562 :
563 6 : foreach (Strings::iterator, i, opArgs)
564 3 : options.pathsToDelete.insert(followLinksToStorePath(*i));
565 :
566 3 : GCResults results;
567 3 : PrintFreed freed(true, results);
568 5 : store->collectGarbage(options, results);
569 1 : }
570 :
571 :
572 : /* Dump a path as a Nix archive. The archive is written to standard
573 : output. */
574 9 : static void opDump(Strings opFlags, Strings opArgs)
575 : {
576 9 : if (!opFlags.empty()) throw UsageError("unknown flag");
577 9 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
578 :
579 9 : FdSink sink(STDOUT_FILENO);
580 9 : string path = *opArgs.begin();
581 9 : dumpPath(path, sink);
582 9 : }
583 :
584 :
585 : /* Restore a value from a Nix archive. The archive is read from
586 : standard input. */
587 12 : static void opRestore(Strings opFlags, Strings opArgs)
588 : {
589 12 : if (!opFlags.empty()) throw UsageError("unknown flag");
590 12 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
591 :
592 12 : FdSource source(STDIN_FILENO);
593 12 : restorePath(*opArgs.begin(), source);
594 12 : }
595 :
596 :
597 3 : static void opExport(Strings opFlags, Strings opArgs)
598 : {
599 3 : bool sign = false;
600 6 : for (Strings::iterator i = opFlags.begin();
601 : i != opFlags.end(); ++i)
602 0 : if (*i == "--sign") sign = true;
603 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
604 :
605 3 : FdSink sink(STDOUT_FILENO);
606 8 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i) {
607 5 : writeInt(1, sink);
608 5 : store->exportPath(*i, sign, sink);
609 : }
610 3 : writeInt(0, sink);
611 3 : }
612 :
613 :
614 3 : static void opImport(Strings opFlags, Strings opArgs)
615 : {
616 3 : bool requireSignature = false;
617 6 : for (Strings::iterator i = opFlags.begin();
618 : i != opFlags.end(); ++i)
619 0 : if (*i == "--require-signature") requireSignature = true;
620 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
621 :
622 3 : if (!opArgs.empty()) throw UsageError("no arguments expected");
623 :
624 3 : FdSource source(STDIN_FILENO);
625 3 : while (readInt(source) == 1)
626 8 : cout << format("%1%\n") % store->importPath(requireSignature, source) << std::flush;
627 2 : }
628 :
629 :
630 : /* Initialise the Nix databases. */
631 15 : static void opInit(Strings opFlags, Strings opArgs)
632 : {
633 15 : if (!opFlags.empty()) throw UsageError("unknown flag");
634 15 : if (!opArgs.empty())
635 0 : throw UsageError("no arguments expected");
636 : /* Doesn't do anything right now; database tables are initialised
637 : automatically. */
638 15 : }
639 :
640 :
641 : /* Verify the consistency of the Nix environment. */
642 1 : static void opVerify(Strings opFlags, Strings opArgs)
643 : {
644 1 : if (!opArgs.empty())
645 0 : throw UsageError("no arguments expected");
646 :
647 1 : bool checkContents = false;
648 :
649 2 : for (Strings::iterator i = opFlags.begin();
650 : i != opFlags.end(); ++i)
651 0 : if (*i == "--check-contents") checkContents = true;
652 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
653 :
654 1 : ensureLocalStore().verifyStore(checkContents);
655 1 : }
656 :
657 :
658 0 : static void showOptimiseStats(OptimiseStats & stats)
659 : {
660 0 : printMsg(lvlError,
661 : format("%1% freed by hard-linking %2% files; there are %3% files with equal contents out of %4% files in total")
662 : % showBytes(stats.bytesFreed, stats.blocksFreed)
663 : % stats.filesLinked
664 : % stats.sameContents
665 : % stats.totalFiles);
666 0 : }
667 :
668 :
669 : /* Optimise the disk space usage of the Nix store by hard-linking
670 : files with the same contents. */
671 0 : static void opOptimise(Strings opFlags, Strings opArgs)
672 : {
673 0 : if (!opArgs.empty())
674 0 : throw UsageError("no arguments expected");
675 :
676 0 : bool dryRun = false;
677 :
678 0 : for (Strings::iterator i = opFlags.begin();
679 : i != opFlags.end(); ++i)
680 0 : if (*i == "--dry-run") dryRun = true;
681 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
682 :
683 0 : OptimiseStats stats;
684 : try {
685 0 : ensureLocalStore().optimiseStore(dryRun, stats);
686 0 : } catch (...) {
687 0 : showOptimiseStats(stats);
688 0 : throw;
689 : }
690 0 : showOptimiseStats(stats);
691 0 : }
692 :
693 :
694 : /* Scan the arguments; find the operation, set global flags, put all
695 : other flags in a list, and put all other arguments in another
696 : list. */
697 206 : void run(Strings args)
698 : {
699 206 : Strings opFlags, opArgs;
700 206 : Operation op = 0;
701 :
702 911 : for (Strings::iterator i = args.begin(); i != args.end(); ) {
703 499 : string arg = *i++;
704 :
705 499 : Operation oldOp = op;
706 :
707 499 : if (arg == "--realise" || arg == "-r")
708 34 : op = opRealise;
709 465 : else if (arg == "--add" || arg == "-A")
710 5 : op = opAdd;
711 460 : else if (arg == "--add-fixed")
712 16 : op = opAddFixed;
713 444 : else if (arg == "--print-fixed-path")
714 12 : op = opPrintFixedPath;
715 432 : else if (arg == "--delete")
716 3 : op = opDelete;
717 429 : else if (arg == "--query" || arg == "-q")
718 48 : op = opQuery;
719 381 : else if (arg == "--read-log" || arg == "-l")
720 0 : op = opReadLog;
721 381 : else if (arg == "--dump-db")
722 0 : op = opDumpDB;
723 381 : else if (arg == "--load-db")
724 0 : op = opLoadDB;
725 381 : else if (arg == "--register-validity")
726 3 : op = opRegisterValidity;
727 378 : else if (arg == "--check-validity")
728 24 : op = opCheckValidity;
729 354 : else if (arg == "--gc")
730 18 : op = opGC;
731 336 : else if (arg == "--dump")
732 9 : op = opDump;
733 327 : else if (arg == "--restore")
734 12 : op = opRestore;
735 315 : else if (arg == "--export")
736 3 : op = opExport;
737 312 : else if (arg == "--import")
738 3 : op = opImport;
739 309 : else if (arg == "--init")
740 15 : op = opInit;
741 294 : else if (arg == "--verify")
742 1 : op = opVerify;
743 293 : else if (arg == "--optimise")
744 0 : op = opOptimise;
745 293 : else if (arg == "--add-root") {
746 10 : if (i == args.end())
747 0 : throw UsageError("`--add-root requires an argument");
748 10 : gcRoot = absPath(*i++);
749 : }
750 283 : else if (arg == "--indirect")
751 10 : indirectRoot = true;
752 273 : else if (arg[0] == '-') {
753 51 : opFlags.push_back(arg);
754 51 : if (arg == "--max-freed" || arg == "--max-links" || arg == "--max-atime") { /* !!! hack */
755 0 : if (i != args.end()) opFlags.push_back(*i++);
756 : }
757 : }
758 : else
759 222 : opArgs.push_back(arg);
760 :
761 499 : if (oldOp && oldOp != op)
762 0 : throw UsageError("only one operation may be specified");
763 : }
764 :
765 206 : if (!op) throw UsageError("no operation specified");
766 :
767 206 : if (op != opDump && op != opRestore) /* !!! hack */
768 185 : store = openStore();
769 :
770 237 : op(opFlags, opArgs);
771 175 : }
772 :
773 0 :
774 828 : string programId = "nix-store";
|