Add support for systemd style socket activation.
authorGuus Sliepen <guus@tinc-vpn.org>
Mon, 26 Mar 2012 13:46:09 +0000 (14:46 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Mon, 26 Mar 2012 13:46:09 +0000 (14:46 +0100)
If the LISTEN_FDS environment variable is set and tinc is run in the
foreground, tinc will use filedescriptors 3 to 3 + LISTEN_FDS for its listening
TCP sockets. For now, tinc will create matching listening UDP sockets itself.

There is no dependency on systemd or on libsystemd-daemon.

src/net_setup.c
src/tincd.c

index 4b90737f3c68d4e8d43baf473e110f27ab2c4bf8..d3940e72b36aa698404a9e70ac1c47167d03a17a 100644 (file)
@@ -581,71 +581,112 @@ static bool setup_myself(void) {
 
        /* Open sockets */
 
-       listen_sockets = 0;
-       cfg = lookup_config(config_tree, "BindToAddress");
+       if(!do_detach && getenv("LISTEN_FDS")) {
+               sockaddr_t sa;
+               socklen_t salen;
 
-       do {
-               get_config_string(cfg, &address);
-               if(cfg)
-                       cfg = lookup_config_next(config_tree, cfg);
+               listen_sockets = atoi(getenv("LISTEN_FDS"));
+#ifdef HAVE_UNSETENV
+               unsetenv("LISTEN_FDS");
+#endif
 
-               char *port = myport;
+               if(listen_sockets > MAXSOCKETS) {
+                       logger(LOG_ERR, "Too many listening sockets");
+                       return false;
+               }
 
-               if(address) {
-                       char *space = strchr(address, ' ');
-                       if(space) {
-                               *space++ = 0;
-                               port = space;
+               for(i = 0; i < listen_sockets; i++) {
+                       salen = sizeof sa;
+                       if(getsockname(i + 3, &sa.sa, &salen) < 0) {
+                               logger(LOG_ERR, "Could not get address of listen fd %d: %s", i + 3, sockstrerror(errno));
+                               return false;
                        }
 
-                       if(!strcmp(address, "*"))
-                               *address = 0;
-               }
+                       listen_socket[i].tcp = i + 3;
+
+#ifdef FD_CLOEXEC
+                       fcntl(i + 3, F_SETFD, FD_CLOEXEC);
+#endif
 
-               hint.ai_family = addressfamily;
-               hint.ai_socktype = SOCK_STREAM;
-               hint.ai_protocol = IPPROTO_TCP;
-               hint.ai_flags = AI_PASSIVE;
+                       listen_socket[i].udp = setup_vpn_in_socket(&sa);
+                       if(listen_socket[i].udp < 0)
+                               return false;
 
-               err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
-               free(address);
+                       ifdebug(CONNECTIONS) {
+                               hostname = sockaddr2hostname(&sa);
+                               logger(LOG_NOTICE, "Listening on %s", hostname);
+                               free(hostname);
+                       }
 
-               if(err || !ai) {
-                       logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
-                                  gai_strerror(err));
-                       return false;
+                       memcpy(&listen_socket[i].sa, &sa, salen);
                }
+       } else {
+               listen_sockets = 0;
+               cfg = lookup_config(config_tree, "BindToAddress");
+
+               do {
+                       get_config_string(cfg, &address);
+                       if(cfg)
+                               cfg = lookup_config_next(config_tree, cfg);
+
+                       char *port = myport;
+
+                       if(address) {
+                               char *space = strchr(address, ' ');
+                               if(space) {
+                                       *space++ = 0;
+                                       port = space;
+                               }
+
+                               if(!strcmp(address, "*"))
+                                       *address = 0;
+                       }
+
+                       hint.ai_family = addressfamily;
+                       hint.ai_socktype = SOCK_STREAM;
+                       hint.ai_protocol = IPPROTO_TCP;
+                       hint.ai_flags = AI_PASSIVE;
 
-               for(aip = ai; aip; aip = aip->ai_next) {
-                       if(listen_sockets >= MAXSOCKETS) {
-                               logger(LOG_ERR, "Too many listening sockets");
+                       err = getaddrinfo(address && *address ? address : NULL, port, &hint, &ai);
+                       free(address);
+
+                       if(err || !ai) {
+                               logger(LOG_ERR, "System call `%s' failed: %s", "getaddrinfo",
+                                          gai_strerror(err));
                                return false;
                        }
 
-                       listen_socket[listen_sockets].tcp =
-                               setup_listen_socket((sockaddr_t *) aip->ai_addr);
+                       for(aip = ai; aip; aip = aip->ai_next) {
+                               if(listen_sockets >= MAXSOCKETS) {
+                                       logger(LOG_ERR, "Too many listening sockets");
+                                       return false;
+                               }
 
-                       if(listen_socket[listen_sockets].tcp < 0)
-                               continue;
+                               listen_socket[listen_sockets].tcp =
+                                       setup_listen_socket((sockaddr_t *) aip->ai_addr);
 
-                       listen_socket[listen_sockets].udp =
-                               setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
+                               if(listen_socket[listen_sockets].tcp < 0)
+                                       continue;
 
-                       if(listen_socket[listen_sockets].udp < 0)
-                               continue;
+                               listen_socket[listen_sockets].udp =
+                                       setup_vpn_in_socket((sockaddr_t *) aip->ai_addr);
 
-                       ifdebug(CONNECTIONS) {
-                               hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
-                               logger(LOG_NOTICE, "Listening on %s", hostname);
-                               free(hostname);
-                       }
+                               if(listen_socket[listen_sockets].udp < 0)
+                                       continue;
 
-                       memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
-                       listen_sockets++;
-               }
+                               ifdebug(CONNECTIONS) {
+                                       hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
+                                       logger(LOG_NOTICE, "Listening on %s", hostname);
+                                       free(hostname);
+                               }
 
-               freeaddrinfo(ai);
-       } while(cfg);
+                               memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
+                               listen_sockets++;
+                       }
+
+                       freeaddrinfo(ai);
+               } while(cfg);
+       }
 
        if(listen_sockets)
                logger(LOG_NOTICE, "Ready");
index 443301e083f178a501926dd0d383315463c2409b..148e13e444ef2c7ef8188e04baa3e1a189504a55 100644 (file)
@@ -539,6 +539,12 @@ int main(int argc, char **argv) {
 
        g_argv = argv;
 
+       if(getenv("LISTEN_PID") && atoi(getenv("LISTEN_PID")) == getpid())
+               do_detach = false;
+#ifdef HAVE_UNSETENV
+       unsetenv("LISTEN_PID");
+#endif
+
        init_configuration(&config_tree);
 
        /* Slllluuuuuuurrrrp! */