1 : #include "config.h"
2 :
3 : #include "shared.hh"
4 : #include "globals.hh"
5 : #include "store-api.hh"
6 : #include "util.hh"
7 : #include "misc.hh"
8 :
9 : #include <iostream>
10 : #include <cctype>
11 : #include <exception>
12 :
13 : #include <sys/stat.h>
14 : #include <unistd.h>
15 :
16 : #include <aterm2.h>
17 :
18 :
19 : namespace nix {
20 :
21 :
22 : volatile sig_atomic_t blockInt = 0;
23 :
24 :
25 0 : static void sigintHandler(int signo)
26 : {
27 0 : if (!blockInt) {
28 0 : _isInterrupted = 1;
29 0 : blockInt = 1;
30 : }
31 0 : }
32 :
33 :
34 18 : Path makeRootName(const Path & gcRoot, int & counter)
35 : {
36 18 : counter++;
37 18 : if (counter == 1)
38 18 : return gcRoot;
39 : else
40 0 : return (format("%1%-%2%") % gcRoot % counter).str();
41 : }
42 :
43 :
44 74 : void printGCWarning()
45 : {
46 : static bool haveWarned = false;
47 : warnOnce(haveWarned,
48 : "you did not specify `--add-root'; "
49 74 : "the result might be removed by the garbage collector");
50 74 : }
51 :
52 :
53 64 : void printMissing(const PathSet & paths)
54 : {
55 : unsigned long long downloadSize;
56 64 : PathSet willBuild, willSubstitute, unknown;
57 64 : queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize);
58 :
59 64 : if (!willBuild.empty()) {
60 44 : printMsg(lvlInfo, format("the following derivations will be built:"));
61 133 : foreach (PathSet::iterator, i, willBuild)
62 89 : printMsg(lvlInfo, format(" %1%") % *i);
63 : }
64 :
65 64 : if (!willSubstitute.empty()) {
66 6 : printMsg(lvlInfo, format("the following paths will be downloaded/copied (%.2f MiB):") %
67 : (downloadSize / (1024.0 * 1024.0)));
68 14 : foreach (PathSet::iterator, i, willSubstitute)
69 8 : printMsg(lvlInfo, format(" %1%") % *i);
70 : }
71 :
72 64 : if (!unknown.empty()) {
73 1 : printMsg(lvlInfo, format("don't know how to build the following paths%1%:")
74 : % (readOnlyMode ? " (may be caused by read-only store access)" : ""));
75 2 : foreach (PathSet::iterator, i, unknown)
76 1 : printMsg(lvlInfo, format(" %1%") % *i);
77 64 : }
78 64 : }
79 :
80 :
81 1 : static void setLogType(string lt)
82 : {
83 1 : if (lt == "pretty") logType = ltPretty;
84 1 : else if (lt == "escapes") logType = ltEscapes;
85 0 : else if (lt == "flat") logType = ltFlat;
86 0 : else throw UsageError("unknown log type");
87 1 : }
88 :
89 :
90 : unsigned long long getIntArg(const string & opt,
91 2 : Strings::iterator & i, const Strings::iterator & end)
92 : {
93 2 : ++i;
94 2 : if (i == end) throw UsageError(format("`%1%' requires an argument") % opt);
95 : long long n;
96 2 : if (!string2Int(*i, n) || n < 0)
97 0 : throw UsageError(format("`%1%' requires a non-negative integer") % opt);
98 2 : return n;
99 : }
100 :
101 :
102 : void initDerivationsHelpers();
103 :
104 :
105 0 : static void closeStore()
106 : {
107 : try {
108 0 : throw;
109 0 : } catch (std::exception & e) {
110 0 : printMsg(lvlError,
111 : format("FATAL: unexpected exception (closing store and aborting): %1%") % e.what());
112 : }
113 : try {
114 0 : store.reset((StoreAPI *) 0);
115 0 : } catch (...) {
116 0 : ignoreException();
117 : }
118 0 : abort();
119 : }
120 :
121 :
122 639 : RemoveTempRoots::~RemoveTempRoots()
123 : {
124 639 : removeTempRoots();
125 639 : }
126 :
127 :
128 : /* Initialize and reorder arguments, then call the actual argument
129 : processor. */
130 553 : static void initAndRun(int argc, char * * argv)
131 : {
132 : /* Setup Nix paths. */
133 553 : nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
134 1106 : nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
135 1106 : nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
136 1106 : nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
137 1106 : nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
138 1106 : nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
139 1106 : nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
140 1106 : nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
141 1106 : nixChrootsDir = canonPath(getEnv("NIX_CHROOTS_DIR", nixStateDir + "/chroots"));
142 :
143 1106 : string subs = getEnv("NIX_SUBSTITUTERS", "default");
144 1106 : if (subs == "default") {
145 549 : substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
146 549 : substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
147 : } else
148 4 : substituters = tokenizeString(subs, ":");
149 :
150 : /* Get some settings from the configuration file. */
151 553 : thisSystem = querySetting("system", SYSTEM);
152 1106 : maxBuildJobs = queryIntSetting("build-max-jobs", 1);
153 1106 : maxSilentTime = queryIntSetting("build-max-silent-time", 0);
154 :
155 : /* Catch SIGINT. */
156 : struct sigaction act, oact;
157 553 : act.sa_handler = sigintHandler;
158 553 : sigfillset(&act.sa_mask);
159 553 : act.sa_flags = 0;
160 553 : if (sigaction(SIGINT, &act, &oact))
161 0 : throw SysError("installing handler for SIGINT");
162 553 : if (sigaction(SIGTERM, &act, &oact))
163 0 : throw SysError("installing handler for SIGTERM");
164 553 : if (sigaction(SIGHUP, &act, &oact))
165 0 : throw SysError("installing handler for SIGHUP");
166 :
167 : /* Ignore SIGPIPE. */
168 553 : act.sa_handler = SIG_IGN;
169 553 : act.sa_flags = 0;
170 553 : if (sigaction(SIGPIPE, &act, &oact))
171 0 : throw SysError("ignoring SIGPIPE");
172 :
173 : /* There is no privacy in the Nix system ;-) At least not for
174 : now. In particular, store objects should be readable by
175 : everybody. This prevents nasty surprises when using a shared
176 : store (with the setuid() hack). */
177 553 : umask(0022);
178 :
179 : /* Process the NIX_LOG_TYPE environment variable. */
180 553 : string lt = getEnv("NIX_LOG_TYPE");
181 1106 : if (lt != "") setLogType(lt);
182 :
183 : /* ATerm stuff. !!! find a better place to put this */
184 553 : initDerivationsHelpers();
185 :
186 : /* Put the arguments in a vector. */
187 553 : Strings args, remaining;
188 2274 : while (argc--) args.push_back(*argv++);
189 553 : args.erase(args.begin());
190 :
191 : /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'), and
192 : ignore options for the ATerm library. */
193 2274 : for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
194 1721 : string arg = *i;
195 1721 : if (string(arg, 0, 4) == "-at-") ;
196 1721 : else if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
197 408 : for (unsigned int j = 1; j < arg.length(); j++)
198 156 : if (isalpha(arg[j]))
199 154 : remaining.push_back((string) "-" + arg[j]);
200 : else {
201 2 : remaining.push_back(string(arg, j));
202 2 : break;
203 : }
204 1671 : } else remaining.push_back(arg);
205 : }
206 553 : args = remaining;
207 553 : remaining.clear();
208 :
209 : /* Process default options. */
210 553 : for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
211 1824 : string arg = *i;
212 1824 : if (arg == "--verbose" || arg == "-v")
213 102 : verbosity = (Verbosity) ((int) verbosity + 1);
214 1722 : else if (arg == "--log-type") {
215 1 : ++i;
216 1 : if (i == args.end()) throw UsageError("`--log-type' requires an argument");
217 1 : setLogType(*i);
218 : }
219 1721 : else if (arg == "--build-output" || arg == "-B")
220 : ; /* !!! obsolete - remove eventually */
221 1721 : else if (arg == "--no-build-output" || arg == "-Q")
222 0 : buildVerbosity = lvlVomit;
223 1721 : else if (arg == "--print-build-trace")
224 0 : printBuildTrace = true;
225 1721 : else if (arg == "--help") {
226 4 : printHelp();
227 5 : return;
228 : }
229 1717 : else if (arg == "--version") {
230 1 : std::cout << format("%1% (Nix) %2%") % programId % NIX_VERSION << std::endl;
231 : return;
232 : }
233 1716 : else if (arg == "--keep-failed" || arg == "-K")
234 0 : keepFailed = true;
235 1716 : else if (arg == "--keep-going" || arg == "-k")
236 0 : keepGoing = true;
237 1716 : else if (arg == "--fallback")
238 2 : tryFallback = true;
239 1714 : else if (arg == "--max-jobs" || arg == "-j")
240 2 : maxBuildJobs = getIntArg(arg, i, args.end());
241 1712 : else if (arg == "--readonly-mode")
242 1 : readOnlyMode = true;
243 1711 : else if (arg == "--max-silent-time")
244 0 : maxSilentTime = getIntArg(arg, i, args.end());
245 1711 : else if (arg == "--no-build-hook")
246 1 : useBuildHook = false;
247 1710 : else remaining.push_back(arg);
248 : }
249 :
250 : /* Automatically clean up the temporary roots file when we
251 : exit. */
252 : RemoveTempRoots removeTempRoots __attribute__((unused));
253 :
254 : /* Make sure that the database gets closed properly, even if
255 : terminate() is called (which happens sometimes due to bugs in
256 : destructor/exceptions interaction, but that needn't preclude a
257 : clean shutdown of the database). */
258 548 : std::set_terminate(closeStore);
259 :
260 548 : run(remaining);
261 :
262 : /* Close the Nix database. */
263 492 : store.reset((StoreAPI *) 0);
264 : }
265 :
266 :
267 : bool setuidMode = false;
268 :
269 :
270 553 : static void setuidInit()
271 : {
272 : /* Don't do anything if this is not a setuid binary. */
273 553 : if (getuid() == geteuid() && getgid() == getegid()) return;
274 :
275 0 : uid_t nixUid = geteuid();
276 0 : gid_t nixGid = getegid();
277 :
278 0 : setuidCleanup();
279 :
280 : /* Don't trust the current directory. */
281 0 : if (chdir("/") == -1) abort();
282 :
283 : /* Set the real (and preferably also the save) uid/gid to the
284 : effective uid/gid. This matters mostly when we're not using
285 : build-users (bad!), since some builders (like Perl) complain
286 : when real != effective.
287 :
288 : On systems where setresuid is unavailable, we can't drop the
289 : saved uid/gid. This means that we could go back to the
290 : original real uid (i.e., the uid of the caller). That's not
291 : really a problem, except maybe when we execute a builder and
292 : we're not using build-users. In that case, the builder may be
293 : able to switch to the uid of the caller and possibly do bad
294 : stuff. But note that when not using build-users, the builder
295 : could also modify the Nix executables (say, replace them by a
296 : Trojan horse), so the problem is already there. */
297 :
298 : #if HAVE_SETRESUID
299 0 : if (setresuid(nixUid, nixUid, nixUid)) abort();
300 0 : if (setresgid(nixGid, nixGid, nixGid)) abort();
301 : #elif HAVE_SETREUID
302 : /* Note: doesn't set saved uid/gid! */
303 : fprintf(stderr, "warning: cannot set saved uid\n");
304 : if (setreuid(nixUid, nixUid)) abort();
305 : if (setregid(nixGid, nixGid)) abort();
306 : #else
307 : /* Note: doesn't set real and saved uid/gid! */
308 : fprintf(stderr, "warning: cannot set real and saved uids\n");
309 : if (setuid(nixUid)) abort();
310 : if (setgid(nixGid)) abort();
311 : #endif
312 :
313 0 : setuidMode = true;
314 : }
315 :
316 :
317 : }
318 :
319 :
320 : static char buf[1024];
321 :
322 553 : int main(int argc, char * * argv)
323 : {
324 : using namespace nix;
325 :
326 : /* If we're setuid, then we need to take some security precautions
327 : right away. */
328 553 : if (argc == 0) abort();
329 553 : setuidInit();
330 :
331 : /* ATerm setup. */
332 : ATerm bottomOfStack;
333 553 : ATinit(argc, argv, &bottomOfStack);
334 :
335 : /* Turn on buffering for cerr. */
336 : #if HAVE_PUBSETBUF
337 553 : std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf));
338 : #endif
339 :
340 553 : std::ios::sync_with_stdio(false);
341 :
342 : try {
343 : try {
344 553 : initAndRun(argc, argv);
345 110 : } catch (...) {
346 : /* Subtle: we have to make sure that any `interrupted'
347 : condition is discharged before we reach printMsg()
348 : below, since otherwise it will throw an (uncaught)
349 : exception. */
350 55 : blockInt = 1; /* ignore further SIGINTs */
351 55 : _isInterrupted = 0;
352 55 : throw;
353 : }
354 4 : } catch (UsageError & e) {
355 2 : printMsg(lvlError,
356 : format(
357 : "error: %1%\n"
358 : "Try `%2% --help' for more information.")
359 : % e.what() % programId);
360 4 : return 1;
361 106 : } catch (BaseError & e) {
362 53 : printMsg(lvlError, format("error: %1%") % e.msg());
363 106 : return 1;
364 0 : } catch (std::exception & e) {
365 0 : printMsg(lvlError, format("error: %1%") % e.what());
366 0 : return 1;
367 : }
368 :
369 497 : return 0;
370 1106 : }
371 553 :
372 :
|