about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2012-06-19T03·01-0400
committerEelco Dolstra <eelco.dolstra@logicblox.com>2012-06-19T03·01-0400
commit2f3f413e91620d2314be59870f4bd67292f7d2de (patch)
treef2c9fce2695e56473662ee579c75f8d57d7cf779
parent02fb6323e06502e410dd5d5f8fe10589a7c938d4 (diff)
Support socket-based, on-demand activation of the Nix daemon with systemd
Systemd can start the Nix daemon on demand when the Nix daemon socket
is first accessed.  This is signalled through the LISTEN_FDS
environment variable, so all we need to do is check for that and then
use file descriptor 3 as the listen socket instead of creating one
ourselves.
-rw-r--r--src/nix-worker/nix-worker.cc76
1 files changed, 46 insertions, 30 deletions
diff --git a/src/nix-worker/nix-worker.cc b/src/nix-worker/nix-worker.cc
index b7bce20bd412..6131e73e6c00 100644
--- a/src/nix-worker/nix-worker.cc
+++ b/src/nix-worker/nix-worker.cc
@@ -695,51 +695,67 @@ static void setSigChldAction(bool autoReap)
 }
 
 
+#define SD_LISTEN_FDS_START 3
+
+
 static void daemonLoop()
 {
     /* Get rid of children automatically; don't let them become
        zombies. */
     setSigChldAction(true);
-    
-    /* Create and bind to a Unix domain socket. */
-    AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
-    if (fdSocket == -1)
-        throw SysError("cannot create Unix domain socket");
 
-    closeOnExec(fdSocket);
+    AutoCloseFD fdSocket;
+
+    /* Handle socket-based activation by systemd. */
+    if (getEnv("LISTEN_FDS") != "") {
+        if (getEnv("LISTEN_PID") != int2String(getpid()) || getEnv("LISTEN_FDS") != "1")
+            throw Error("unexpected systemd environment variables");
+        fdSocket = SD_LISTEN_FDS_START;
+    }
+
+    /* Otherwise, create and bind to a Unix domain socket. */
+    else {
     
-    string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
+        /* Create and bind to a Unix domain socket. */
+        fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
+        if (fdSocket == -1)
+            throw SysError("cannot create Unix domain socket");
+
+        string socketPath = nixStateDir + DEFAULT_SOCKET_PATH;
 
-    createDirs(dirOf(socketPath));
+        createDirs(dirOf(socketPath));
 
-    /* Urgh, sockaddr_un allows path names of only 108 characters.  So
-       chdir to the socket directory so that we can pass a relative
-       path name. */
-    chdir(dirOf(socketPath).c_str());
-    Path socketPathRel = "./" + baseNameOf(socketPath);
+        /* Urgh, sockaddr_un allows path names of only 108 characters.
+           So chdir to the socket directory so that we can pass a
+           relative path name. */
+        chdir(dirOf(socketPath).c_str());
+        Path socketPathRel = "./" + baseNameOf(socketPath);
     
-    struct sockaddr_un addr;
-    addr.sun_family = AF_UNIX;
-    if (socketPathRel.size() >= sizeof(addr.sun_path))
-        throw Error(format("socket path `%1%' is too long") % socketPathRel);
-    strcpy(addr.sun_path, socketPathRel.c_str());
+        struct sockaddr_un addr;
+        addr.sun_family = AF_UNIX;
+        if (socketPathRel.size() >= sizeof(addr.sun_path))
+            throw Error(format("socket path `%1%' is too long") % socketPathRel);
+        strcpy(addr.sun_path, socketPathRel.c_str());
 
-    unlink(socketPath.c_str());
+        unlink(socketPath.c_str());
 
-    /* Make sure that the socket is created with 0666 permission
-       (everybody can connect --- provided they have access to the
-       directory containing the socket). */
-    mode_t oldMode = umask(0111);
-    int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
-    umask(oldMode);
-    if (res == -1)
-        throw SysError(format("cannot bind to socket `%1%'") % socketPath);
+        /* Make sure that the socket is created with 0666 permission
+           (everybody can connect --- provided they have access to the
+           directory containing the socket). */
+        mode_t oldMode = umask(0111);
+        int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
+        umask(oldMode);
+        if (res == -1)
+            throw SysError(format("cannot bind to socket `%1%'") % socketPath);
 
-    chdir("/"); /* back to the root */
+        chdir("/"); /* back to the root */
 
-    if (listen(fdSocket, 5) == -1)
-        throw SysError(format("cannot listen on socket `%1%'") % socketPath);
+        if (listen(fdSocket, 5) == -1)
+            throw SysError(format("cannot listen on socket `%1%'") % socketPath);
+    }
 
+    closeOnExec(fdSocket);
+        
     /* Loop accepting connections. */
     while (1) {