gnu: kdenlive: Add ffmpeg to input list
[jackhill/guix/guix.git] / nix / nix-daemon / nix-daemon.cc
CommitLineData
82c4ad96 1#include "config.h"
36457566
LC
2#include "shared.hh"
3#include "local-store.hh"
4#include "util.hh"
5#include "serialise.hh"
6#include "worker-protocol.hh"
7#include "archive.hh"
8#include "affinity.hh"
9#include "globals.hh"
f9aefa2d 10#include "builtins.hh"
36457566 11
2bb04905
LC
12#include <algorithm>
13
36457566
LC
14#include <cstring>
15#include <unistd.h>
16#include <signal.h>
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <sys/stat.h>
20#include <sys/socket.h>
21#include <sys/un.h>
1071f781 22#include <arpa/inet.h>
6efb578a
LC
23#include <netinet/in.h>
24#include <netinet/tcp.h>
25
36457566
LC
26#include <fcntl.h>
27#include <errno.h>
2bb04905
LC
28#include <pwd.h>
29#include <grp.h>
36457566
LC
30
31using namespace nix;
32
33
34/* On platforms that have O_ASYNC, we can detect when a client
35 disconnects and immediately kill any ongoing builds. On platforms
36 that lack it, we only notice the disconnection the next time we try
37 to write to the client. So if you have a builder that never
38 generates output on stdout/stderr, the daemon will never notice
39 that the client has disconnected until the builder terminates.
40
41 GNU/Hurd does have O_ASYNC, but its Unix-domain socket translator
42 (pflocal) does not implement F_SETOWN. See
43 <http://lists.gnu.org/archive/html/bug-guix/2013-07/msg00021.html> for
44 details.*/
45#if defined O_ASYNC && !defined __GNU__
46#define HAVE_HUP_NOTIFICATION
47#ifndef SIGPOLL
48#define SIGPOLL SIGIO
49#endif
50#endif
51
52
53static FdSource from(STDIN_FILENO);
54static FdSink to(STDOUT_FILENO);
55
56bool canSendStderr;
36457566 57
5cefb13d
RJ
58/* This variable is used to keep track of whether a connection
59 comes from a host other than the host running guix-daemon. */
60static bool isRemoteConnection;
36457566
LC
61
62/* This function is called anytime we want to write something to
63 stderr. If we're in a state where the protocol allows it (i.e.,
64 when canSendStderr), send the message to the client over the
65 socket. */
66static void tunnelStderr(const unsigned char * buf, size_t count)
67{
2bb04905 68 if (canSendStderr) {
36457566
LC
69 try {
70 writeInt(STDERR_NEXT, to);
71 writeString(buf, count, to);
72 to.flush();
73 } catch (...) {
74 /* Write failed; that means that the other side is
75 gone. */
76 canSendStderr = false;
77 throw;
78 }
79 } else
80 writeFull(STDERR_FILENO, buf, count);
81}
82
83
84/* Return true if the remote side has closed its end of the
85 connection, false otherwise. Should not be called on any socket on
86 which we expect input! */
87static bool isFarSideClosed(int socket)
88{
89 struct timeval timeout;
90 timeout.tv_sec = timeout.tv_usec = 0;
91
92 fd_set fds;
93 FD_ZERO(&fds);
94 FD_SET(socket, &fds);
95
96 while (select(socket + 1, &fds, 0, 0, &timeout) == -1)
97 if (errno != EINTR) throw SysError("select()");
98
99 if (!FD_ISSET(socket, &fds)) return false;
100
101 /* Destructive read to determine whether the select() marked the
102 socket as readable because there is actual input or because
103 we've reached EOF (i.e., a read of size 0 is available). */
104 char c;
105 int rd;
106 if ((rd = read(socket, &c, 1)) > 0)
107 throw Error("EOF expected (protocol error?)");
108 else if (rd == -1 && errno != ECONNRESET)
109 throw SysError("expected connection reset or EOF");
110
111 return true;
112}
113
114
115/* A SIGPOLL signal is received when data is available on the client
116 communication socket, or when the client has closed its side of the
117 socket. This handler is enabled at precisely those moments in the
118 protocol when we're doing work and the client is supposed to be
119 quiet. Thus, if we get a SIGPOLL signal, it means that the client
120 has quit. So we should quit as well.
121
122 Too bad most operating systems don't support the POLL_HUP value for
123 si_code in siginfo_t. That would make most of the SIGPOLL
124 complexity unnecessary, i.e., we could just enable SIGPOLL all the
125 time and wouldn't have to worry about races. */
126static void sigPollHandler(int sigNo)
127{
128 using namespace std;
129 try {
130 /* Check that the far side actually closed. We're still
131 getting spurious signals every once in a while. I.e.,
132 there is no input available, but we get a signal with
133 POLL_IN set. Maybe it's delayed or something. */
134 if (isFarSideClosed(from.fd)) {
135 if (!blockInt) {
136 _isInterrupted = 1;
137 blockInt = 1;
138 canSendStderr = false;
139 const char * s = "SIGPOLL\n";
140 write(STDERR_FILENO, s, strlen(s));
141 }
142 } else {
143 const char * s = "spurious SIGPOLL\n";
144 write(STDERR_FILENO, s, strlen(s));
145 }
146 }
147 catch (Error & e) {
148 /* Shouldn't happen. */
149 string s = "impossible: " + e.msg() + '\n';
150 write(STDERR_FILENO, s.data(), s.size());
151 throw;
152 }
153}
154
155
156static void setSigPollAction(bool enable)
157{
158#ifdef HAVE_HUP_NOTIFICATION
159 struct sigaction act, oact;
160 act.sa_handler = enable ? sigPollHandler : SIG_IGN;
161 sigfillset(&act.sa_mask);
162 act.sa_flags = 0;
163 if (sigaction(SIGPOLL, &act, &oact))
164 throw SysError("setting handler for SIGPOLL");
165#endif
166}
167
168
169/* startWork() means that we're starting an operation for which we
170 want to send out stderr to the client. */
171static void startWork()
172{
173 canSendStderr = true;
174
175 /* Handle client death asynchronously. */
176 setSigPollAction(true);
177
178 /* Of course, there is a race condition here: the socket could
179 have closed between when we last read from / wrote to it, and
180 between the time we set the handler for SIGPOLL. In that case
181 we won't get the signal. So do a non-blocking select() to find
182 out if any input is available on the socket. If there is, it
183 has to be the 0-byte read that indicates that the socket has
184 closed. */
185 if (isFarSideClosed(from.fd)) {
186 _isInterrupted = 1;
187 checkInterrupt();
188 }
189}
190
191
192/* stopWork() means that we're done; stop sending stderr to the
193 client. */
194static void stopWork(bool success = true, const string & msg = "", unsigned int status = 0)
195{
196 /* Stop handling async client death; we're going to a state where
197 we're either sending or receiving from the client, so we'll be
198 notified of client death anyway. */
199 setSigPollAction(false);
200
201 canSendStderr = false;
202
203 if (success)
204 writeInt(STDERR_LAST, to);
205 else {
206 writeInt(STDERR_ERROR, to);
207 writeString(msg, to);
208 if (status != 0) writeInt(status, to);
209 }
210}
211
212
9a8f9f84 213struct TunnelSink : BufferedSink
36457566
LC
214{
215 Sink & to;
9a8f9f84
LC
216 TunnelSink(Sink & to) : BufferedSink(64 * 1024), to(to) { }
217 virtual void write(const unsigned char * data, size_t len)
36457566
LC
218 {
219 writeInt(STDERR_WRITE, to);
220 writeString(data, len, to);
221 }
222};
223
224
225struct TunnelSource : BufferedSource
226{
227 Source & from;
228 TunnelSource(Source & from) : from(from) { }
229 size_t readUnbuffered(unsigned char * data, size_t len)
230 {
231 /* Careful: we're going to receive data from the client now,
232 so we have to disable the SIGPOLL handler. */
233 setSigPollAction(false);
234 canSendStderr = false;
235
236 writeInt(STDERR_READ, to);
237 writeInt(len, to);
238 to.flush();
239 size_t n = readString(data, len, from);
240
241 startWork();
242 if (n == 0) throw EndOfFile("unexpected end-of-file");
243 return n;
244 }
245};
246
247
248/* If the NAR archive contains a single file at top-level, then save
249 the contents of the file to `s'. Otherwise barf. */
250struct RetrieveRegularNARSink : ParseSink
251{
252 bool regular;
253 string s;
254
255 RetrieveRegularNARSink() : regular(true) { }
256
257 void createDirectory(const Path & path)
258 {
259 regular = false;
260 }
261
262 void receiveContents(unsigned char * data, unsigned int len)
263 {
264 s.append((const char *) data, len);
265 }
266
267 void createSymlink(const Path & path, const string & target)
268 {
269 regular = false;
270 }
271};
272
273
274/* Adapter class of a Source that saves all data read to `s'. */
275struct SavingSourceAdapter : Source
276{
277 Source & orig;
278 string s;
279 SavingSourceAdapter(Source & orig) : orig(orig) { }
280 size_t read(unsigned char * data, size_t len)
281 {
282 size_t n = orig.read(data, len);
283 s.append((const char *) data, n);
284 return n;
285 }
286};
287
288
289static void performOp(bool trusted, unsigned int clientVersion,
290 Source & from, Sink & to, unsigned int op)
291{
292 switch (op) {
293
36457566
LC
294 case wopIsValidPath: {
295 /* 'readStorePath' could raise an error leading to the connection
296 being closed. To be able to recover from an invalid path error,
297 call 'startWork' early, and do 'assertStorePath' afterwards so
298 that the 'Error' exception handler doesn't close the
299 connection. */
300 Path path = readString(from);
301 startWork();
302 assertStorePath(path);
303 bool result = store->isValidPath(path);
304 stopWork();
305 writeInt(result, to);
306 break;
307 }
308
309 case wopQueryValidPaths: {
310 PathSet paths = readStorePaths<PathSet>(from);
311 startWork();
312 PathSet res = store->queryValidPaths(paths);
313 stopWork();
314 writeStrings(res, to);
315 break;
316 }
317
318 case wopHasSubstitutes: {
319 Path path = readStorePath(from);
320 startWork();
321 PathSet res = store->querySubstitutablePaths(singleton<PathSet>(path));
322 stopWork();
323 writeInt(res.find(path) != res.end(), to);
324 break;
325 }
326
327 case wopQuerySubstitutablePaths: {
328 PathSet paths = readStorePaths<PathSet>(from);
329 startWork();
330 PathSet res = store->querySubstitutablePaths(paths);
331 stopWork();
332 writeStrings(res, to);
333 break;
334 }
335
336 case wopQueryPathHash: {
337 Path path = readStorePath(from);
338 startWork();
339 Hash hash = store->queryPathHash(path);
340 stopWork();
341 writeString(printHash(hash), to);
342 break;
343 }
344
345 case wopQueryReferences:
346 case wopQueryReferrers:
347 case wopQueryValidDerivers:
348 case wopQueryDerivationOutputs: {
349 Path path = readStorePath(from);
350 startWork();
351 PathSet paths;
352 if (op == wopQueryReferences)
353 store->queryReferences(path, paths);
354 else if (op == wopQueryReferrers)
355 store->queryReferrers(path, paths);
356 else if (op == wopQueryValidDerivers)
357 paths = store->queryValidDerivers(path);
358 else paths = store->queryDerivationOutputs(path);
359 stopWork();
360 writeStrings(paths, to);
361 break;
362 }
363
364 case wopQueryDerivationOutputNames: {
365 Path path = readStorePath(from);
366 startWork();
367 StringSet names;
368 names = store->queryDerivationOutputNames(path);
369 stopWork();
370 writeStrings(names, to);
371 break;
372 }
373
374 case wopQueryDeriver: {
375 Path path = readStorePath(from);
376 startWork();
377 Path deriver = store->queryDeriver(path);
378 stopWork();
379 writeString(deriver, to);
380 break;
381 }
382
383 case wopQueryPathFromHashPart: {
384 string hashPart = readString(from);
385 startWork();
386 Path path = store->queryPathFromHashPart(hashPart);
387 stopWork();
388 writeString(path, to);
389 break;
390 }
391
392 case wopAddToStore: {
393 string baseName = readString(from);
394 bool fixed = readInt(from) == 1; /* obsolete */
395 bool recursive = readInt(from) == 1;
396 string s = readString(from);
397 /* Compatibility hack. */
398 if (!fixed) {
399 s = "sha256";
400 recursive = true;
401 }
402 HashType hashAlgo = parseHashType(s);
403
404 SavingSourceAdapter savedNAR(from);
405 RetrieveRegularNARSink savedRegular;
406
407 if (recursive) {
408 /* Get the entire NAR dump from the client and save it to
409 a string so that we can pass it to
410 addToStoreFromDump(). */
411 ParseSink sink; /* null sink; just parse the NAR */
412 parseDump(sink, savedNAR);
413 } else
414 parseDump(savedRegular, from);
415
416 startWork();
417 if (!savedRegular.regular) throw Error("regular file expected");
418 Path path = dynamic_cast<LocalStore *>(store.get())
419 ->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
420 stopWork();
421
422 writeString(path, to);
423 break;
424 }
425
426 case wopAddTextToStore: {
427 string suffix = readString(from);
428 string s = readString(from);
429 PathSet refs = readStorePaths<PathSet>(from);
430 startWork();
431 Path path = store->addTextToStore(suffix, s, refs);
432 stopWork();
433 writeString(path, to);
434 break;
435 }
436
437 case wopExportPath: {
438 Path path = readStorePath(from);
439 bool sign = readInt(from) == 1;
440 startWork();
441 TunnelSink sink(to);
2e009ae7
JN
442 try {
443 store->exportPath(path, sign, sink);
444 }
445 catch (Error &e) {
446 /* Flush SINK beforehand or its destructor will rightfully trigger
447 an assertion failure. */
448 sink.flush();
449 throw e;
450 }
9a8f9f84 451 sink.flush();
36457566
LC
452 stopWork();
453 writeInt(1, to);
454 break;
455 }
456
457 case wopImportPaths: {
458 startWork();
459 TunnelSource source(from);
ef80ca96 460
79aa1a83
ED
461 /* Unlike Nix, always require a signature, even for "trusted"
462 users. */
ef80ca96 463 Paths paths = store->importPaths(true, source);
36457566
LC
464 stopWork();
465 writeStrings(paths, to);
466 break;
467 }
468
469 case wopBuildPaths: {
470 PathSet drvs = readStorePaths<PathSet>(from);
708d9070
LC
471 BuildMode mode = bmNormal;
472 if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
473 mode = (BuildMode)readInt(from);
474
475 /* Repairing is not atomic, so disallowed for "untrusted"
476 clients. */
477 if (mode == bmRepair && !trusted)
8327e733 478 throw Error("repairing is a privileged operation");
708d9070 479 }
36457566 480 startWork();
708d9070 481 store->buildPaths(drvs, mode);
36457566
LC
482 stopWork();
483 writeInt(1, to);
484 break;
485 }
486
487 case wopEnsurePath: {
488 Path path = readStorePath(from);
489 startWork();
490 store->ensurePath(path);
491 stopWork();
492 writeInt(1, to);
493 break;
494 }
495
496 case wopAddTempRoot: {
497 Path path = readStorePath(from);
498 startWork();
499 store->addTempRoot(path);
500 stopWork();
501 writeInt(1, to);
502 break;
503 }
504
505 case wopAddIndirectRoot: {
506 Path path = absPath(readString(from));
507 startWork();
508 store->addIndirectRoot(path);
509 stopWork();
510 writeInt(1, to);
511 break;
512 }
513
514 case wopSyncWithGC: {
515 startWork();
516 store->syncWithGC();
517 stopWork();
518 writeInt(1, to);
519 break;
520 }
521
522 case wopFindRoots: {
523 startWork();
524 Roots roots = store->findRoots();
525 stopWork();
526 writeInt(roots.size(), to);
527 for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
528 writeString(i->first, to);
529 writeString(i->second, to);
530 }
531 break;
532 }
533
534 case wopCollectGarbage: {
5cefb13d
RJ
535 if (isRemoteConnection) {
536 throw Error("Garbage collection is disabled for remote hosts.");
537 break;
538 }
539
36457566
LC
540 GCOptions options;
541 options.action = (GCOptions::GCAction) readInt(from);
542 options.pathsToDelete = readStorePaths<PathSet>(from);
543 options.ignoreLiveness = readInt(from);
544 options.maxFreed = readLongLong(from);
545 readInt(from); // obsolete field
546 if (GET_PROTOCOL_MINOR(clientVersion) >= 5) {
547 /* removed options */
548 readInt(from);
549 readInt(from);
550 }
551
552 GCResults results;
553
554 startWork();
555 if (options.ignoreLiveness)
556 throw Error("you are not allowed to ignore liveness");
557 store->collectGarbage(options, results);
558 stopWork();
559
560 writeStrings(results.paths, to);
561 writeLongLong(results.bytesFreed, to);
562 writeLongLong(0, to); // obsolete
563
564 break;
565 }
566
567 case wopSetOptions: {
568 settings.keepFailed = readInt(from) != 0;
bb640d61
LC
569 if (isRemoteConnection)
570 /* When the client is remote, don't keep the failed build tree as
571 it is presumably inaccessible to the client and could fill up
572 our disk. */
573 settings.keepFailed = 0;
574
36457566
LC
575 settings.keepGoing = readInt(from) != 0;
576 settings.set("build-fallback", readInt(from) ? "true" : "false");
577 verbosity = (Verbosity) readInt(from);
deac976d
LC
578
579 if (GET_PROTOCOL_MINOR(clientVersion) < 0x61) {
580 settings.set("build-max-jobs", std::to_string(readInt(from)));
581 settings.set("build-max-silent-time", std::to_string(readInt(from)));
582 }
583
bc69ea2d
LC
584 if (GET_PROTOCOL_MINOR(clientVersion) >= 2) {
585#ifdef HAVE_DAEMON_OFFLOAD_HOOK
36457566 586 settings.useBuildHook = readInt(from) != 0;
bc69ea2d
LC
587#else
588 readInt(from); // ignore the user's setting
589#endif
590 }
591
36457566
LC
592 if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
593 settings.buildVerbosity = (Verbosity) readInt(from);
594 logType = (LogType) readInt(from);
595 settings.printBuildTrace = readInt(from) != 0;
596 }
deac976d
LC
597 if (GET_PROTOCOL_MINOR(clientVersion) >= 6
598 && GET_PROTOCOL_MINOR(clientVersion) < 0x61)
79aa1a83 599 settings.set("build-cores", std::to_string(readInt(from)));
f6919ebd
LC
600 if (GET_PROTOCOL_MINOR(clientVersion) >= 10) {
601 if (settings.useSubstitutes)
602 settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
603 else
604 readInt(from); // substitutes remain disabled
605 }
36457566
LC
606 if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
607 unsigned int n = readInt(from);
608 for (unsigned int i = 0; i < n; i++) {
609 string name = readString(from);
610 string value = readString(from);
deac976d
LC
611 if (name == "build-timeout" || name == "build-max-silent-time"
612 || name == "build-max-jobs" || name == "build-cores"
613 || name == "build-repeat"
6ef61cc4 614 || name == "multiplexed-build-output")
36457566 615 settings.set(name, value);
81c580c8
LC
616 else if (name == "user-name"
617 && settings.clientUid == (uid_t) -1) {
618 /* Create the user profile. This is necessary if
619 clientUid = -1, for instance because the client
620 connected over TCP. */
621 struct passwd *pw = getpwnam(value.c_str());
622 if (pw != NULL)
623 store->createUser(value, pw->pw_uid);
624 else
625 printMsg(lvlInfo, format("user name %1% not found") % value);
626 }
36457566
LC
627 else
628 settings.set(trusted ? name : "untrusted-" + name, value);
629 }
630 }
631 settings.update();
632 startWork();
633 stopWork();
634 break;
635 }
636
637 case wopQuerySubstitutablePathInfo: {
638 Path path = absPath(readString(from));
639 startWork();
640 SubstitutablePathInfos infos;
641 store->querySubstitutablePathInfos(singleton<PathSet>(path), infos);
642 stopWork();
643 SubstitutablePathInfos::iterator i = infos.find(path);
644 if (i == infos.end())
645 writeInt(0, to);
646 else {
647 writeInt(1, to);
648 writeString(i->second.deriver, to);
649 writeStrings(i->second.references, to);
650 writeLongLong(i->second.downloadSize, to);
651 if (GET_PROTOCOL_MINOR(clientVersion) >= 7)
652 writeLongLong(i->second.narSize, to);
653 }
654 break;
655 }
656
657 case wopQuerySubstitutablePathInfos: {
658 PathSet paths = readStorePaths<PathSet>(from);
659 startWork();
660 SubstitutablePathInfos infos;
661 store->querySubstitutablePathInfos(paths, infos);
662 stopWork();
663 writeInt(infos.size(), to);
664 foreach (SubstitutablePathInfos::iterator, i, infos) {
665 writeString(i->first, to);
666 writeString(i->second.deriver, to);
667 writeStrings(i->second.references, to);
668 writeLongLong(i->second.downloadSize, to);
669 writeLongLong(i->second.narSize, to);
670 }
671 break;
672 }
673
674 case wopQueryAllValidPaths: {
675 startWork();
676 PathSet paths = store->queryAllValidPaths();
677 stopWork();
678 writeStrings(paths, to);
679 break;
680 }
681
682 case wopQueryFailedPaths: {
683 startWork();
684 PathSet paths = store->queryFailedPaths();
685 stopWork();
686 writeStrings(paths, to);
687 break;
688 }
689
690 case wopClearFailedPaths: {
691 PathSet paths = readStrings<PathSet>(from);
692 startWork();
693 store->clearFailedPaths(paths);
694 stopWork();
695 writeInt(1, to);
696 break;
697 }
698
699 case wopQueryPathInfo: {
700 Path path = readStorePath(from);
701 startWork();
702 ValidPathInfo info = store->queryPathInfo(path);
703 stopWork();
704 writeString(info.deriver, to);
705 writeString(printHash(info.hash), to);
706 writeStrings(info.references, to);
707 writeInt(info.registrationTime, to);
708 writeLongLong(info.narSize, to);
709 break;
710 }
711
2bb04905 712 case wopOptimiseStore:
54c260e6
LC
713 startWork();
714 store->optimiseStore();
715 stopWork();
716 writeInt(1, to);
717 break;
718
719 case wopVerifyStore: {
720 bool checkContents = readInt(from) != 0;
721 bool repair = readInt(from) != 0;
722 startWork();
723 if (repair && !trusted)
724 throw Error("you are not privileged to repair paths");
725 bool errors = store->verifyStore(checkContents, repair);
726 stopWork();
727 writeInt(errors, to);
728 break;
729 }
2bb04905 730
f9aefa2d
LC
731 case wopBuiltinBuilders: {
732 startWork();
733 auto names = builtinBuilderNames();
734 stopWork();
735 writeStrings(names, to);
736 break;
737 }
738
36457566
LC
739 default:
740 throw Error(format("invalid operation %1%") % op);
741 }
742}
743
744
81c580c8 745static void processConnection(bool trusted, uid_t userId)
36457566
LC
746{
747 canSendStderr = false;
36457566
LC
748 _writeToStderr = tunnelStderr;
749
750#ifdef HAVE_HUP_NOTIFICATION
751 /* Allow us to receive SIGPOLL for events on the client socket. */
752 setSigPollAction(false);
753 if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
754 throw SysError("F_SETOWN");
755 if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | O_ASYNC) == -1)
756 throw SysError("F_SETFL");
757#endif
758
759 /* Exchange the greeting. */
760 unsigned int magic = readInt(from);
761 if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
762 writeInt(WORKER_MAGIC_2, to);
763 writeInt(PROTOCOL_VERSION, to);
764 to.flush();
765 unsigned int clientVersion = readInt(from);
766
767 if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
768 setAffinityTo(readInt(from));
769
770 bool reserveSpace = true;
771 if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
772 reserveSpace = readInt(from) != 0;
773
774 /* Send startup error messages to the client. */
775 startWork();
776
777 try {
778
779 /* If we can't accept clientVersion, then throw an error
780 *here* (not above). */
781
782#if 0
783 /* Prevent users from doing something very dangerous. */
784 if (geteuid() == 0 &&
785 querySetting("build-users-group", "") == "")
786 throw Error("if you run `nix-daemon' as root, then you MUST set `build-users-group'!");
787#endif
788
789 /* Open the store. */
790 store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
791
81c580c8
LC
792 if (userId != (uid_t) -1) {
793 /* Create the user profile. */
794 struct passwd *pw = getpwuid(userId);
795 if (pw != NULL && pw->pw_name != NULL)
796 store->createUser(pw->pw_name, userId);
797 else
798 printMsg(lvlInfo, format("user with UID %1% not found") % userId);
799 }
800
36457566
LC
801 stopWork();
802 to.flush();
803
804 } catch (Error & e) {
2bb04905 805 stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
36457566
LC
806 to.flush();
807 return;
808 }
809
810 /* Process client requests. */
811 unsigned int opCount = 0;
812
813 while (true) {
814 WorkerOp op;
815 try {
816 op = (WorkerOp) readInt(from);
817 } catch (EndOfFile & e) {
818 break;
819 }
820
821 opCount++;
822
823 try {
824 performOp(trusted, clientVersion, from, to, op);
825 } catch (Error & e) {
826 /* If we're not in a state where we can send replies, then
827 something went wrong processing the input of the
828 client. This can happen especially if I/O errors occur
829 during addTextToStore() / importPath(). If that
830 happens, just send the error message and exit. */
831 bool errorAllowed = canSendStderr;
36457566 832 stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
2bb04905 833 if (!errorAllowed) throw;
36457566 834 } catch (std::bad_alloc & e) {
8327e733 835 stopWork(false, "build daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
36457566
LC
836 throw;
837 }
838
839 to.flush();
840
841 assert(!canSendStderr);
842 };
843
54c260e6
LC
844 canSendStderr = false;
845 _isInterrupted = false;
2bb04905 846 printMsg(lvlDebug, format("%1% operations") % opCount);
36457566
LC
847}
848
849
850static void sigChldHandler(int sigNo)
851{
852 /* Reap all dead children. */
853 while (waitpid(-1, 0, WNOHANG) > 0) ;
854}
855
856
857static void setSigChldAction(bool autoReap)
858{
859 struct sigaction act, oact;
860 act.sa_handler = autoReap ? sigChldHandler : SIG_DFL;
861 sigfillset(&act.sa_mask);
862 act.sa_flags = 0;
863 if (sigaction(SIGCHLD, &act, &oact))
864 throw SysError("setting SIGCHLD handler");
865}
866
867
1071f781
LC
868/* Accept a connection on FDSOCKET and fork a server process to process the
869 new connection. */
870static void acceptConnection(int fdSocket)
2bb04905 871{
1071f781
LC
872 uid_t clientUid = (uid_t) -1;
873 gid_t clientGid = (gid_t) -1;
36457566 874
1071f781
LC
875 try {
876 /* Important: the server process *cannot* open the SQLite
877 database, because it doesn't like forks very much. */
878 assert(!store);
879
880 /* Accept a connection. */
881 struct sockaddr_storage remoteAddr;
882 socklen_t remoteAddrLen = sizeof(remoteAddr);
883
884 try_again:
885 AutoCloseFD remote = accept(fdSocket,
886 (struct sockaddr *) &remoteAddr, &remoteAddrLen);
887 checkInterrupt();
888 if (remote == -1) {
889 if (errno == EINTR)
890 goto try_again;
891 else
892 throw SysError("accepting connection");
893 }
894
895 closeOnExec(remote);
896
6efb578a
LC
897 {
898 int enabled = 1;
899
900 /* If we're on a TCP connection, disable Nagle's algorithm so that
901 data is sent as soon as possible. */
902 (void) setsockopt(remote, SOL_TCP, TCP_NODELAY,
903 &enabled, sizeof enabled);
904
905#if defined(TCP_QUICKACK)
906 /* Enable TCP quick-ack if applicable; this might help a little. */
907 (void) setsockopt(remote, SOL_TCP, TCP_QUICKACK,
908 &enabled, sizeof enabled);
909#endif
910 }
911
1071f781
LC
912 pid_t clientPid = -1;
913 bool trusted = false;
914
915 /* Get the identity of the caller, if possible. */
916 if (remoteAddr.ss_family == AF_UNIX) {
36457566 917#if defined(SO_PEERCRED)
1071f781
LC
918 ucred cred;
919 socklen_t credLen = sizeof(cred);
920 if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED,
921 &cred, &credLen) == -1)
922 throw SysError("getting peer credentials");
36457566 923
1071f781
LC
924 clientPid = cred.pid;
925 clientUid = cred.uid;
926 clientGid = cred.gid;
927 trusted = clientUid == 0;
36457566 928
2bb04905 929 struct passwd * pw = getpwuid(cred.uid);
79aa1a83 930 string user = pw ? pw->pw_name : std::to_string(cred.uid);
36457566 931
1071f781
LC
932 printMsg(lvlInfo,
933 format((string) "accepted connection from pid %1%, user %2%")
934 % clientPid % user);
2bb04905 935#endif
1071f781
LC
936 } else {
937 char address_str[128];
938 const char *result;
939
940 if (remoteAddr.ss_family == AF_INET) {
941 struct sockaddr_in *addr = (struct sockaddr_in *) &remoteAddr;
5c82722c 942 result = inet_ntop(AF_INET, &addr->sin_addr,
1071f781
LC
943 address_str, sizeof address_str);
944 } else if (remoteAddr.ss_family == AF_INET6) {
945 struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &remoteAddr;
5c82722c 946 result = inet_ntop(AF_INET6, &addr->sin6_addr,
1071f781
LC
947 address_str, sizeof address_str);
948 } else {
949 result = NULL;
950 }
951
952 if (result != NULL) {
953 printMsg(lvlInfo,
954 format("accepted connection from %1%")
955 % address_str);
956 }
957 }
958
959 /* Fork a child to handle the connection. */
960 startProcess([&]() {
961 close(fdSocket);
36457566 962
2bb04905
LC
963 /* Background the daemon. */
964 if (setsid() == -1)
965 throw SysError(format("creating a new session"));
36457566 966
2bb04905
LC
967 /* Restore normal handling of SIGCHLD. */
968 setSigChldAction(false);
36457566 969
2bb04905
LC
970 /* For debugging, stuff the pid into argv[1]. */
971 if (clientPid != -1 && argvSaved[1]) {
79aa1a83 972 string processName = std::to_string(clientPid);
2bb04905 973 strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
36457566 974 }
2bb04905 975
2608e409
HG
976 /* Store the client's user and group for this connection. This
977 has to be done in the forked process since it is per
1071f781
LC
978 connection. Setting these to -1 means: do not change. */
979 settings.clientUid = clientUid;
980 settings.clientGid = clientGid;
5cefb13d 981 isRemoteConnection = (remoteAddr.ss_family != AF_UNIX);
2608e409 982
2bb04905
LC
983 /* Handle the connection. */
984 from.fd = remote;
985 to.fd = remote;
81c580c8 986 processConnection(trusted, clientUid);
2bb04905 987
36457566 988 exit(0);
8327e733 989 }, false, "unexpected build daemon error: ", true);
36457566 990
1071f781
LC
991 } catch (Interrupted & e) {
992 throw;
993 } catch (Error & e) {
994 printMsg(lvlError, format("error processing connection: %1%") % e.msg());
36457566
LC
995 }
996}
997
1071f781 998static void daemonLoop(const std::vector<int>& sockets)
36457566 999{
1071f781
LC
1000 if (chdir("/") == -1)
1001 throw SysError("cannot change current directory");
1002
1003 /* Get rid of children automatically; don't let them become
1004 zombies. */
1005 setSigChldAction(true);
1006
1007 /* Mark sockets as close-on-exec. */
1008 for(int fd: sockets) {
1009 closeOnExec(fd);
36457566
LC
1010 }
1011
1071f781
LC
1012 /* Prepare the FD set corresponding to SOCKETS. */
1013 auto initializeFDSet = [&](fd_set *set) {
1014 FD_ZERO(set);
1015 for (int fd: sockets) {
1016 FD_SET(fd, set);
1017 }
1018 };
1019
1020 /* Loop accepting connections. */
1021 while (1) {
1022 fd_set readfds;
1023
1024 initializeFDSet(&readfds);
1025 int count =
1026 select(*std::max_element(sockets.begin(), sockets.end()) + 1,
1027 &readfds, NULL, NULL,
1028 NULL);
1029 if (count < 0) {
1030 int err = errno;
1031 if (err == EINTR)
1032 continue;
1033 throw SysError(format("select error: %1%") % strerror(err));
1034 }
1035
1036 for (unsigned int i = 0; i < sockets.size(); i++) {
1037 if (FD_ISSET(sockets[i], &readfds)) {
1038 acceptConnection(sockets[i]);
1039 }
1040 }
1041 }
1042}
1043
1044
1045void run(const std::vector<int>& sockets)
1046{
1047 daemonLoop(sockets);
36457566
LC
1048}
1049
1050
1051void printHelp()
1052{
1053 showManPage("nix-daemon");
1054}
1055
1056
1057string programId = "nix-daemon";