diff options
| author | Ludovic Courtès <ludovic.courtes@inria.fr> | 2017-06-19 17:39:24 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2017-06-22 10:59:07 +0200 |
| commit | 1071f781d97509347144754b3248581cf7c6c1d5 (patch) | |
| tree | 59565eacafc47841647596a5cf84c4e0311af39a /nix/nix-daemon | |
| parent | 5df1395a8d4bb83e002e1aab5d930edd2b49d27e (diff) | |
daemon: '--listen' can be passed several times, can specify TCP endpoints.
* nix/nix-daemon/guix-daemon.cc (DEFAULT_GUIX_PORT): New macro.
(listen_options): New variable.
(parse_opt): Push back '--listen' options to LISTEN_OPTIONS.
(open_unix_domain_socket, open_inet_socket)
(listening_sockets): New functions.
(main): Use it. Pass SOCKETS to 'run'.
* nix/nix-daemon/nix-daemon.cc (matchUser): Remove.
(SD_LISTEN_FDS_START): Remove.
(acceptConnection): New function.
(daemonLoop): Rewrite to take a vector of file descriptors, to select(2)
on them, and to call 'acceptConnection'.
(run): Change to take a vector of file descriptors.
* tests/guix-daemon.sh: Add test.
Diffstat (limited to 'nix/nix-daemon')
| -rw-r--r-- | nix/nix-daemon/guix-daemon.cc | 152 | ||||
| -rw-r--r-- | nix/nix-daemon/nix-daemon.cc | 283 |
2 files changed, 266 insertions, 169 deletions
diff --git a/nix/nix-daemon/guix-daemon.cc b/nix/nix-daemon/guix-daemon.cc index 0d9c33d1d2e..7963358202d 100644 --- a/nix/nix-daemon/guix-daemon.cc +++ b/nix/nix-daemon/guix-daemon.cc | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | /* GNU Guix --- Functional package management for GNU | 1 | /* GNU Guix --- Functional package management for GNU |
| 2 | Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org> | 2 | Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org> |
| 3 | Copyright (C) 2006, 2010, 2012, 2014 Eelco Dolstra <e.dolstra@tudelft.nl> | ||
| 3 | 4 | ||
| 4 | This file is part of GNU Guix. | 5 | This file is part of GNU Guix. |
| 5 | 6 | ||
| @@ -30,8 +31,12 @@ | |||
| 30 | #include <unistd.h> | 31 | #include <unistd.h> |
| 31 | #include <sys/types.h> | 32 | #include <sys/types.h> |
| 32 | #include <sys/stat.h> | 33 | #include <sys/stat.h> |
| 34 | #include <sys/socket.h> | ||
| 35 | #include <sys/un.h> | ||
| 36 | #include <netdb.h> | ||
| 33 | #include <strings.h> | 37 | #include <strings.h> |
| 34 | #include <exception> | 38 | #include <exception> |
| 39 | #include <iostream> | ||
| 35 | 40 | ||
| 36 | #include <libintl.h> | 41 | #include <libintl.h> |
| 37 | #include <locale.h> | 42 | #include <locale.h> |
| @@ -43,7 +48,7 @@ char **argvSaved; | |||
| 43 | using namespace nix; | 48 | using namespace nix; |
| 44 | 49 | ||
| 45 | /* Entry point in `nix-daemon.cc'. */ | 50 | /* Entry point in `nix-daemon.cc'. */ |
| 46 | extern void run (Strings args); | 51 | extern void run (const std::vector<int> &); |
| 47 | 52 | ||
| 48 | 53 | ||
| 49 | /* Command-line options. */ | 54 | /* Command-line options. */ |
| @@ -149,6 +154,12 @@ to live outputs") }, | |||
| 149 | }; | 154 | }; |
| 150 | 155 | ||
| 151 | 156 | ||
| 157 | /* Default port for '--listen' on TCP/IP. */ | ||
| 158 | #define DEFAULT_GUIX_PORT "44146" | ||
| 159 | |||
| 160 | /* List of '--listen' options. */ | ||
| 161 | static std::list<std::string> listen_options; | ||
| 162 | |||
| 152 | /* Convert ARG to a Boolean value, or throw an error if it does not denote a | 163 | /* Convert ARG to a Boolean value, or throw an error if it does not denote a |
| 153 | Boolean. */ | 164 | Boolean. */ |
| 154 | static bool | 165 | static bool |
| @@ -217,15 +228,7 @@ parse_opt (int key, char *arg, struct argp_state *state) | |||
| 217 | settings.keepLog = false; | 228 | settings.keepLog = false; |
| 218 | break; | 229 | break; |
| 219 | case GUIX_OPT_LISTEN: | 230 | case GUIX_OPT_LISTEN: |
| 220 | try | 231 | listen_options.push_back (arg); |
| 221 | { | ||
| 222 | settings.nixDaemonSocketFile = canonPath (arg); | ||
| 223 | } | ||
| 224 | catch (std::exception &e) | ||
| 225 | { | ||
| 226 | fprintf (stderr, _("error: %s\n"), e.what ()); | ||
| 227 | exit (EXIT_FAILURE); | ||
| 228 | } | ||
| 229 | break; | 232 | break; |
| 230 | case GUIX_OPT_SUBSTITUTE_URLS: | 233 | case GUIX_OPT_SUBSTITUTE_URLS: |
| 231 | settings.set ("substitute-urls", arg); | 234 | settings.set ("substitute-urls", arg); |
| @@ -276,13 +279,134 @@ static const struct argp argp = | |||
| 276 | guix_textdomain | 279 | guix_textdomain |
| 277 | }; | 280 | }; |
| 278 | 281 | ||
| 282 | |||
| 283 | static int | ||
| 284 | open_unix_domain_socket (const char *file) | ||
| 285 | { | ||
| 286 | /* Create and bind to a Unix domain socket. */ | ||
| 287 | AutoCloseFD fdSocket = socket (PF_UNIX, SOCK_STREAM, 0); | ||
| 288 | if (fdSocket == -1) | ||
| 289 | throw SysError (_("cannot create Unix domain socket")); | ||
| 290 | |||
| 291 | createDirs (dirOf (file)); | ||
| 292 | |||
| 293 | /* Urgh, sockaddr_un allows path names of only 108 characters. | ||
| 294 | So chdir to the socket directory so that we can pass a | ||
| 295 | relative path name. */ | ||
| 296 | if (chdir (dirOf (file).c_str ()) == -1) | ||
| 297 | throw SysError (_("cannot change current directory")); | ||
| 298 | Path fileRel = "./" + baseNameOf (file); | ||
| 299 | |||
| 300 | struct sockaddr_un addr; | ||
| 301 | addr.sun_family = AF_UNIX; | ||
| 302 | if (fileRel.size () >= sizeof (addr.sun_path)) | ||
| 303 | throw Error (format (_("socket file name '%1%' is too long")) % fileRel); | ||
| 304 | strcpy (addr.sun_path, fileRel.c_str ()); | ||
| 305 | |||
| 306 | unlink (file); | ||
| 307 | |||
| 308 | /* Make sure that the socket is created with 0666 permission | ||
| 309 | (everybody can connect --- provided they have access to the | ||
| 310 | directory containing the socket). */ | ||
| 311 | mode_t oldMode = umask (0111); | ||
| 312 | int res = bind (fdSocket, (struct sockaddr *) &addr, sizeof addr); | ||
| 313 | umask (oldMode); | ||
| 314 | if (res == -1) | ||
| 315 | throw SysError (format (_("cannot bind to socket '%1%'")) % file); | ||
| 316 | |||
| 317 | if (chdir ("/") == -1) /* back to the root */ | ||
| 318 | throw SysError (_("cannot change current directory")); | ||
| 319 | |||
| 320 | if (listen (fdSocket, 5) == -1) | ||
| 321 | throw SysError (format (_("cannot listen on socket '%1%'")) % file); | ||
| 322 | |||
| 323 | return fdSocket.borrow (); | ||
| 324 | } | ||
| 325 | |||
| 326 | /* Return a listening socket for ADDRESS, which has the given LENGTH. */ | ||
| 327 | static int | ||
| 328 | open_inet_socket (const struct sockaddr *address, socklen_t length) | ||
| 329 | { | ||
| 330 | AutoCloseFD fd = socket (address->sa_family, SOCK_STREAM, 0); | ||
| 331 | if (fd == -1) | ||
| 332 | throw SysError (_("cannot create TCP socket")); | ||
| 333 | |||
| 334 | int res = bind (fd, address, length); | ||
| 335 | if (res == -1) | ||
| 336 | throw SysError (_("cannot bind TCP socket")); | ||
| 337 | |||
| 338 | if (listen (fd, 5) == -1) | ||
| 339 | throw SysError (format (_("cannot listen on TCP socket"))); | ||
| 340 | |||
| 341 | return fd.borrow (); | ||
| 342 | } | ||
| 343 | |||
| 344 | /* Return a list of file descriptors of listening sockets. */ | ||
| 345 | static std::vector<int> | ||
| 346 | listening_sockets (const std::list<std::string> &options) | ||
| 347 | { | ||
| 348 | std::vector<int> result; | ||
| 349 | |||
| 350 | if (options.empty ()) | ||
| 351 | { | ||
| 352 | /* Open the default Unix-domain socket. */ | ||
| 353 | auto fd = open_unix_domain_socket (settings.nixDaemonSocketFile.c_str ()); | ||
| 354 | result.push_back (fd); | ||
| 355 | return result; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* Open the user-specified sockets. */ | ||
| 359 | for (const std::string& option: options) | ||
| 360 | { | ||
| 361 | if (option[0] == '/') | ||
| 362 | { | ||
| 363 | /* Assume OPTION is the file name of a Unix-domain socket. */ | ||
| 364 | settings.nixDaemonSocketFile = canonPath (option); | ||
| 365 | int fd = | ||
| 366 | open_unix_domain_socket (settings.nixDaemonSocketFile.c_str ()); | ||
| 367 | result.push_back (fd); | ||
| 368 | } | ||
| 369 | else | ||
| 370 | { | ||
| 371 | /* Assume OPTIONS has the form "HOST" or "HOST:PORT". */ | ||
| 372 | auto colon = option.find_last_of (":"); | ||
| 373 | auto host = colon == std::string::npos | ||
| 374 | ? option : option.substr (0, colon); | ||
| 375 | auto port = colon == std::string::npos | ||
| 376 | ? DEFAULT_GUIX_PORT | ||
| 377 | : option.substr (colon + 1, option.size () - colon - 1); | ||
| 378 | |||
| 379 | struct addrinfo *res, hints; | ||
| 380 | |||
| 381 | memset (&hints, '\0', sizeof hints); | ||
| 382 | hints.ai_socktype = SOCK_STREAM; | ||
| 383 | hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; | ||
| 384 | |||
| 385 | int err = getaddrinfo (host.c_str(), port.c_str (), | ||
| 386 | &hints, &res); | ||
| 387 | |||
| 388 | if (err != 0) | ||
| 389 | throw Error(format ("failed to look up '%1%': %2%") | ||
| 390 | % option % gai_strerror (err)); | ||
| 391 | |||
| 392 | printMsg (lvlDebug, format ("listening on '%1%', port '%2%'") | ||
| 393 | % host % port); | ||
| 394 | |||
| 395 | /* XXX: Pick the first result, RES. */ | ||
| 396 | result.push_back (open_inet_socket (res->ai_addr, | ||
| 397 | res->ai_addrlen)); | ||
| 398 | |||
| 399 | freeaddrinfo (res); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | return result; | ||
| 404 | } | ||
| 279 | 405 | ||
| 280 | 406 | ||
| 281 | int | 407 | int |
| 282 | main (int argc, char *argv[]) | 408 | main (int argc, char *argv[]) |
| 283 | { | 409 | { |
| 284 | static const Strings nothing; | ||
| 285 | |||
| 286 | setlocale (LC_ALL, ""); | 410 | setlocale (LC_ALL, ""); |
| 287 | bindtextdomain (guix_textdomain, LOCALEDIR); | 411 | bindtextdomain (guix_textdomain, LOCALEDIR); |
| 288 | textdomain (guix_textdomain); | 412 | textdomain (guix_textdomain); |
| @@ -359,6 +483,8 @@ main (int argc, char *argv[]) | |||
| 359 | 483 | ||
| 360 | argp_parse (&argp, argc, argv, 0, 0, 0); | 484 | argp_parse (&argp, argc, argv, 0, 0, 0); |
| 361 | 485 | ||
| 486 | auto sockets = listening_sockets (listen_options); | ||
| 487 | |||
| 362 | /* Effect all the changes made via 'settings.set'. */ | 488 | /* Effect all the changes made via 'settings.set'. */ |
| 363 | settings.update (); | 489 | settings.update (); |
| 364 | 490 | ||
| @@ -402,7 +528,7 @@ using `--build-users-group' is highly recommended\n")); | |||
| 402 | printMsg (lvlDebug, | 528 | printMsg (lvlDebug, |
| 403 | format ("listening on `%1%'") % settings.nixDaemonSocketFile); | 529 | format ("listening on `%1%'") % settings.nixDaemonSocketFile); |
| 404 | 530 | ||
| 405 | run (nothing); | 531 | run (sockets); |
| 406 | } | 532 | } |
| 407 | catch (std::exception &e) | 533 | catch (std::exception &e) |
| 408 | { | 534 | { |
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc index 79580ffb48c..3d8e909901c 100644 --- a/nix/nix-daemon/nix-daemon.cc +++ b/nix/nix-daemon/nix-daemon.cc | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <sys/stat.h> | 18 | #include <sys/stat.h> |
| 19 | #include <sys/socket.h> | 19 | #include <sys/socket.h> |
| 20 | #include <sys/un.h> | 20 | #include <sys/un.h> |
| 21 | #include <arpa/inet.h> | ||
| 21 | #include <fcntl.h> | 22 | #include <fcntl.h> |
| 22 | #include <errno.h> | 23 | #include <errno.h> |
| 23 | #include <pwd.h> | 24 | #include <pwd.h> |
| @@ -809,151 +810,87 @@ static void setSigChldAction(bool autoReap) | |||
| 809 | } | 810 | } |
| 810 | 811 | ||
| 811 | 812 | ||
| 812 | bool matchUser(const string & user, const string & group, const Strings & users) | 813 | /* Accept a connection on FDSOCKET and fork a server process to process the |
| 814 | new connection. */ | ||
| 815 | static void acceptConnection(int fdSocket) | ||
| 813 | { | 816 | { |
| 814 | if (find(users.begin(), users.end(), "*") != users.end()) | 817 | uid_t clientUid = (uid_t) -1; |
| 815 | return true; | 818 | gid_t clientGid = (gid_t) -1; |
| 816 | |||
| 817 | if (find(users.begin(), users.end(), user) != users.end()) | ||
| 818 | return true; | ||
| 819 | |||
| 820 | for (auto & i : users) | ||
| 821 | if (string(i, 0, 1) == "@") { | ||
| 822 | if (group == string(i, 1)) return true; | ||
| 823 | struct group * gr = getgrnam(i.c_str() + 1); | ||
| 824 | if (!gr) continue; | ||
| 825 | for (char * * mem = gr->gr_mem; *mem; mem++) | ||
| 826 | if (user == string(*mem)) return true; | ||
| 827 | } | ||
| 828 | |||
| 829 | return false; | ||
| 830 | } | ||
| 831 | |||
| 832 | |||
| 833 | #define SD_LISTEN_FDS_START 3 | ||
| 834 | |||
| 835 | |||
| 836 | static void daemonLoop() | ||
| 837 | { | ||
| 838 | if (chdir("/") == -1) | ||
| 839 | throw SysError("cannot change current directory"); | ||
| 840 | |||
| 841 | /* Get rid of children automatically; don't let them become | ||
| 842 | zombies. */ | ||
| 843 | setSigChldAction(true); | ||
| 844 | |||
| 845 | AutoCloseFD fdSocket; | ||
| 846 | |||
| 847 | /* Handle socket-based activation by systemd. */ | ||
| 848 | if (getEnv("LISTEN_FDS") != "") { | ||
| 849 | if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1") | ||
| 850 | throw Error("unexpected systemd environment variables"); | ||
| 851 | fdSocket = SD_LISTEN_FDS_START; | ||
| 852 | } | ||
| 853 | |||
| 854 | /* Otherwise, create and bind to a Unix domain socket. */ | ||
| 855 | else { | ||
| 856 | |||
| 857 | /* Create and bind to a Unix domain socket. */ | ||
| 858 | fdSocket = socket(PF_UNIX, SOCK_STREAM, 0); | ||
| 859 | if (fdSocket == -1) | ||
| 860 | throw SysError("cannot create Unix domain socket"); | ||
| 861 | |||
| 862 | string socketPath = settings.nixDaemonSocketFile; | ||
| 863 | |||
| 864 | createDirs(dirOf(socketPath)); | ||
| 865 | |||
| 866 | /* Urgh, sockaddr_un allows path names of only 108 characters. | ||
| 867 | So chdir to the socket directory so that we can pass a | ||
| 868 | relative path name. */ | ||
| 869 | if (chdir(dirOf(socketPath).c_str()) == -1) | ||
| 870 | throw SysError("cannot change current directory"); | ||
| 871 | Path socketPathRel = "./" + baseNameOf(socketPath); | ||
| 872 | |||
| 873 | struct sockaddr_un addr; | ||
| 874 | addr.sun_family = AF_UNIX; | ||
| 875 | if (socketPathRel.size() >= sizeof(addr.sun_path)) | ||
| 876 | throw Error(format("socket path `%1%' is too long") % socketPathRel); | ||
| 877 | strcpy(addr.sun_path, socketPathRel.c_str()); | ||
| 878 | |||
| 879 | unlink(socketPath.c_str()); | ||
| 880 | |||
| 881 | /* Make sure that the socket is created with 0666 permission | ||
| 882 | (everybody can connect --- provided they have access to the | ||
| 883 | directory containing the socket). */ | ||
| 884 | mode_t oldMode = umask(0111); | ||
| 885 | int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr)); | ||
| 886 | umask(oldMode); | ||
| 887 | if (res == -1) | ||
| 888 | throw SysError(format("cannot bind to socket `%1%'") % socketPath); | ||
| 889 | |||
| 890 | if (chdir("/") == -1) /* back to the root */ | ||
| 891 | throw SysError("cannot change current directory"); | ||
| 892 | |||
| 893 | if (listen(fdSocket, 5) == -1) | ||
| 894 | throw SysError(format("cannot listen on socket `%1%'") % socketPath); | ||
| 895 | } | ||
| 896 | |||
| 897 | closeOnExec(fdSocket); | ||
| 898 | |||
| 899 | /* Loop accepting connections. */ | ||
| 900 | while (1) { | ||
| 901 | |||
| 902 | try { | ||
| 903 | /* Important: the server process *cannot* open the SQLite | ||
| 904 | database, because it doesn't like forks very much. */ | ||
| 905 | assert(!store); | ||
| 906 | |||
| 907 | /* Accept a connection. */ | ||
| 908 | struct sockaddr_un remoteAddr; | ||
| 909 | socklen_t remoteAddrLen = sizeof(remoteAddr); | ||
| 910 | |||
| 911 | AutoCloseFD remote = accept(fdSocket, | ||
| 912 | (struct sockaddr *) &remoteAddr, &remoteAddrLen); | ||
| 913 | checkInterrupt(); | ||
| 914 | if (remote == -1) { | ||
| 915 | if (errno == EINTR) | ||
| 916 | continue; | ||
| 917 | else | ||
| 918 | throw SysError("accepting connection"); | ||
| 919 | } | ||
| 920 | |||
| 921 | closeOnExec(remote); | ||
| 922 | |||
| 923 | bool trusted = false; | ||
| 924 | pid_t clientPid = -1; | ||
| 925 | 819 | ||
| 820 | try { | ||
| 821 | /* Important: the server process *cannot* open the SQLite | ||
| 822 | database, because it doesn't like forks very much. */ | ||
| 823 | assert(!store); | ||
| 824 | |||
| 825 | /* Accept a connection. */ | ||
| 826 | struct sockaddr_storage remoteAddr; | ||
| 827 | socklen_t remoteAddrLen = sizeof(remoteAddr); | ||
| 828 | |||
| 829 | try_again: | ||
| 830 | AutoCloseFD remote = accept(fdSocket, | ||
| 831 | (struct sockaddr *) &remoteAddr, &remoteAddrLen); | ||
| 832 | checkInterrupt(); | ||
| 833 | if (remote == -1) { | ||
| 834 | if (errno == EINTR) | ||
| 835 | goto try_again; | ||
| 836 | else | ||
| 837 | throw SysError("accepting connection"); | ||
| 838 | } | ||
| 839 | |||
| 840 | closeOnExec(remote); | ||
| 841 | |||
| 842 | pid_t clientPid = -1; | ||
| 843 | bool trusted = false; | ||
| 844 | |||
| 845 | /* Get the identity of the caller, if possible. */ | ||
| 846 | if (remoteAddr.ss_family == AF_UNIX) { | ||
| 926 | #if defined(SO_PEERCRED) | 847 | #if defined(SO_PEERCRED) |
| 927 | /* Get the identity of the caller, if possible. */ | 848 | ucred cred; |
| 928 | ucred cred; | 849 | socklen_t credLen = sizeof(cred); |
| 929 | socklen_t credLen = sizeof(cred); | 850 | if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, |
| 930 | if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) | 851 | &cred, &credLen) == -1) |
| 931 | throw SysError("getting peer credentials"); | 852 | throw SysError("getting peer credentials"); |
| 932 | 853 | ||
| 933 | clientPid = cred.pid; | 854 | clientPid = cred.pid; |
| 855 | clientUid = cred.uid; | ||
| 856 | clientGid = cred.gid; | ||
| 857 | trusted = clientUid == 0; | ||
| 934 | 858 | ||
| 935 | struct passwd * pw = getpwuid(cred.uid); | 859 | struct passwd * pw = getpwuid(cred.uid); |
| 936 | string user = pw ? pw->pw_name : std::to_string(cred.uid); | 860 | string user = pw ? pw->pw_name : std::to_string(cred.uid); |
| 937 | 861 | ||
| 938 | struct group * gr = getgrgid(cred.gid); | 862 | printMsg(lvlInfo, |
| 939 | string group = gr ? gr->gr_name : std::to_string(cred.gid); | 863 | format((string) "accepted connection from pid %1%, user %2%") |
| 940 | 864 | % clientPid % user); | |
| 941 | Strings trustedUsers = settings.get("trusted-users", Strings({"root"})); | ||
| 942 | Strings allowedUsers = settings.get("allowed-users", Strings({"*"})); | ||
| 943 | |||
| 944 | if (matchUser(user, group, trustedUsers)) | ||
| 945 | trusted = true; | ||
| 946 | |||
| 947 | if (!trusted && !matchUser(user, group, allowedUsers)) | ||
| 948 | throw Error(format("user `%1%' is not allowed to connect to the Nix daemon") % user); | ||
| 949 | |||
| 950 | printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%" | ||
| 951 | + (trusted ? " (trusted)" : "")) % clientPid % user); | ||
| 952 | #endif | 865 | #endif |
| 953 | 866 | } else { | |
| 954 | /* Fork a child to handle the connection. */ | 867 | char address_str[128]; |
| 955 | startProcess([&]() { | 868 | const char *result; |
| 956 | fdSocket.close(); | 869 | |
| 870 | if (remoteAddr.ss_family == AF_INET) { | ||
| 871 | struct sockaddr_in *addr = (struct sockaddr_in *) &remoteAddr; | ||
| 872 | struct in_addr inaddr = { addr->sin_addr }; | ||
| 873 | result = inet_ntop(AF_INET, &inaddr, | ||
| 874 | address_str, sizeof address_str); | ||
| 875 | } else if (remoteAddr.ss_family == AF_INET6) { | ||
| 876 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &remoteAddr; | ||
| 877 | struct in6_addr inaddr = { addr->sin6_addr }; | ||
| 878 | result = inet_ntop(AF_INET6, &inaddr, | ||
| 879 | address_str, sizeof address_str); | ||
| 880 | } else { | ||
| 881 | result = NULL; | ||
| 882 | } | ||
| 883 | |||
| 884 | if (result != NULL) { | ||
| 885 | printMsg(lvlInfo, | ||
| 886 | format("accepted connection from %1%") | ||
| 887 | % address_str); | ||
| 888 | } | ||
| 889 | } | ||
| 890 | |||
| 891 | /* Fork a child to handle the connection. */ | ||
| 892 | startProcess([&]() { | ||
| 893 | close(fdSocket); | ||
| 957 | 894 | ||
| 958 | /* Background the daemon. */ | 895 | /* Background the daemon. */ |
| 959 | if (setsid() == -1) | 896 | if (setsid() == -1) |
| @@ -968,17 +905,11 @@ static void daemonLoop() | |||
| 968 | strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); | 905 | strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); |
| 969 | } | 906 | } |
| 970 | 907 | ||
| 971 | #if defined(SO_PEERCRED) | ||
| 972 | /* Store the client's user and group for this connection. This | 908 | /* Store the client's user and group for this connection. This |
| 973 | has to be done in the forked process since it is per | 909 | has to be done in the forked process since it is per |
| 974 | connection. */ | 910 | connection. Setting these to -1 means: do not change. */ |
| 975 | settings.clientUid = cred.uid; | 911 | settings.clientUid = clientUid; |
| 976 | settings.clientGid = cred.gid; | 912 | settings.clientGid = clientGid; |
| 977 | #else | ||
| 978 | /* Setting these to -1 means: do not change */ | ||
| 979 | settings.clientUid = (uid_t) -1; | ||
| 980 | settings.clientGid = (gid_t) -1; | ||
| 981 | #endif | ||
| 982 | 913 | ||
| 983 | /* Handle the connection. */ | 914 | /* Handle the connection. */ |
| 984 | from.fd = remote; | 915 | from.fd = remote; |
| @@ -988,23 +919,63 @@ static void daemonLoop() | |||
| 988 | exit(0); | 919 | exit(0); |
| 989 | }, false, "unexpected Nix daemon error: ", true); | 920 | }, false, "unexpected Nix daemon error: ", true); |
| 990 | 921 | ||
| 991 | } catch (Interrupted & e) { | 922 | } catch (Interrupted & e) { |
| 992 | throw; | 923 | throw; |
| 993 | } catch (Error & e) { | 924 | } catch (Error & e) { |
| 994 | printMsg(lvlError, format("error processing connection: %1%") % e.msg()); | 925 | printMsg(lvlError, format("error processing connection: %1%") % e.msg()); |
| 995 | } | ||
| 996 | } | 926 | } |
| 997 | } | 927 | } |
| 998 | 928 | ||
| 999 | 929 | static void daemonLoop(const std::vector<int>& sockets) | |
| 1000 | void run(Strings args) | ||
| 1001 | { | 930 | { |
| 1002 | for (Strings::iterator i = args.begin(); i != args.end(); ) { | 931 | if (chdir("/") == -1) |
| 1003 | string arg = *i++; | 932 | throw SysError("cannot change current directory"); |
| 1004 | if (arg == "--daemon") /* ignored for backwards compatibility */; | 933 | |
| 934 | /* Get rid of children automatically; don't let them become | ||
| 935 | zombies. */ | ||
| 936 | setSigChldAction(true); | ||
| 937 | |||
| 938 | /* Mark sockets as close-on-exec. */ | ||
| 939 | for(int fd: sockets) { | ||
| 940 | closeOnExec(fd); | ||
| 1005 | } | 941 | } |
| 1006 | 942 | ||
| 1007 | daemonLoop(); | 943 | /* Prepare the FD set corresponding to SOCKETS. */ |
| 944 | auto initializeFDSet = [&](fd_set *set) { | ||
| 945 | FD_ZERO(set); | ||
| 946 | for (int fd: sockets) { | ||
| 947 | FD_SET(fd, set); | ||
| 948 | } | ||
| 949 | }; | ||
| 950 | |||
| 951 | /* Loop accepting connections. */ | ||
| 952 | while (1) { | ||
| 953 | fd_set readfds; | ||
| 954 | |||
| 955 | initializeFDSet(&readfds); | ||
| 956 | int count = | ||
| 957 | select(*std::max_element(sockets.begin(), sockets.end()) + 1, | ||
| 958 | &readfds, NULL, NULL, | ||
| 959 | NULL); | ||
| 960 | if (count < 0) { | ||
| 961 | int err = errno; | ||
| 962 | if (err == EINTR) | ||
| 963 | continue; | ||
| 964 | throw SysError(format("select error: %1%") % strerror(err)); | ||
| 965 | } | ||
| 966 | |||
| 967 | for (unsigned int i = 0; i < sockets.size(); i++) { | ||
| 968 | if (FD_ISSET(sockets[i], &readfds)) { | ||
| 969 | acceptConnection(sockets[i]); | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | } | ||
| 974 | |||
| 975 | |||
| 976 | void run(const std::vector<int>& sockets) | ||
| 977 | { | ||
| 978 | daemonLoop(sockets); | ||
| 1008 | } | 979 | } |
| 1009 | 980 | ||
| 1010 | 981 | ||
