1 : #include <iostream>
2 :
3 : #include "globals.hh"
4 : #include "normalise.hh"
5 : #include "gc.hh"
6 : #include "archive.hh"
7 : #include "shared.hh"
8 : #include "dotgraph.hh"
9 : #include "help.txt.hh"
10 :
11 :
12 : typedef void (* Operation) (Strings opFlags, Strings opArgs);
13 :
14 :
15 : void printHelp()
16 0 : {
17 0 : cout << string((char *) helpText, sizeof helpText);
18 : }
19 :
20 :
21 : /* Realise paths from the given store expressions. */
22 : static void opRealise(Strings opFlags, Strings opArgs)
23 7 : {
24 19063 : if (!opFlags.empty()) throw UsageError("unknown flag");
25 :
26 14 : for (Strings::iterator i = opArgs.begin();
27 : i != opArgs.end(); i++)
28 : {
29 7 : Path nfPath = realiseStoreExpr(*i);
30 7 : cout << format("%1%\n") % (string) nfPath;
31 : }
32 : }
33 :
34 :
35 : /* Add paths to the Nix values directory and print the hashes of those
36 : paths. */
37 : static void opAdd(Strings opFlags, Strings opArgs)
38 0 : {
39 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
40 :
41 0 : for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); i++)
42 0 : cout << format("%1%\n") % addToStore(*i);
43 : }
44 :
45 :
46 : Path maybeNormalise(const Path & ne, bool normalise, bool realise)
47 9 : {
48 9 : if (realise) {
49 6 : Path ne2 = realiseStoreExpr(ne);
50 6 : return normalise ? ne2 : ne;
51 : } else
52 3 : return normalise ? normaliseStoreExpr(ne) : ne;
53 : }
54 :
55 :
56 : /* Perform various sorts of queries. */
57 : static void opQuery(Strings opFlags, Strings opArgs)
58 9 : {
59 : enum { qList, qRequisites, qPredecessors, qGraph
60 9 : } query = qList;
61 9 : bool normalise = false;
62 9 : bool realise = false;
63 9 : bool includeExprs = true;
64 9 : bool includeSuccessors = false;
65 :
66 23 : for (Strings::iterator i = opFlags.begin();
67 : i != opFlags.end(); i++)
68 14 : if (*i == "--list" || *i == "-l") query = qList;
69 14 : else if (*i == "--requisites" || *i == "-R") query = qRequisites;
70 13 : else if (*i == "--predecessors") query = qPredecessors;
71 13 : else if (*i == "--graph") query = qGraph;
72 13 : else if (*i == "--normalise" || *i == "-n") normalise = true;
73 6 : else if (*i == "--force-realise" || *i == "-f") realise = true;
74 0 : else if (*i == "--exclude-exprs") includeExprs = false;
75 0 : else if (*i == "--include-successors") includeSuccessors = true;
76 0 : else throw UsageError(format("unknown flag `%1%'") % *i);
77 :
78 9 : switch (query) {
79 :
80 : case qList: {
81 16 : for (Strings::iterator i = opArgs.begin();
82 : i != opArgs.end(); i++)
83 : {
84 : StringSet paths = storeExprRoots(
85 4902 : maybeNormalise(*i, normalise, realise));
86 16 : for (StringSet::iterator j = paths.begin();
87 : j != paths.end(); j++)
88 8 : cout << format("%s\n") % *j;
89 : }
90 1 : break;
91 : }
92 :
93 : case qRequisites: {
94 1 : StringSet paths;
95 2 : for (Strings::iterator i = opArgs.begin();
96 : i != opArgs.end(); i++)
97 : {
98 : StringSet paths2 = storeExprRequisites(
99 : maybeNormalise(*i, normalise, realise),
100 1 : includeExprs, includeSuccessors);
101 1 : paths.insert(paths2.begin(), paths2.end());
102 : }
103 4 : for (StringSet::iterator i = paths.begin();
104 : i != paths.end(); i++)
105 3 : cout << format("%s\n") % *i;
106 1 : break;
107 : }
108 :
109 : case qPredecessors: {
110 0 : for (Strings::iterator i = opArgs.begin();
111 : i != opArgs.end(); i++)
112 : {
113 2564 : Paths preds = queryPredecessors(*i);
114 0 : for (Paths::iterator j = preds.begin();
115 : j != preds.end(); j++)
116 0 : cout << format("%s\n") % *j;
117 : }
118 0 : break;
119 : }
120 :
121 : case qGraph: {
122 0 : PathSet roots;
123 0 : for (Strings::iterator i = opArgs.begin();
124 : i != opArgs.end(); i++)
125 0 : roots.insert(maybeNormalise(*i, normalise, realise));
126 0 : printDotGraph(roots);
127 0 : break;
128 : }
129 :
130 : default:
131 0 : abort();
132 : }
133 : }
134 :
135 :
136 : static void opSuccessor(Strings opFlags, Strings opArgs)
137 3 : {
138 3 : if (!opFlags.empty()) throw UsageError("unknown flag");
139 3 : if (opArgs.size() % 2) throw UsageError("expecting even number of arguments");
140 :
141 3 : Transaction txn;
142 3 : createStoreTransaction(txn);
143 6 : for (Strings::iterator i = opArgs.begin();
144 : i != opArgs.end(); )
145 : {
146 3 : Path path1 = *i++;
147 3 : Path path2 = *i++;
148 3 : registerSuccessor(txn, path1, path2);
149 : }
150 3 : txn.commit();
151 : }
152 :
153 :
154 : static void opSubstitute(Strings opFlags, Strings opArgs)
155 7 : {
156 7 : if (!opFlags.empty()) throw UsageError("unknown flag");
157 7 : if (!opArgs.empty())
158 0 : throw UsageError("no arguments expected");
159 :
160 21 : SubstitutePairs subPairs;
161 7 : Transaction txn;
162 7 : createStoreTransaction(txn);
163 :
164 14 : while (1) {
165 14 : Path srcPath;
166 1030 : Substitute sub;
167 14 : getline(cin, srcPath);
168 14 : if (cin.eof()) break;
169 7 : getline(cin, sub.storeExpr);
170 7 : getline(cin, sub.program);
171 7 : string s;
172 7 : getline(cin, s);
173 7 : int n;
174 7 : if (!string2Int(s, n)) throw Error("number expected");
175 25 : while (n--) {
176 18 : getline(cin, s);
177 18 : sub.args.push_back(s);
178 : }
179 7 : if (!cin || cin.eof()) throw Error("missing input");
180 149 : subPairs.push_back(pair<Path, Substitute>(srcPath, sub));
181 : }
182 :
183 7 : registerSubstitutes(txn, subPairs);
184 :
185 7 : txn.commit();
186 : }
187 :
188 :
189 : static void opValidPath(Strings opFlags, Strings opArgs)
190 0 : {
191 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
192 :
193 0 : Transaction txn;
194 0 : createStoreTransaction(txn);
195 0 : for (Strings::iterator i = opArgs.begin();
196 : i != opArgs.end(); ++i)
197 0 : registerValidPath(txn, *i);
198 0 : txn.commit();
199 : }
200 :
201 :
202 : static void opIsValid(Strings opFlags, Strings opArgs)
203 0 : {
204 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
205 :
206 0 : for (Strings::iterator i = opArgs.begin();
207 : i != opArgs.end(); ++i)
208 0 : if (!isValidPath(*i))
209 0 : throw Error(format("path `%1%' is not valid") % *i);
210 : }
211 :
212 :
213 : static void opGC(Strings opFlags, Strings opArgs)
214 0 : {
215 : /* Do what? */
216 0 : enum { soPrintLive, soPrintDead, soDelete } subOp;
217 0 : time_t minAge = 0;
218 0 : for (Strings::iterator i = opFlags.begin();
219 : i != opFlags.end(); ++i)
220 0 : if (*i == "--print-live") subOp = soPrintLive;
221 0 : else if (*i == "--print-dead") subOp = soPrintDead;
222 0 : else if (*i == "--delete") subOp = soDelete;
223 0 : else if (*i == "--min-age") {
224 0 : int n;
225 0 : if (opArgs.size() == 0 || !string2Int(opArgs.front(), n))
226 0 : throw UsageError("`--min-age' requires an integer argument");
227 0 : minAge = n;
228 : }
229 0 : else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
230 :
231 0 : Paths roots;
232 0 : while (1) {
233 0 : Path root;
234 0 : getline(cin, root);
235 0 : if (cin.eof()) break;
236 0 : roots.push_back(root);
237 : }
238 :
239 0 : PathSet live = findLivePaths(roots);
240 :
241 0 : if (subOp == soPrintLive) {
242 0 : for (PathSet::iterator i = live.begin(); i != live.end(); ++i)
243 0 : cout << *i << endl;
244 0 : return;
245 : }
246 :
247 0 : PathSet dead = findDeadPaths(live, minAge * 3600);
248 :
249 0 : if (subOp == soPrintDead) {
250 0 : for (PathSet::iterator i = dead.begin(); i != dead.end(); ++i)
251 0 : cout << *i << endl;
252 0 : return;
253 : }
254 :
255 0 : if (subOp == soDelete) {
256 :
257 : /* !!! What happens if the garbage collector run is aborted
258 : halfway through? In particular, dead paths can always
259 : become live again (through re-instantiation), and might
260 : then refer to deleted paths. => check instantiation
261 : invariants */
262 :
263 0 : for (PathSet::iterator i = dead.begin(); i != dead.end(); ++i) {
264 0 : printMsg(lvlInfo, format("deleting `%1%'") % *i);
265 0 : deleteFromStore(*i);
266 : }
267 : }
268 : }
269 :
270 :
271 : /* A sink that writes dump output to stdout. */
272 : struct StdoutSink : DumpSink
273 : {
274 : virtual void operator ()
275 : (const unsigned char * data, unsigned int len)
276 0 : {
277 0 : writeFull(STDOUT_FILENO, data, len);
278 : }
279 : };
280 :
281 :
282 : /* Dump a path as a Nix archive. The archive is written to standard
283 : output. */
284 : static void opDump(Strings opFlags, Strings opArgs)
285 0 : {
286 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
287 0 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
288 :
289 0 : StdoutSink sink;
290 0 : string path = *opArgs.begin();
291 0 : dumpPath(path, sink);
292 : }
293 :
294 :
295 : /* A source that read restore intput to stdin. */
296 : struct StdinSource : RestoreSource
297 : {
298 : virtual void operator () (unsigned char * data, unsigned int len)
299 0 : {
300 0 : readFull(STDIN_FILENO, data, len);
301 : }
302 : };
303 :
304 :
305 : /* Restore a value from a Nix archive. The archive is written to
306 : standard input. */
307 : static void opRestore(Strings opFlags, Strings opArgs)
308 0 : {
309 0 : if (!opFlags.empty()) throw UsageError("unknown flag");
310 0 : if (opArgs.size() != 1) throw UsageError("only one argument allowed");
311 :
312 0 : StdinSource source;
313 0 : restorePath(*opArgs.begin(), source);
314 : }
315 :
316 :
317 : /* Initialise the Nix databases. */
318 : static void opInit(Strings opFlags, Strings opArgs)
319 1 : {
320 1 : if (!opFlags.empty()) throw UsageError("unknown flag");
321 1 : if (!opArgs.empty())
322 0 : throw UsageError("no arguments expected");
323 1 : initDB();
324 : }
325 :
326 :
327 : /* Verify the consistency of the Nix environment. */
328 : static void opVerify(Strings opFlags, Strings opArgs)
329 1 : {
330 1 : verifyStore();
331 : }
332 :
333 :
334 : /* Scan the arguments; find the operation, set global flags, put all
335 : other flags in a list, and put all other arguments in another
336 : list. */
337 : void run(Strings args)
338 28 : {
339 28 : Strings opFlags, opArgs;
340 28 : Operation op = 0;
341 :
342 92 : for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
343 64 : string arg = *i;
344 :
345 64 : Operation oldOp = op;
346 :
347 64 : if (arg == "--realise" || arg == "-r")
348 7 : op = opRealise;
349 57 : else if (arg == "--add" || arg == "-A")
350 0 : op = opAdd;
351 57 : else if (arg == "--query" || arg == "-q")
352 9 : op = opQuery;
353 48 : else if (arg == "--successor")
354 3 : op = opSuccessor;
355 45 : else if (arg == "--substitute")
356 7 : op = opSubstitute;
357 38 : else if (arg == "--validpath")
358 0 : op = opValidPath;
359 38 : else if (arg == "--isvalid")
360 0 : op = opIsValid;
361 38 : else if (arg == "--gc")
362 0 : op = opGC;
363 38 : else if (arg == "--dump")
364 0 : op = opDump;
365 38 : else if (arg == "--restore")
366 0 : op = opRestore;
367 38 : else if (arg == "--init")
368 1 : op = opInit;
369 37 : else if (arg == "--verify")
370 1 : op = opVerify;
371 36 : else if (arg[0] == '-')
372 14 : opFlags.push_back(arg);
373 : else
374 22 : opArgs.push_back(arg);
375 :
376 64 : if (oldOp && oldOp != op)
377 0 : throw UsageError("only one operation may be specified");
378 : }
379 :
380 28 : if (!op) throw UsageError("no operation specified");
381 :
382 28 : if (op != opDump && op != opRestore) /* !!! hack */
383 28 : openDB();
384 :
385 28 : op(opFlags, opArgs);
386 : }
387 :
388 :
389 56 : string programId = "nix-store";
|