1 #include "user-env.hh"
2 #include "util.hh"
3 #include "derivations.hh"
4 #include "store-api.hh"
5 #include "globals.hh"
6 #include "shared.hh"
7 #include "eval.hh"
8 #include "eval-inline.hh"
9 #include "profiles.hh"
10 
11 
12 namespace nix {
13 
14 
queryInstalled(EvalState & state,const Path & userEnv)15 DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
16 {
17     DrvInfos elems;
18     Path manifestFile = userEnv + "/manifest.nix";
19     if (pathExists(manifestFile)) {
20         Value v;
21         state.evalFile(manifestFile, v);
22         Bindings & bindings(*state.allocBindings(0));
23         getDerivations(state, v, "", bindings, elems, false);
24     }
25     return elems;
26 }
27 
28 
createUserEnv(EvalState & state,DrvInfos & elems,const Path & profile,bool keepDerivations,const string & lockToken)29 bool createUserEnv(EvalState & state, DrvInfos & elems,
30     const Path & profile, bool keepDerivations,
31     const string & lockToken)
32 {
33     /* Build the components in the user environment, if they don't
34        exist already. */
35     PathSet drvsToBuild;
36     for (auto & i : elems)
37         if (i.queryDrvPath() != "")
38             drvsToBuild.insert(i.queryDrvPath());
39 
40     debug(format("building user environment dependencies"));
41     state.store->buildPaths(drvsToBuild, state.repair ? bmRepair : bmNormal);
42 
43     /* Construct the whole top level derivation. */
44     PathSet references;
45     Value manifest;
46     state.mkList(manifest, elems.size());
47     unsigned int n = 0;
48     for (auto & i : elems) {
49         /* Create a pseudo-derivation containing the name, system,
50            output paths, and optionally the derivation path, as well
51            as the meta attributes. */
52         Path drvPath = keepDerivations ? i.queryDrvPath() : "";
53 
54         Value & v(*state.allocValue());
55         manifest.listElems()[n++] = &v;
56         state.mkAttrs(v, 16);
57 
58         mkString(*state.allocAttr(v, state.sType), "derivation");
59         mkString(*state.allocAttr(v, state.sName), i.queryName());
60         auto system = i.querySystem();
61         if (!system.empty())
62             mkString(*state.allocAttr(v, state.sSystem), system);
63         mkString(*state.allocAttr(v, state.sOutPath), i.queryOutPath());
64         if (drvPath != "")
65             mkString(*state.allocAttr(v, state.sDrvPath), i.queryDrvPath());
66 
67         // Copy each output meant for installation.
68         DrvInfo::Outputs outputs = i.queryOutputs(true);
69         Value & vOutputs = *state.allocAttr(v, state.sOutputs);
70         state.mkList(vOutputs, outputs.size());
71         unsigned int m = 0;
72         for (auto & j : outputs) {
73             mkString(*(vOutputs.listElems()[m++] = state.allocValue()), j.first);
74             Value & vOutputs = *state.allocAttr(v, state.symbols.create(j.first));
75             state.mkAttrs(vOutputs, 2);
76             mkString(*state.allocAttr(vOutputs, state.sOutPath), j.second);
77 
78             /* This is only necessary when installing store paths, e.g.,
79                `nix-env -i /nix/store/abcd...-foo'. */
80             state.store->addTempRoot(j.second);
81             state.store->ensurePath(j.second);
82 
83             references.insert(j.second);
84         }
85 
86         // Copy the meta attributes.
87         Value & vMeta = *state.allocAttr(v, state.sMeta);
88         state.mkAttrs(vMeta, 16);
89         StringSet metaNames = i.queryMetaNames();
90         for (auto & j : metaNames) {
91             Value * v = i.queryMeta(j);
92             if (!v) continue;
93             vMeta.attrs->push_back(Attr(state.symbols.create(j), v));
94         }
95         vMeta.attrs->sort();
96         v.attrs->sort();
97 
98         if (drvPath != "") references.insert(drvPath);
99     }
100 
101     /* Also write a copy of the list of user environment elements to
102        the store; we need it for future modifications of the
103        environment. */
104     Path manifestFile = state.store->addTextToStore("env-manifest.nix",
105         (format("%1%") % manifest).str(), references);
106 
107     /* Get the environment builder expression. */
108     Value envBuilder;
109     state.evalFile(state.findFile("nix/buildenv.nix"), envBuilder);
110 
111     /* Construct a Nix expression that calls the user environment
112        builder with the manifest as argument. */
113     Value args, topLevel;
114     state.mkAttrs(args, 3);
115     mkString(*state.allocAttr(args, state.symbols.create("manifest")),
116         manifestFile, {manifestFile});
117     args.attrs->push_back(Attr(state.symbols.create("derivations"), &manifest));
118     args.attrs->sort();
119     mkApp(topLevel, envBuilder, args);
120 
121     /* Evaluate it. */
122     debug("evaluating user environment builder");
123     state.forceValue(topLevel);
124     PathSet context;
125     Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
126     Path topLevelDrv = state.coerceToPath(aDrvPath.pos ? *(aDrvPath.pos) : noPos, *(aDrvPath.value), context);
127     Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
128     Path topLevelOut = state.coerceToPath(aOutPath.pos ? *(aOutPath.pos) : noPos, *(aOutPath.value), context);
129 
130     /* Realise the resulting store expression. */
131     debug("building user environment");
132     state.store->buildPaths({topLevelDrv}, state.repair ? bmRepair : bmNormal);
133 
134     /* Switch the current user environment to the output path. */
135     auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
136 
137     if (store2) {
138         PathLocks lock;
139         lockProfile(lock, profile);
140 
141         Path lockTokenCur = optimisticLockProfile(profile);
142         if (lockToken != lockTokenCur) {
143             printError(format("profile '%1%' changed while we were busy; restarting") % profile);
144             return false;
145         }
146 
147         debug(format("switching to new user environment"));
148         Path generation = createGeneration(ref<LocalFSStore>(store2), profile, topLevelOut);
149         switchLink(profile, generation);
150     }
151 
152     return true;
153 }
154 
155 
156 }
157