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