support for systemd style listen fd passing
authorChristian Grothoff <christian@grothoff.org>
Wed, 23 Jun 2010 11:58:33 +0000 (11:58 +0000)
committerChristian Grothoff <christian@grothoff.org>
Wed, 23 Jun 2010 11:58:33 +0000 (11:58 +0000)
src/include/gnunet_network_lib.h
src/include/gnunet_server_lib.h
src/util/network.c
src/util/server.c
src/util/service.c

index 1666abc9037da9264ac61aa1cdb2331461aa0892..d7d3acbf0a2275fa52cdc74c1d5a7d976b18dc1c 100644 (file)
@@ -71,6 +71,16 @@ struct GNUNET_NETWORK_Handle *GNUNET_NETWORK_socket_accept (const struct
                                                             address_len);
 
 
+/**
+ * Box a native socket (and check that it is a socket).
+ *
+ * @param fd socket to box
+ * @return NULL on error (including not supported on target platform)
+ */
+struct GNUNET_NETWORK_Handle *
+GNUNET_NETWORK_socket_box_native (int fd);
+
+
 /**
  * Bind to a connected socket
  *
index 6dd756a6126b0f063e2539f210f7a1d382c6c9b4..9649b177660124bba8812de51f49667ce7d3ac9f 100644 (file)
@@ -108,6 +108,29 @@ struct GNUNET_SERVER_MessageHandler
 };
 
 
+/**
+ * Create a new server.
+ *
+ * @param sched scheduler to use
+ * @param access function for access control
+ * @param access_cls closure for access
+ * @param lsocks NULL-terminated array of listen sockets
+ * @param maxbuf maximum write buffer size for accepted sockets
+ * @param idle_timeout after how long should we timeout idle connections?
+ * @param require_found if YES, connections sending messages of unknown type
+ *        will be closed
+ * @return handle for the new server, NULL on error
+ *         (typically, "port" already in use)
+ */
+struct GNUNET_SERVER_Handle *
+GNUNET_SERVER_create_with_sockets (struct GNUNET_SCHEDULER_Handle *sched,
+                                  GNUNET_CONNECTION_AccessCheck access, void *access_cls,
+                                  struct GNUNET_NETWORK_Handle **lsocks,
+                                  size_t maxbuf,
+                                  struct GNUNET_TIME_Relative
+                                  idle_timeout,
+                                  int require_found);
+
 /**
  * Create a new server.
  *
index 13ed345bfff439d9c8150add92814ae186151326..4a287ada9bcd9e0beffad04535b15310964e951f 100644 (file)
@@ -307,6 +307,29 @@ GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc)
 }
 
 
+/**
+ * Box a native socket (and check that it is a socket).
+ *
+ * @param fd socket to box
+ * @return NULL on error (including not supported on target platform)
+ */
+struct GNUNET_NETWORK_Handle *
+GNUNET_NETWORK_socket_box_native (int fd)
+{
+#if MINGW
+  return NULL;
+#else
+  struct GNUNET_NETWORK_Handle *ret;
+
+  if (fcntl (fd, F_GETFD) < 0)
+    return NULL; /* invalid FD */
+  ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle)); 
+  ret->fd = fd;
+  return ret;
+#endif
+}
+
+
 /**
  * Connect a socket
  * @param desc socket
index 940aad363cb23a81d1102ccb4d1523893a3e9d44..d551cbbd3764979e14a4eb392c27be10407b97c7 100644 (file)
@@ -389,6 +389,60 @@ open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
 }
 
 
+/**
+ * Create a new server.
+ *
+ * @param sched scheduler to use
+ * @param access function for access control
+ * @param access_cls closure for access
+ * @param lsocks NULL-terminated array of listen sockets
+ * @param maxbuf maximum write buffer size for accepted sockets
+ * @param idle_timeout after how long should we timeout idle connections?
+ * @param require_found if YES, connections sending messages of unknown type
+ *        will be closed
+ * @return handle for the new server, NULL on error
+ *         (typically, "port" already in use)
+ */
+struct GNUNET_SERVER_Handle *
+GNUNET_SERVER_create_with_sockets (struct GNUNET_SCHEDULER_Handle *sched,
+                                  GNUNET_CONNECTION_AccessCheck access, void *access_cls,
+                                  struct GNUNET_NETWORK_Handle **lsocks,
+                                  size_t maxbuf,
+                                  struct GNUNET_TIME_Relative
+                                  idle_timeout,
+                                  int require_found)
+{
+  struct GNUNET_SERVER_Handle *ret;
+  struct GNUNET_NETWORK_FDSet *r;
+  int i;
+
+  ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
+  ret->sched = sched;
+  ret->maxbuf = maxbuf;
+  ret->idle_timeout = idle_timeout;
+  ret->listen_sockets = lsocks;
+  ret->access = access;
+  ret->access_cls = access_cls;
+  ret->require_found = require_found;
+  if (lsocks != NULL)
+    {
+      r = GNUNET_NETWORK_fdset_create ();
+      i = 0;
+      while (NULL != ret->listen_sockets[i])
+        GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]);
+      ret->listen_task = GNUNET_SCHEDULER_add_select (sched,
+                                                      GNUNET_SCHEDULER_PRIORITY_HIGH,
+                                                      GNUNET_SCHEDULER_NO_TASK,
+                                                      GNUNET_TIME_UNIT_FOREVER_REL,
+                                                      r, NULL,
+                                                      &process_listen_socket,
+                                                      ret);
+      GNUNET_NETWORK_fdset_destroy (r);
+    }
+  return ret;
+}
+
+
 /**
  * Create a new server.
  *
@@ -414,9 +468,7 @@ GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched,
                       struct GNUNET_TIME_Relative
                       idle_timeout, int require_found)
 {
-  struct GNUNET_SERVER_Handle *ret;
   struct GNUNET_NETWORK_Handle **lsocks;
-  struct GNUNET_NETWORK_FDSet *r;
   unsigned int i;
   unsigned int j;
 
@@ -448,30 +500,12 @@ GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched,
     {
       lsocks = NULL;
     }
-  ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
-  ret->sched = sched;
-  ret->maxbuf = maxbuf;
-  ret->idle_timeout = idle_timeout;
-  ret->listen_sockets = lsocks;
-  ret->access = access;
-  ret->access_cls = access_cls;
-  ret->require_found = require_found;
-  if (lsocks != NULL)
-    {
-      r = GNUNET_NETWORK_fdset_create ();
-      i = 0;
-      while (NULL != ret->listen_sockets[i])
-        GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]);
-      ret->listen_task = GNUNET_SCHEDULER_add_select (sched,
-                                                      GNUNET_SCHEDULER_PRIORITY_HIGH,
-                                                      GNUNET_SCHEDULER_NO_TASK,
-                                                      GNUNET_TIME_UNIT_FOREVER_REL,
-                                                      r, NULL,
-                                                      &process_listen_socket,
-                                                      ret);
-      GNUNET_NETWORK_fdset_destroy (r);
-    }
-  return ret;
+  return GNUNET_SERVER_create_with_sockets (sched,
+                                           access, access_cls,
+                                           lsocks,
+                                           maxbuf, 
+                                           idle_timeout,
+                                           require_found);
 }
 
 
index cd27380ee2d99f1f54c3f89a59f7c95ede402476..b45a736ae596769ede63d7a27c078df55b5725f5 100644 (file)
@@ -435,7 +435,8 @@ struct GNUNET_SERVICE_Context
   struct GNUNET_SCHEDULER_Handle *sched;
 
   /**
-   * NULL-terminated array of addresses to bind to.
+   * NULL-terminated array of addresses to bind to, NULL if we got pre-bound
+   * listen sockets.
    */
   struct sockaddr **addrs;
 
@@ -482,6 +483,16 @@ struct GNUNET_SERVICE_Context
    */
   struct GNUNET_SERVER_MessageHandler *my_handlers;
 
+  /**
+   * Array of the lengths of the entries in addrs.
+   */
+  socklen_t * addrlens;
+
+  /**
+   * NULL-terminated array of listen sockets we should take over.
+   */
+  struct GNUNET_NETWORK_Handle **lsocks;
+
   /**
    * Idle timeout for server.
    */
@@ -515,11 +526,6 @@ struct GNUNET_SERVICE_Context
    */
   enum GNUNET_SERVICE_Options options;
 
-  /**
-   * Array of the lengths of the entries in addrs.
-   */
-  socklen_t * addrlens;
-
 };
 
 
@@ -1085,6 +1091,11 @@ setup_service (struct GNUNET_SERVICE_Context *sctx)
   unsigned long long maxbuf;
   struct GNUNET_TIME_Relative idleout;
   int tolerant;
+  const char *lpid;
+  unsigned int pid;
+  const char *nfds;
+  unsigned int cnt;
+  int flags;
 
   if (GNUNET_CONFIGURATION_have_value (sctx->cfg,
                                        sctx->serviceName, "TIMEOUT"))
@@ -1140,11 +1151,45 @@ setup_service (struct GNUNET_SERVICE_Context *sctx)
   else
     tolerant = GNUNET_NO;
 
-  if (GNUNET_SYSERR ==
-      GNUNET_SERVICE_get_server_addresses (sctx->serviceName,
-                                          sctx->cfg,
-                                          &sctx->addrs,
-                                          &sctx->addrlens))
+#ifndef MINGW
+  errno = 0;
+  if ( (NULL != (lpid = getenv ("LISTEN_PID"))) &&
+       (1 == sscanf ("%u", lpid, &pid)) &&
+       (getpid () == (pid_t) pid) &&
+       (NULL != (nfds = getenv ("LISTEN_FDS"))) &&
+       (1 == sscanf ("%u", nfds, &cnt)) &&
+       (cnt > 0) )
+    {
+      sctx->lsocks = GNUNET_malloc (sizeof(struct GNUNET_NETWORK_Handle*) * (cnt+1));
+      while (0 < cnt--)
+       {
+         flags = fcntl (3 + cnt, F_GETFD);      
+         if ( (flags < 0) ||
+              (0 != (flags & FD_CLOEXEC)) ||
+              (NULL == (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))) )
+           {
+             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                         _("Could not access pre-bound socket %u, will try to bind myself\n"),
+                         (unsigned int) 3 +cnt);
+             cnt++;
+             while (sctx->lsocks[cnt] != NULL)
+               GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++]);
+             GNUNET_free (sctx->lsocks);
+             sctx->lsocks = NULL;
+             break;
+           }
+       }
+      unsetenv ("LISTEN_PID");
+      unsetenv ("LISTEN_FDS");
+    }
+#endif
+
+  if ( (sctx->lsocks == NULL) &&
+       (GNUNET_SYSERR ==
+       GNUNET_SERVICE_get_server_addresses (sctx->serviceName,
+                                            sctx->cfg,
+                                            &sctx->addrs,
+                                            &sctx->addrlens)) )
     return GNUNET_SYSERR;
   sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES;
   sctx->maxbuf = (size_t) maxbuf;
@@ -1265,23 +1310,34 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   unsigned int i;
 
   sctx->sched = tc->sched;
-  sctx->server = GNUNET_SERVER_create (tc->sched,
-                                       &check_access,
-                                       sctx,
-                                       sctx->addrs,
-                                       sctx->addrlens,
-                                       sctx->maxbuf,
-                                       sctx->timeout, sctx->require_found);
+  if (sctx->lsocks != NULL)
+    sctx->server = GNUNET_SERVER_create_with_sockets (tc->sched,
+                                                     &check_access,
+                                                     sctx,
+                                                     sctx->lsocks,
+                                                     sctx->maxbuf,
+                                                     sctx->timeout, sctx->require_found);
+  else
+    sctx->server = GNUNET_SERVER_create (tc->sched,
+                                        &check_access,
+                                        sctx,
+                                        sctx->addrs,
+                                        sctx->addrlens,
+                                        sctx->maxbuf,
+                                        sctx->timeout, sctx->require_found);
   if (sctx->server == NULL)
     {
-      i = 0;
-      while (sctx->addrs[i] != NULL)
+      if (sctx->addrs != NULL)
        {
-         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                     _("Failed to start `%s' at `%s'\n"),
-                     sctx->serviceName, 
-                     GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
-         i++;
+         i = 0;
+         while (sctx->addrs[i] != NULL)
+           {
+             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                         _("Failed to start `%s' at `%s'\n"),
+                         sctx->serviceName, 
+                         GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+             i++;
+           }
        }
       sctx->ret = GNUNET_SYSERR;
       return;
@@ -1307,14 +1363,17 @@ service_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       sctx->ready_confirm_fd = -1;
       write_pid_file (sctx, getpid ());
     }
-  i = 0;
-  while (sctx->addrs[i] != NULL)
+  if (sctx->addrs != NULL)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                 _("Service `%s' runs at %s\n"),
-                 sctx->serviceName, 
-                 GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
-      i++;
+      i = 0;
+      while (sctx->addrs[i] != NULL)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                     _("Service `%s' runs at %s\n"),
+                     sctx->serviceName, 
+                     GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+         i++;
+       }
     }
   sctx->task (sctx->task_cls, tc->sched, sctx->server, sctx->cfg);
 }
@@ -1594,15 +1653,29 @@ GNUNET_SERVICE_start (const char *serviceName,
   sctx->sched = sched;
 
   /* setup subsystems */
-  if ((GNUNET_OK != setup_service (sctx)) ||
-      (NULL == (sctx->server = GNUNET_SERVER_create (sched,
-                                                     &check_access,
-                                                     sctx,
-                                                     sctx->addrs,
-                                                     sctx->addrlens,
-                                                     sctx->maxbuf,
-                                                     sctx->timeout,
-                                                     sctx->require_found))))
+  if (GNUNET_OK != setup_service (sctx))
+    {
+      GNUNET_SERVICE_stop (sctx);
+      return NULL;
+    }
+  if (sctx->lsocks != NULL)
+    sctx->server = GNUNET_SERVER_create_with_sockets (sched,
+                                                     &check_access,
+                                                     sctx,
+                                                     sctx->lsocks,
+                                                     sctx->maxbuf,
+                                                     sctx->timeout, sctx->require_found);
+  else
+    sctx->server = GNUNET_SERVER_create (sched,
+                                        &check_access,
+                                        sctx,
+                                        sctx->addrs,
+                                        sctx->addrlens,
+                                        sctx->maxbuf,
+                                        sctx->timeout,
+                                        sctx->require_found);
+                                        
+  if (NULL == sctx->server)
     {
       GNUNET_SERVICE_stop (sctx);
       return NULL;
@@ -1642,10 +1715,13 @@ GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx)
   if (NULL != sctx->server)
     GNUNET_SERVER_destroy (sctx->server);
   GNUNET_free_non_null (sctx->my_handlers);
-  i = 0;
-  while (sctx->addrs[i] != NULL)    
-    GNUNET_free (sctx->addrs[i++]);    
-  GNUNET_free (sctx->addrs);
+  if (sctx->addrs != NULL)
+    {
+      i = 0;
+      while (sctx->addrs[i] != NULL)    
+       GNUNET_free (sctx->addrs[i++]);    
+      GNUNET_free (sctx->addrs);
+    }
   GNUNET_free_non_null (sctx->addrlens);
   GNUNET_free_non_null (sctx->v4_denied);
   GNUNET_free_non_null (sctx->v6_denied);