aboutsummaryrefslogtreecommitdiff
path: root/src/nix-store/nix-store.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix-store/nix-store.cc')
-rw-r--r--src/nix-store/nix-store.cc731
1 files changed, 731 insertions, 0 deletions
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
new file mode 100644
index 000000000..701e83974
--- /dev/null
+++ b/src/nix-store/nix-store.cc
@@ -0,0 +1,731 @@
+#include <iostream>
+#include <algorithm>
+
+#include "globals.hh"
+#include "misc.hh"
+#include "archive.hh"
+#include "shared.hh"
+#include "dotgraph.hh"
+#include "local-store.hh"
+#include "db.hh"
+#include "util.hh"
+#include "help.txt.hh"
+
+
+using namespace nix;
+using std::cin;
+using std::cout;
+
+
+typedef void (* Operation) (Strings opFlags, Strings opArgs);
+
+
+void printHelp()
+{
+ cout << string((char *) helpText, sizeof helpText);
+}
+
+
+static Path gcRoot;
+static int rootNr = 0;
+static bool indirectRoot = false;
+
+
+static Path fixPath(Path path)
+{
+ path = absPath(path);
+ while (!isInStore(path)) {
+ if (!isLink(path)) break;
+ string target = readLink(path);
+ path = absPath(target, dirOf(path));
+ }
+ return toStorePath(path);
+}
+
+
+static Path useDeriver(Path path)
+{
+ if (!isDerivation(path)) {
+ path = queryDeriver(noTxn, path);
+ if (path == "")
+ throw Error(format("deriver of path `%1%' is not known") % path);
+ }
+ return path;
+}
+
+
+/* Realisation the given path. For a derivation that means build it;
+ for other paths it means ensure their validity. */
+static Path realisePath(const Path & path)
+{
+ if (isDerivation(path)) {
+ PathSet paths;
+ paths.insert(path);
+ store->buildDerivations(paths);
+ Path outPath = findOutput(derivationFromPath(path), "out");
+
+ if (gcRoot == "")
+ printGCWarning();
+ else
+ outPath = addPermRoot(outPath,
+ makeRootName(gcRoot, rootNr),
+ indirectRoot);
+
+ return outPath;
+ } else {
+ store->ensurePath(path);
+ return path;
+ }
+}
+
+
+/* Realise the given paths. */
+static void opRealise(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ *i = fixPath(*i);
+
+ if (opArgs.size() > 1) {
+ PathSet drvPaths;
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ if (isDerivation(*i))
+ drvPaths.insert(*i);
+ store->buildDerivations(drvPaths);
+ }
+
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ cout << format("%1%\n") % realisePath(*i);
+}
+
+
+/* Add files to the Nix store and print the resulting paths. */
+static void opAdd(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+
+ for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
+ cout << format("%1%\n") % store->addToStore(*i);
+}
+
+
+/* Preload the output of a fixed-output derivation into the Nix
+ store. */
+static void opAddFixed(Strings opFlags, Strings opArgs)
+{
+ bool recursive = false;
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--recursive") recursive = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ if (opArgs.empty())
+ throw UsageError("first argument must be hash algorithm");
+
+ string hashAlgo = opArgs.front();
+ opArgs.pop_front();
+
+ for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
+ cout << format("%1%\n") % store->addToStore(*i, true, recursive, hashAlgo);
+}
+
+
+static Hash parseHash16or32(HashType ht, const string & s)
+{
+ return s.size() == Hash(ht).hashSize * 2
+ ? parseHash(ht, s)
+ : parseHash32(ht, s);
+}
+
+
+/* Hack to support caching in `nix-prefetch-url'. */
+static void opPrintFixedPath(Strings opFlags, Strings opArgs)
+{
+ bool recursive = false;
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--recursive") recursive = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ Strings::iterator i = opArgs.begin();
+ string hashAlgo = *i++;
+ string hash = *i++;
+ string name = *i++;
+
+ cout << format("%1%\n") %
+ makeFixedOutputPath(recursive, hashAlgo,
+ parseHash16or32(parseHashType(hashAlgo), hash), name);
+}
+
+
+/* Place in `paths' the set of paths that are required to `realise'
+ the given store path, i.e., all paths necessary for valid
+ deployment of the path. For a derivation, this is the union of
+ requisites of the inputs, plus the derivation; for other store
+ paths, it is the set of paths in the FS closure of the path. If
+ `includeOutputs' is true, include the requisites of the output
+ paths of derivations as well.
+
+ Note that this function can be used to implement three different
+ deployment policies:
+
+ - Source deployment (when called on a derivation).
+ - Binary deployment (when called on an output path).
+ - Source/binary deployment (when called on a derivation with
+ `includeOutputs' set to true).
+*/
+static void storePathRequisites(const Path & storePath,
+ bool includeOutputs, PathSet & paths)
+{
+ computeFSClosure(storePath, paths);
+
+ if (includeOutputs) {
+ for (PathSet::iterator i = paths.begin();
+ i != paths.end(); ++i)
+ if (isDerivation(*i)) {
+ Derivation drv = derivationFromPath(*i);
+ for (DerivationOutputs::iterator j = drv.outputs.begin();
+ j != drv.outputs.end(); ++j)
+ if (store->isValidPath(j->second.path))
+ computeFSClosure(j->second.path, paths);
+ }
+ }
+}
+
+
+static Path maybeUseOutput(const Path & storePath, bool useOutput, bool forceRealise)
+{
+ if (forceRealise) realisePath(storePath);
+ if (useOutput && isDerivation(storePath)) {
+ Derivation drv = derivationFromPath(storePath);
+ return findOutput(drv, "out");
+ }
+ else return storePath;
+}
+
+
+static void printPathSet(const PathSet & paths)
+{
+ for (PathSet::iterator i = paths.begin();
+ i != paths.end(); ++i)
+ cout << format("%s\n") % *i;
+}
+
+
+/* Some code to print a tree representation of a derivation dependency
+ graph. Topological sorting is used to keep the tree relatively
+ flat. */
+
+const string treeConn = "+---";
+const string treeLine = "| ";
+const string treeNull = " ";
+
+
+static void dfsVisit(const PathSet & paths, const Path & path,
+ PathSet & visited, Paths & sorted)
+{
+ if (visited.find(path) != visited.end()) return;
+ visited.insert(path);
+
+ PathSet closure;
+ computeFSClosure(path, closure);
+
+ for (PathSet::iterator i = closure.begin();
+ i != closure.end(); ++i)
+ if (*i != path && paths.find(*i) != paths.end())
+ dfsVisit(paths, *i, visited, sorted);
+
+ sorted.push_front(path);
+}
+
+
+static Paths topoSort(const PathSet & paths)
+{
+ Paths sorted;
+ PathSet visited;
+ for (PathSet::const_iterator i = paths.begin(); i != paths.end(); ++i)
+ dfsVisit(paths, *i, visited, sorted);
+ return sorted;
+}
+
+
+static void printTree(const Path & path,
+ const string & firstPad, const string & tailPad, PathSet & done)
+{
+ if (done.find(path) != done.end()) {
+ cout << format("%1%%2% [...]\n") % firstPad % path;
+ return;
+ }
+ done.insert(path);
+
+ cout << format("%1%%2%\n") % firstPad % path;
+
+ PathSet references;
+ store->queryReferences(path, references);
+
+#if 0
+ for (PathSet::iterator i = drv.inputSrcs.begin();
+ i != drv.inputSrcs.end(); ++i)
+ cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
+#endif
+
+ /* Topologically sort under the relation A < B iff A \in
+ closure(B). That is, if derivation A is an (possibly indirect)
+ input of B, then A is printed first. This has the effect of
+ flattening the tree, preventing deeply nested structures. */
+ Paths sorted = topoSort(references);
+ reverse(sorted.begin(), sorted.end());
+
+ for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) {
+ Paths::iterator j = i; ++j;
+ printTree(*i, tailPad + treeConn,
+ j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
+ done);
+ }
+}
+
+
+/* Perform various sorts of queries. */
+static void opQuery(Strings opFlags, Strings opArgs)
+{
+ enum { qOutputs, qRequisites, qReferences, qReferrers
+ , qReferrersClosure, qDeriver, qBinding, qHash
+ , qTree, qGraph } query = qOutputs;
+ bool useOutput = false;
+ bool includeOutputs = false;
+ bool forceRealise = false;
+ string bindingName;
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--outputs") query = qOutputs;
+ else if (*i == "--requisites" || *i == "-R") query = qRequisites;
+ else if (*i == "--references") query = qReferences;
+ else if (*i == "--referrers" || *i == "--referers") query = qReferrers;
+ else if (*i == "--referrers-closure" || *i == "--referers-closure") query = qReferrersClosure;
+ else if (*i == "--deriver" || *i == "-d") query = qDeriver;
+ else if (*i == "--binding" || *i == "-b") {
+ if (opArgs.size() == 0)
+ throw UsageError("expected binding name");
+ bindingName = opArgs.front();
+ opArgs.pop_front();
+ query = qBinding;
+ }
+ else if (*i == "--hash") query = qHash;
+ else if (*i == "--tree") query = qTree;
+ else if (*i == "--graph") query = qGraph;
+ else if (*i == "--use-output" || *i == "-u") useOutput = true;
+ else if (*i == "--force-realise" || *i == "-f") forceRealise = true;
+ else if (*i == "--include-outputs") includeOutputs = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ switch (query) {
+
+ case qOutputs: {
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ *i = fixPath(*i);
+ if (forceRealise) realisePath(*i);
+ Derivation drv = derivationFromPath(*i);
+ cout << format("%1%\n") % findOutput(drv, "out");
+ }
+ break;
+ }
+
+ case qRequisites:
+ case qReferences:
+ case qReferrers:
+ case qReferrersClosure: {
+ PathSet paths;
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
+ if (query == qRequisites)
+ storePathRequisites(path, includeOutputs, paths);
+ else if (query == qReferences) store->queryReferences(path, paths);
+ else if (query == qReferrers) store->queryReferrers(path, paths);
+ else if (query == qReferrersClosure) computeFSClosure(path, paths, true);
+ }
+ printPathSet(paths);
+ break;
+ }
+
+ case qDeriver:
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ Path deriver = queryDeriver(noTxn, fixPath(*i));
+ cout << format("%1%\n") %
+ (deriver == "" ? "unknown-deriver" : deriver);
+ }
+ break;
+
+ case qBinding:
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ Path path = useDeriver(fixPath(*i));
+ Derivation drv = derivationFromPath(path);
+ StringPairs::iterator j = drv.env.find(bindingName);
+ if (j == drv.env.end())
+ throw Error(format("derivation `%1%' has no environment binding named `%2%'")
+ % path % bindingName);
+ cout << format("%1%\n") % j->second;
+ }
+ break;
+
+ case qHash:
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ Path path = maybeUseOutput(fixPath(*i), useOutput, forceRealise);
+ Hash hash = store->queryPathHash(path);
+ assert(hash.type == htSHA256);
+ cout << format("sha256:%1%\n") % printHash32(hash);
+ }
+ break;
+
+ case qTree: {
+ PathSet done;
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ printTree(fixPath(*i), "", "", done);
+ break;
+ }
+
+ case qGraph: {
+ PathSet roots;
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ roots.insert(maybeUseOutput(fixPath(*i), useOutput, forceRealise));
+ printDotGraph(roots);
+ break;
+ }
+
+ default:
+ abort();
+ }
+}
+
+
+static void opReadLog(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ {
+ Path path = useDeriver(fixPath(*i));
+
+ Path logPath = (format("%1%/%2%/%3%") %
+ nixLogDir % drvsLogDir % baseNameOf(path)).str();
+
+ if (!pathExists(logPath))
+ throw Error(format("build log of derivation `%1%' is not available") % path);
+
+ /* !!! Make this run in O(1) memory. */
+ string log = readFile(logPath);
+ writeFull(STDOUT_FILENO, (const unsigned char *) log.c_str(), log.size());
+ }
+}
+
+
+static void opRegisterSubstitutes(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+ if (!opArgs.empty()) throw UsageError("no arguments expected");
+
+ Transaction txn;
+ createStoreTransaction(txn);
+
+ while (1) {
+ Path srcPath;
+ Substitute sub;
+ PathSet references;
+ getline(cin, srcPath);
+ if (cin.eof()) break;
+ getline(cin, sub.deriver);
+ getline(cin, sub.program);
+ string s; int n;
+ getline(cin, s);
+ if (!string2Int(s, n)) throw Error("number expected");
+ while (n--) {
+ getline(cin, s);
+ sub.args.push_back(s);
+ }
+ getline(cin, s);
+ if (!string2Int(s, n)) throw Error("number expected");
+ while (n--) {
+ getline(cin, s);
+ references.insert(s);
+ }
+ if (!cin || cin.eof()) throw Error("missing input");
+ registerSubstitute(txn, srcPath, sub);
+ setReferences(txn, srcPath, references);
+ }
+
+ txn.commit();
+}
+
+
+static void opClearSubstitutes(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+ if (!opArgs.empty())
+ throw UsageError("no arguments expected");
+
+ clearSubstitutes();
+}
+
+
+static void opRegisterValidity(Strings opFlags, Strings opArgs)
+{
+ bool reregister = false; // !!! maybe this should be the default
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--reregister") reregister = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ if (!opArgs.empty()) throw UsageError("no arguments expected");
+
+ ValidPathInfos infos;
+
+ while (1) {
+ ValidPathInfo info;
+ getline(cin, info.path);
+ if (cin.eof()) break;
+ getline(cin, info.deriver);
+ string s; int n;
+ getline(cin, s);
+ if (!string2Int(s, n)) throw Error("number expected");
+ while (n--) {
+ getline(cin, s);
+ info.references.insert(s);
+ }
+ if (!cin || cin.eof()) throw Error("missing input");
+ if (!store->isValidPath(info.path) || reregister) {
+ /* !!! races */
+ canonicalisePathMetaData(info.path);
+ info.hash = hashPath(htSHA256, info.path);
+ infos.push_back(info);
+ }
+ }
+
+ Transaction txn;
+ createStoreTransaction(txn);
+ registerValidPaths(txn, infos);
+ txn.commit();
+}
+
+
+static void opCheckValidity(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ if (!store->isValidPath(*i))
+ throw Error(format("path `%1%' is not valid") % *i);
+}
+
+
+struct PrintFreed
+{
+ bool show, dryRun;
+ unsigned long long bytesFreed;
+ PrintFreed(bool show, bool dryRun)
+ : show(show), dryRun(dryRun), bytesFreed(0) { }
+ ~PrintFreed()
+ {
+ if (show)
+ cout << format(
+ (dryRun
+ ? "%d bytes would be freed (%.2f MiB)\n"
+ : "%d bytes freed (%.2f MiB)\n"))
+ % bytesFreed % (bytesFreed / (1024.0 * 1024.0));
+ }
+};
+
+
+static void opGC(Strings opFlags, Strings opArgs)
+{
+ GCAction action = gcDeleteDead;
+
+ /* Do what? */
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--print-roots") action = gcReturnRoots;
+ else if (*i == "--print-live") action = gcReturnLive;
+ else if (*i == "--print-dead") action = gcReturnDead;
+ else if (*i == "--delete") action = gcDeleteDead;
+ else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
+
+ PathSet result;
+ PrintFreed freed(action == gcDeleteDead || action == gcReturnDead,
+ action == gcReturnDead);
+ store->collectGarbage(action, PathSet(), false, result, freed.bytesFreed);
+
+ if (action != gcDeleteDead) {
+ for (PathSet::iterator i = result.begin(); i != result.end(); ++i)
+ cout << *i << std::endl;
+ }
+}
+
+
+/* Remove paths from the Nix store if possible (i.e., if they do not
+ have any remaining referrers and are not reachable from any GC
+ roots). */
+static void opDelete(Strings opFlags, Strings opArgs)
+{
+ bool ignoreLiveness = false;
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--ignore-liveness") ignoreLiveness = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ PathSet pathsToDelete;
+ for (Strings::iterator i = opArgs.begin();
+ i != opArgs.end(); ++i)
+ pathsToDelete.insert(fixPath(*i));
+
+ PathSet dummy;
+ PrintFreed freed(true, false);
+ store->collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness,
+ dummy, freed.bytesFreed);
+}
+
+
+/* Dump a path as a Nix archive. The archive is written to standard
+ output. */
+static void opDump(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+ if (opArgs.size() != 1) throw UsageError("only one argument allowed");
+
+ FdSink sink(STDOUT_FILENO);
+ string path = *opArgs.begin();
+ dumpPath(path, sink);
+}
+
+
+/* Restore a value from a Nix archive. The archive is read from
+ standard input. */
+static void opRestore(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+ if (opArgs.size() != 1) throw UsageError("only one argument allowed");
+
+ FdSource source(STDIN_FILENO);
+ restorePath(*opArgs.begin(), source);
+}
+
+
+/* Initialise the Nix databases. */
+static void opInit(Strings opFlags, Strings opArgs)
+{
+ if (!opFlags.empty()) throw UsageError("unknown flag");
+ if (!opArgs.empty())
+ throw UsageError("no arguments expected");
+ /* Doesn't do anything right now; database tables are initialised
+ automatically. */
+}
+
+
+/* Verify the consistency of the Nix environment. */
+static void opVerify(Strings opFlags, Strings opArgs)
+{
+ if (!opArgs.empty())
+ throw UsageError("no arguments expected");
+
+ bool checkContents = false;
+
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--check-contents") checkContents = true;
+ else throw UsageError(format("unknown flag `%1%'") % *i);
+
+ verifyStore(checkContents);
+}
+
+
+/* Scan the arguments; find the operation, set global flags, put all
+ other flags in a list, and put all other arguments in another
+ list. */
+void run(Strings args)
+{
+ Strings opFlags, opArgs;
+ Operation op = 0;
+
+ for (Strings::iterator i = args.begin(); i != args.end(); ) {
+ string arg = *i++;
+
+ Operation oldOp = op;
+
+ if (arg == "--realise" || arg == "-r")
+ op = opRealise;
+ else if (arg == "--add" || arg == "-A")
+ op = opAdd;
+ else if (arg == "--add-fixed")
+ op = opAddFixed;
+ else if (arg == "--print-fixed-path")
+ op = opPrintFixedPath;
+ else if (arg == "--delete")
+ op = opDelete;
+ else if (arg == "--query" || arg == "-q")
+ op = opQuery;
+ else if (arg == "--read-log" || arg == "-l")
+ op = opReadLog;
+ else if (arg == "--register-substitutes")
+ op = opRegisterSubstitutes;
+ else if (arg == "--clear-substitutes")
+ op = opClearSubstitutes;
+ else if (arg == "--register-validity")
+ op = opRegisterValidity;
+ else if (arg == "--check-validity")
+ op = opCheckValidity;
+ else if (arg == "--gc")
+ op = opGC;
+ else if (arg == "--dump")
+ op = opDump;
+ else if (arg == "--restore")
+ op = opRestore;
+ else if (arg == "--init")
+ op = opInit;
+ else if (arg == "--verify")
+ op = opVerify;
+ else if (arg == "--add-root") {
+ if (i == args.end())
+ throw UsageError("`--add-root requires an argument");
+ gcRoot = absPath(*i++);
+ }
+ else if (arg == "--indirect")
+ indirectRoot = true;
+ else if (arg[0] == '-')
+ opFlags.push_back(arg);
+ else
+ opArgs.push_back(arg);
+
+ if (oldOp && oldOp != op)
+ throw UsageError("only one operation may be specified");
+ }
+
+ if (!op) throw UsageError("no operation specified");
+
+ if (op != opDump && op != opRestore) /* !!! hack */
+ store = openStore(op != opGC);
+
+ op(opFlags, opArgs);
+}
+
+
+string programId = "nix-store";