[git] GnuPG - branch, master, updated. gnupg-2.1.15-197-g1a9c8d7

by Werner Koch cvs at cvs.gnupg.org
Tue Oct 4 17:17:58 CEST 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  1a9c8d78ece2f31fdb1a8e2be049aa71053061fa (commit)
       via  afcfae7959f39e7d85309b9496e1f1cf9acd5cc2 (commit)
       via  dc059af1ff007842e2633e686c87d05daf1d45e3 (commit)
       via  9f92b62a51d2d60f038fdbe01602865c5933fa95 (commit)
      from  4a232d23a8f51bebf9ee382e480248b4bde30f28 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 1a9c8d78ece2f31fdb1a8e2be049aa71053061fa
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Oct 4 17:02:49 2016 +0200

    agent: Streamline the supervised mode code.
    
    * agent/gpg-agent.c (get_socket_path): Rename to ...
    (get_socket_name): this.  This is to comply with the GNU coding guide.
    Use xtrymalloc instead of malloc.  Do not build for W32.
    (map_supervised_sockets): Use strtokenize and set the the socket names
    here.
    (main): Adjust for above change.  Do not close the socket.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 0ebba1e..1696e5a 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -576,6 +576,208 @@ remove_socket (char *name, char *redir_name)
 }
 
 
+/* Return a malloc'ed string that is the path to the passed
+ * unix-domain socket (or return NULL if this is not a valid
+ * unix-domain socket).  We use a plain int here because it is only
+ * used on Linux.
+ *
+ * FIXME: This function needs to be moved to libassuan.  */
+#ifndef HAVE_W32_SYSTEM
+static char *
+get_socket_name (int fd)
+{
+  struct sockaddr_un un;
+  socklen_t len = sizeof(un);
+  char *name = NULL;
+
+  if (getsockname (fd, (struct sockaddr*)&un, &len) != 0)
+    log_error ("could not getsockname(%d): %s\n", fd,
+               gpg_strerror (gpg_error_from_syserror ()));
+  else if (un.sun_family != AF_UNIX)
+    log_error ("file descriptor %d is not a unix-domain socket\n", fd);
+  else if (len <= offsetof (struct sockaddr_un, sun_path))
+    log_error ("socket name not present for file descriptor %d\n", fd);
+  else if (len > sizeof(un))
+    log_error ("socket name for file descriptor %d was truncated "
+               "(passed %lu bytes, wanted %u)\n", fd, sizeof(un), len);
+  else
+    {
+      log_debug ("file descriptor %d has path %s (%lu octets)\n", fd,
+                 un.sun_path, len - offsetof (struct sockaddr_un, sun_path));
+      name = xtrymalloc (len - offsetof (struct sockaddr_un, sun_path) + 1);
+      if (!name)
+        log_error ("failed to allocate memory for name of fd %d: %s\n",
+                   fd, gpg_strerror (gpg_error_from_syserror ()));
+      else
+        {
+          memcpy (name, un.sun_path, len);
+          name[len] = 0;
+        }
+    }
+
+  return name;
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Discover which inherited file descriptors correspond to which
+ * services/sockets offered by gpg-agent, using the LISTEN_FDS and
+ * LISTEN_FDNAMES convention.  The understood labels are "ssh",
+ * "extra", and "browser".  "std" or other labels will be interpreted
+ * as the standard socket.
+ *
+ * This function is designed to log errors when the expected file
+ * descriptors don't make sense, but to do its best to continue to
+ * work even in the face of minor misconfigurations.
+ *
+ * For more information on the LISTEN_FDS convention, see
+ * sd_listen_fds(3) on certain Linux distributions.
+ */
+#ifndef HAVE_W32_SYSTEM
+static void
+map_supervised_sockets (gnupg_fd_t *r_fd,
+                        gnupg_fd_t *r_fd_extra,
+                        gnupg_fd_t *r_fd_browser,
+                        gnupg_fd_t *r_fd_ssh)
+{
+  struct {
+    const char *label;
+    int **fdaddr;
+    char **nameaddr;
+  } tbl[] = {
+    { "ssh",     &r_fd_ssh,     &socket_name_ssh },
+    { "browser", &r_fd_browser, &socket_name_browser },
+    { "extra",   &r_fd_extra,   &socket_name_extra },
+    { "std",     &r_fd,         &socket_name }  /* (Must be the last item.)  */
+  };
+  const char *envvar;
+  char **fdnames;
+  int nfdnames;
+  int fd_count;
+
+  *r_fd = *r_fd_extra = *r_fd_browser = *r_fd_ssh = -1;
+
+  /* Print a warning if LISTEN_PID does not match outr pid.  */
+  envvar = getenv ("LISTEN_PID");
+  if (!envvar)
+    log_error ("no LISTEN_PID environment variable found in "
+               "--supervised mode (ignoring)\n");
+  else if (strtoul (envvar, NULL, 10) != (unsigned long)getpid ())
+    log_error ("environment variable LISTEN_PID (%lu) does not match"
+               " our pid (%lu) in --supervised mode (ignoring)\n",
+               (unsigned long)strtoul (envvar, NULL, 10),
+               (unsigned long)getpid ());
+
+  /* Parse LISTEN_FDNAMES into the array FDNAMES.  */
+  envvar = getenv ("LISTEN_FDNAMES");
+  if (envvar)
+    {
+      fdnames = strtokenize (envvar, ":");
+      if (!fdnames)
+        {
+          log_error ("strtokenize failed: %s\n",
+                     gpg_strerror (gpg_error_from_syserror ()));
+          agent_exit (1);
+        }
+      for (nfdnames=0; fdnames[nfdnames]; nfdnames++)
+        ;
+    }
+  else
+    {
+      fdnames = NULL;
+      nfdnames = 0;
+    }
+
+  /* Parse LISTEN_FDS into fd_count or provide a replacement.  */
+  envvar = getenv ("LISTEN_FDS");
+  if (envvar)
+    fd_count = atoi (envvar);
+  else if (fdnames)
+    {
+      log_error ("no LISTEN_FDS environment variable found in --supervised"
+                 " mode (relying on LISTEN_FDNAMES instead)\n");
+      fd_count = nfdnames;
+    }
+  else
+    {
+      log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
+                "found in --supervised mode"
+                " (assuming 1 active descriptor)\n");
+      fd_count = 1;
+    }
+
+  if (fd_count < 1)
+    {
+      log_error ("--supervised mode expects at least one file descriptor"
+                 " (was told %d, carrying on as though it were 1)\n",
+                 fd_count);
+      fd_count = 1;
+    }
+
+  /* Assign the descriptors to the return values.  */
+  if (!fdnames)
+    {
+      if (fd_count != 1)
+        log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1"
+                   " in --supervised mode."
+                   " (ignoring all sockets but the first one)\n",
+                   fd_count);
+      *r_fd = 3;
+    }
+  else if (fd_count != nfdnames)
+    {
+      log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
+                 "LISTEN_FDS (%d) in --supervised mode\n",
+                 nfdnames, fd_count);
+    }
+  else
+    {
+      int i, j, fd;
+      char *name;
+
+      for (i = 0; i < nfdnames; i++)
+        {
+          for (j = 0; j < DIM (tbl); j++)
+            {
+              log_debug ("i=%d j=%d fdname=%s check=%s\n", i, j,
+                         fdnames[i], tbl[j].label);
+              if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1)
+                {
+                  if (**tbl[j].fdaddr == -1)
+                    {
+                      fd = 3 + i;
+                      name = get_socket_name (fd);
+                      if (name)
+                        {
+                          **tbl[j].fdaddr = fd;
+                          *tbl[j].nameaddr = name;
+                          log_info ("using fd %d for %s socket (%s)\n",
+                                    fd, tbl[j].label, name);
+                        }
+                      else
+                        {
+                          log_error ("cannot listen on fd %d for %s socket\n",
+                                     fd, tbl[j].label);
+                          close (i);
+                        }
+                    }
+                  else
+                    {
+                      log_error ("cannot listen on more than one %s socket\n",
+                                 tbl[j].label);
+                      close (i);
+                    }
+                  break;
+                }
+            }
+        }
+    }
+
+  xfree (fdnames);
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
 /* Cleanup code for this program.  This is either called has an atexit
    handler or directly.  */
 static void
@@ -756,180 +958,6 @@ initialize_modules (void)
 }
 
 
-/* return a malloc'ed string that is the path to the passed unix-domain socket
-   (or return NULL if this is not a valid unix-domain socket) */
-static char *
-get_socket_path (gnupg_fd_t fd)
-{
-#ifdef HAVE_W32_SYSTEM
-  return NULL;
-#else
-  struct sockaddr_un un;
-  socklen_t len = sizeof(un);
-  char *ret = NULL;
-
-  if (fd == GNUPG_INVALID_FD)
-    return NULL;
-
-  if (getsockname (fd, (struct sockaddr*)&un, &len) != 0)
-    log_error ("could not getsockname(%d) -- error %d (%s)\n", fd,
-               errno, strerror(errno));
-  else if (un.sun_family != AF_UNIX)
-    log_error ("file descriptor %d is not a unix-domain socket\n", fd);
-  else if (len <= offsetof (struct sockaddr_un, sun_path))
-    log_error ("socket path not present for file descriptor %d\n", fd);
-  else if (len > sizeof(un))
-    log_error ("socket path for file descriptor %d was truncated "
-               "(passed %lu bytes, wanted %u)\n", fd, sizeof(un), len);
-  else
-    {
-      log_debug ("file descriptor %d has path %s (%lu octets)\n", fd,
-                 un.sun_path, len - offsetof (struct sockaddr_un, sun_path));
-      ret = malloc(len - offsetof (struct sockaddr_un, sun_path));
-      if (ret == NULL)
-        log_error ("failed to allocate memory for path to file "
-                   "descriptor %d\n", fd);
-      else
-        memcpy (ret, un.sun_path, len);
-    }
-  return ret;
-#endif /* HAVE_W32_SYSTEM */
-}
-
-
-/* Discover which inherited file descriptors correspond to which
-   services/sockets offered by gpg-agent, using the LISTEN_FDS and
-   LISTEN_FDNAMES convention.  The understood labels are "ssh",
-   "extra", and "browser".  Any other label will be interpreted as the
-   standard socket.
-
-   This function is designed to log errors when the expected file
-   descriptors don't make sense, but to do its best to continue to
-   work even in the face of minor misconfigurations.
-
-   For more information on the LISTEN_FDS convention, see
-   sd_listen_fds(3).
- */
-static void
-map_supervised_sockets (gnupg_fd_t *fd,
-                        gnupg_fd_t *fd_extra,
-                        gnupg_fd_t *fd_browser,
-                        gnupg_fd_t *fd_ssh)
-{
-  const char *listen_pid = NULL;
-  const char *listen_fds = NULL;
-  const char *listen_fdnames = NULL;
-  int listen_fd_count = -1;
-  int listen_fdnames_colons = 0;
-  const char *fdnamep = NULL;
-
-  listen_pid = getenv ("LISTEN_PID");
-  listen_fds = getenv ("LISTEN_FDS");
-  listen_fdnames = getenv ("LISTEN_FDNAMES");
-
-  if (!listen_pid)
-    log_error ("no $LISTEN_PID environment variable found in "
-               "--supervised mode (ignoring).\n");
-  else if (atoi (listen_pid) != getpid ())
-    log_error ("$LISTEN_PID (%d) does not match process ID (%d) "
-               "in --supervised mode (ignoring).\n",
-               atoi (listen_pid), getpid ());
-  else
-    log_debug ("$LISTEN_PID matches process ID (%d)\n",
-               getpid());
-
-  if (listen_fdnames)
-    for (fdnamep = listen_fdnames; *fdnamep; fdnamep++)
-      if (*fdnamep == ':')
-        listen_fdnames_colons++;
-  log_debug ("%d colon(s) in $LISTEN_FDNAMES: (%s)\n", listen_fdnames_colons, listen_fdnames);
-
-  if (!listen_fds)
-    {
-      if (!listen_fdnames)
-        {
-          log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
-                     "found in --supervised mode (assuming 1 active descriptor).\n");
-          listen_fd_count = 1;
-        }
-      else
-        {
-          log_error ("no LISTEN_FDS environment variable found in --supervised "
-                     " mode (relying on colons in LISTEN_FDNAMES instead)\n");
-          listen_fd_count = listen_fdnames_colons + 1;
-        }
-    }
-  else
-    listen_fd_count = atoi (listen_fds);
-
-  if (listen_fd_count < 1)
-    {
-      log_error ("--supervised mode expects at least one file descriptor (was told %d) "
-                 "(carrying on as though it were 1)\n", listen_fd_count);
-      listen_fd_count = 1;
-    }
-
-  if (!listen_fdnames)
-    {
-      if (listen_fd_count != 1)
-        log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1 in --supervised mode. "
-                   "(ignoring all sockets but the first one)\n", listen_fd_count);
-      *fd = 3;
-    }
-  else
-    {
-      int i;
-      if (listen_fd_count != listen_fdnames_colons + 1)
-        {
-          log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
-                     "LISTEN_FDS (%d) in --supervised mode\n",
-                     listen_fdnames_colons + 1, listen_fd_count);
-          exit (1);
-        }
-
-      for (i = 3; i < 3 + listen_fd_count; i++)
-        {
-          int found = 0;
-          char *next = strchrnul(listen_fdnames, ':');
-          *next = '\0';
-#define match_socket(var) if (!found && strcmp (listen_fdnames, #var) == 0) \
-            {                                                           \
-              found = 1;                                                \
-              if (*fd_ ## var == GNUPG_INVALID_FD)                      \
-                {                                                       \
-                  *fd_ ## var = i;                                      \
-                  log_info (#var " socket on fd %d\n", i);              \
-                }                                                       \
-              else                                                      \
-                {                                                       \
-                  log_error ("cannot listen on more than one " #var " socket. (closing fd %d)\n", i); \
-                  close (i);                                            \
-                }                                                       \
-            }
-          match_socket(ssh);
-          match_socket(browser);
-          match_socket(extra);
-#undef match_socket
-          if (!found)
-            {
-              if (*fd == GNUPG_INVALID_FD)
-                {
-                  *fd = i;
-                  log_info ("standard socket (\"%s\") on fd %d\n",
-                            listen_fdnames, i);
-                }
-              else
-                {
-                  log_error ("cannot listen on more than one standard socket. (closing fd %d)\n", i);
-                  close (i);
-                }
-            }
-          listen_fdnames = next + 1;
-        }
-    }
-}
-
-
 /* The main entry point.  */
 int
 main (int argc, char **argv )
@@ -1425,10 +1453,8 @@ main (int argc, char **argv )
     }
   else if (is_supervised)
     {
-      gnupg_fd_t fd = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
-      gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
+#ifndef HAVE_W32_SYSTEM
+      gnupg_fd_t fd, fd_extra, fd_browser, fd_ssh;
 
       /* when supervised and sending logs to stderr, the process
          supervisor should handle log entry metadata (pid, name,
@@ -1439,29 +1465,17 @@ main (int argc, char **argv )
       log_info ("%s %s starting in supervised mode.\n",
                 strusage(11), strusage(13) );
 
-      /* See below on why we remove certain envvars.  */
-#ifndef HAVE_W32_SYSTEM
+      /* See below in "regular server mode" on why we remove certain
+       * envvars.  */
       if (!opt.keep_display)
         gnupg_unsetenv ("DISPLAY");
-#endif
       gnupg_unsetenv ("INSIDE_EMACS");
 
-      /* Virtually create the sockets.  */
+      /* Virtually create the sockets.  Note that we use -1 here
+       * because the whole thing works only on Unix. */
       map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh);
-      if (fd == GNUPG_INVALID_FD)
-        {
-          log_error ("no standard socket provided\n");
-          agent_exit (1);
-        }
-      /* record socket names where possible: */
-      socket_name = get_socket_path (fd);
-      socket_name_extra = get_socket_path (fd_extra);
-      if (socket_name_extra)
-        opt.extra_socket = 2;
-      socket_name_browser = get_socket_path (fd_browser);
-      if (socket_name_browser)
-        opt.browser_socket = 2;
-      socket_name_ssh = get_socket_path (fd_ssh);
+      if (fd == -1)
+        log_fatal ("no standard socket provided\n");
 
 #ifdef HAVE_SIGPROCMASK
       if (startup_signal_mask_valid)
@@ -1477,7 +1491,7 @@ main (int argc, char **argv )
       log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n",
                 fd, fd_extra, fd_browser, fd_ssh);
       handle_connections (fd, fd_extra, fd_browser, fd_ssh);
-      assuan_sock_close (fd);
+#endif /*!HAVE_W32_SYSTEM*/
     }
   else if (!is_daemon)
     ; /* NOTREACHED */
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index 7aacb7b..cc10a79 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -164,12 +164,14 @@ shell, gpg-agent terminates within a few seconds.
 Run in the foreground, sending logs by default to stderr, and
 listening on provided file descriptors, which must already be bound to
 listening sockets.  This command is useful when running under systemd
-or other similar process supervision schemes.
+or other similar process supervision schemes.  This option is not
+supported on Windows.
 
 In --supervised mode, different file descriptors can be provided for
 use as different socket types (e.g. ssh, extra) as long as they are
-identified in the environment variable $LISTEN_FDNAMES (see
-sd_listen_fds(3) for more information on this convention).
+identified in the environment variable @code{LISTEN_FDNAMES} (see
+sd_listen_fds(3) on some Linux distributions for more information on
+this convention).
 @end table
 
 @mansect options

commit afcfae7959f39e7d85309b9496e1f1cf9acd5cc2
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Oct 4 16:57:55 2016 +0200

    agent: Adjust cleanup for supervised mode.  Fix for W32.
    
    * agent/gpg-agent.c (opts) [W32]: Remove option --supervised.
    (is_supervised): Move from main() to global.
    (inhibit_socket_removal): New.
    (cleanup): Take care of supervise mode and INHIBIT_SOCKET_REMOVAL.
    (check_own_socket_thread): Set INHIBIT_SOCKET_REMOVAL instead of
    seting the socket names to empty.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 3e938f5..0ebba1e 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -153,7 +153,9 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_n (oDaemon,  "daemon", N_("run in daemon mode (background)")),
   ARGPARSE_s_n (oServer,  "server", N_("run in server mode (foreground)")),
-  ARGPARSE_s_n (oSupervised,  "supervised", N_("run supervised (e.g., systemd)")),
+#ifndef HAVE_W32_SYSTEM
+  ARGPARSE_s_n (oSupervised,  "supervised", N_("run in supervised mode")),
+#endif
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
   ARGPARSE_s_n (oQuiet,	  "quiet",     N_("be somewhat more quiet")),
   ARGPARSE_s_n (oSh,	  "sh",        N_("sh-style command output")),
@@ -323,6 +325,12 @@ static int check_own_socket_running;
 /* Flags to indicate that check_own_socket shall not be called.  */
 static int disable_check_own_socket;
 
+/* Flag indicating that we are in supervised mode.  */
+static int is_supervised;
+
+/* Flag to inhibit socket removal in cleanup.  */
+static int inhibit_socket_removal;
+
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
@@ -579,12 +587,15 @@ cleanup (void)
     return;
   done = 1;
   deinitialize_module_cache ();
-  remove_socket (socket_name, redir_socket_name);
-  if (opt.extra_socket > 1)
-    remove_socket (socket_name_extra, redir_socket_name_extra);
-  if (opt.browser_socket > 1)
-    remove_socket (socket_name_browser, redir_socket_name_browser);
-  remove_socket (socket_name_ssh, redir_socket_name_ssh);
+  if (!is_supervised && !inhibit_socket_removal)
+    {
+      remove_socket (socket_name, redir_socket_name);
+      if (opt.extra_socket > 1)
+        remove_socket (socket_name_extra, redir_socket_name_extra);
+      if (opt.browser_socket > 1)
+        remove_socket (socket_name_browser, redir_socket_name_browser);
+      remove_socket (socket_name_ssh, redir_socket_name_ssh);
+    }
 }
 
 
@@ -934,7 +945,6 @@ main (int argc, char **argv )
   int default_config =1;
   int pipe_server = 0;
   int is_daemon = 0;
-  int is_supervised = 0;
   int nodetach = 0;
   int csh_style = 0;
   char *logfile = NULL;
@@ -3055,11 +3065,8 @@ check_own_socket_thread (void *arg)
   if (rc)
     {
       /* We may not remove the socket as it is now in use by another
-         server.  Setting the name to empty does this.  */
-      if (socket_name)
-        *socket_name = 0;
-      if (socket_name_ssh)
-        *socket_name_ssh = 0;
+         server. */
+      inhibit_socket_removal = 1;
       shutdown_pending = 2;
       log_info ("this process is useless - shutting down\n");
     }

commit dc059af1ff007842e2633e686c87d05daf1d45e3
Author: Werner Koch <wk at gnupg.org>
Date:   Tue Oct 4 11:23:18 2016 +0200

    agent: Adjust supervised mode for the new default socket names.
    
    * agent/gpg-agent.c (main): In supervised mode do not provide default
    socket names.  Unset DISPLAY and INSIDE_EMACS.  Use log_error and
    agent_exit.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 9221dc3..3e938f5 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -1244,10 +1244,10 @@ main (int argc, char **argv )
       agent_exit (0);
     }
 
-  if (! opt.extra_socket)
-    {
-      opt.extra_socket = 1;
-    }
+  if (is_supervised)
+    ;
+  else if (!opt.extra_socket)
+    opt.extra_socket = 1;
   else if (socket_name_extra
            && (!strcmp (socket_name_extra, "none")
                || !strcmp (socket_name_extra, "/dev/null")))
@@ -1257,10 +1257,10 @@ main (int argc, char **argv )
       socket_name_extra = NULL;
     }
 
-  if (! opt.browser_socket)
-    {
-      opt.browser_socket = 1;
-    }
+  if (is_supervised)
+    ;
+  else if (!opt.browser_socket)
+    opt.browser_socket = 1;
   else if (socket_name_browser
            && (!strcmp (socket_name_browser, "none")
                || !strcmp (socket_name_browser, "/dev/null")))
@@ -1429,11 +1429,19 @@ main (int argc, char **argv )
       log_info ("%s %s starting in supervised mode.\n",
                 strusage(11), strusage(13) );
 
+      /* See below on why we remove certain envvars.  */
+#ifndef HAVE_W32_SYSTEM
+      if (!opt.keep_display)
+        gnupg_unsetenv ("DISPLAY");
+#endif
+      gnupg_unsetenv ("INSIDE_EMACS");
+
+      /* Virtually create the sockets.  */
       map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh);
       if (fd == GNUPG_INVALID_FD)
         {
-          log_fatal ("no standard socket provided\n");
-          exit (1);
+          log_error ("no standard socket provided\n");
+          agent_exit (1);
         }
       /* record socket names where possible: */
       socket_name = get_socket_path (fd);
@@ -1456,8 +1464,8 @@ main (int argc, char **argv )
         log_info ("no saved signal mask\n");
 #endif /*HAVE_SIGPROCMASK*/
 
-      log_debug ("FDs: std: %d extra: %d browser: %d ssh: %d\n",
-                 fd, fd_extra, fd_browser, fd_ssh);
+      log_info ("listening on: std=%d extra=%d browser=%d ssh=%d\n",
+                fd, fd_extra, fd_browser, fd_ssh);
       handle_connections (fd, fd_extra, fd_browser, fd_ssh);
       assuan_sock_close (fd);
     }

commit 9f92b62a51d2d60f038fdbe01602865c5933fa95
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Fri Aug 12 01:37:59 2016 -0400

    agent: Implement --supervised command (for systemd, etc).
    
    * agent/gpg-agent.c (get_socket_path): New function for POSIX systems
    to return the path for a provided unix-domain socket.
    (map_supervised_sockets): New function to inspect $LISTEN_FDS and
    $LISTEN_FDNAMES and map them to the specific functionality offered by
    the agent.
    (main): Add --supervised command.  When used, listen on already-open
    file descriptors instead of opening our own.
    * doc/gpg-agent.texi: Document --supervised option.
    
    --
    
    "gpg-agent --supervised" is a way to invoke gpg-agent such that a
    system supervisor like systemd can provide socket-activated startup,
    log management, and scheduled shutdown.
    
    When running in this mode, gpg-agent:
    
     * Does not open its own listening socket; rather, it expects to be
       given a listening socket on incoming file descriptors.
    
     * Does not detach from the invoking process, staying in the
       foreground instead.  Unless otherwise specified, logs are sent to
       stderr.
    
    Signed-off-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>

diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index e65198a..9221dc3 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -90,6 +90,7 @@ enum cmd_and_opt_values
   oLogFile,
   oServer,
   oDaemon,
+  oSupervised,
   oBatch,
 
   oPinentryProgram,
@@ -152,6 +153,7 @@ static ARGPARSE_OPTS opts[] = {
 
   ARGPARSE_s_n (oDaemon,  "daemon", N_("run in daemon mode (background)")),
   ARGPARSE_s_n (oServer,  "server", N_("run in server mode (foreground)")),
+  ARGPARSE_s_n (oSupervised,  "supervised", N_("run supervised (e.g., systemd)")),
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
   ARGPARSE_s_n (oQuiet,	  "quiet",     N_("be somewhat more quiet")),
   ARGPARSE_s_n (oSh,	  "sh",        N_("sh-style command output")),
@@ -730,6 +732,7 @@ thread_init_once (void)
     }
 }
 
+
 static void
 initialize_modules (void)
 {
@@ -742,6 +745,180 @@ initialize_modules (void)
 }
 
 
+/* return a malloc'ed string that is the path to the passed unix-domain socket
+   (or return NULL if this is not a valid unix-domain socket) */
+static char *
+get_socket_path (gnupg_fd_t fd)
+{
+#ifdef HAVE_W32_SYSTEM
+  return NULL;
+#else
+  struct sockaddr_un un;
+  socklen_t len = sizeof(un);
+  char *ret = NULL;
+
+  if (fd == GNUPG_INVALID_FD)
+    return NULL;
+
+  if (getsockname (fd, (struct sockaddr*)&un, &len) != 0)
+    log_error ("could not getsockname(%d) -- error %d (%s)\n", fd,
+               errno, strerror(errno));
+  else if (un.sun_family != AF_UNIX)
+    log_error ("file descriptor %d is not a unix-domain socket\n", fd);
+  else if (len <= offsetof (struct sockaddr_un, sun_path))
+    log_error ("socket path not present for file descriptor %d\n", fd);
+  else if (len > sizeof(un))
+    log_error ("socket path for file descriptor %d was truncated "
+               "(passed %lu bytes, wanted %u)\n", fd, sizeof(un), len);
+  else
+    {
+      log_debug ("file descriptor %d has path %s (%lu octets)\n", fd,
+                 un.sun_path, len - offsetof (struct sockaddr_un, sun_path));
+      ret = malloc(len - offsetof (struct sockaddr_un, sun_path));
+      if (ret == NULL)
+        log_error ("failed to allocate memory for path to file "
+                   "descriptor %d\n", fd);
+      else
+        memcpy (ret, un.sun_path, len);
+    }
+  return ret;
+#endif /* HAVE_W32_SYSTEM */
+}
+
+
+/* Discover which inherited file descriptors correspond to which
+   services/sockets offered by gpg-agent, using the LISTEN_FDS and
+   LISTEN_FDNAMES convention.  The understood labels are "ssh",
+   "extra", and "browser".  Any other label will be interpreted as the
+   standard socket.
+
+   This function is designed to log errors when the expected file
+   descriptors don't make sense, but to do its best to continue to
+   work even in the face of minor misconfigurations.
+
+   For more information on the LISTEN_FDS convention, see
+   sd_listen_fds(3).
+ */
+static void
+map_supervised_sockets (gnupg_fd_t *fd,
+                        gnupg_fd_t *fd_extra,
+                        gnupg_fd_t *fd_browser,
+                        gnupg_fd_t *fd_ssh)
+{
+  const char *listen_pid = NULL;
+  const char *listen_fds = NULL;
+  const char *listen_fdnames = NULL;
+  int listen_fd_count = -1;
+  int listen_fdnames_colons = 0;
+  const char *fdnamep = NULL;
+
+  listen_pid = getenv ("LISTEN_PID");
+  listen_fds = getenv ("LISTEN_FDS");
+  listen_fdnames = getenv ("LISTEN_FDNAMES");
+
+  if (!listen_pid)
+    log_error ("no $LISTEN_PID environment variable found in "
+               "--supervised mode (ignoring).\n");
+  else if (atoi (listen_pid) != getpid ())
+    log_error ("$LISTEN_PID (%d) does not match process ID (%d) "
+               "in --supervised mode (ignoring).\n",
+               atoi (listen_pid), getpid ());
+  else
+    log_debug ("$LISTEN_PID matches process ID (%d)\n",
+               getpid());
+
+  if (listen_fdnames)
+    for (fdnamep = listen_fdnames; *fdnamep; fdnamep++)
+      if (*fdnamep == ':')
+        listen_fdnames_colons++;
+  log_debug ("%d colon(s) in $LISTEN_FDNAMES: (%s)\n", listen_fdnames_colons, listen_fdnames);
+
+  if (!listen_fds)
+    {
+      if (!listen_fdnames)
+        {
+          log_error ("no LISTEN_FDS or LISTEN_FDNAMES environment variables "
+                     "found in --supervised mode (assuming 1 active descriptor).\n");
+          listen_fd_count = 1;
+        }
+      else
+        {
+          log_error ("no LISTEN_FDS environment variable found in --supervised "
+                     " mode (relying on colons in LISTEN_FDNAMES instead)\n");
+          listen_fd_count = listen_fdnames_colons + 1;
+        }
+    }
+  else
+    listen_fd_count = atoi (listen_fds);
+
+  if (listen_fd_count < 1)
+    {
+      log_error ("--supervised mode expects at least one file descriptor (was told %d) "
+                 "(carrying on as though it were 1)\n", listen_fd_count);
+      listen_fd_count = 1;
+    }
+
+  if (!listen_fdnames)
+    {
+      if (listen_fd_count != 1)
+        log_error ("no LISTEN_FDNAMES and LISTEN_FDS (%d) != 1 in --supervised mode. "
+                   "(ignoring all sockets but the first one)\n", listen_fd_count);
+      *fd = 3;
+    }
+  else
+    {
+      int i;
+      if (listen_fd_count != listen_fdnames_colons + 1)
+        {
+          log_fatal ("number of items in LISTEN_FDNAMES (%d) does not match "
+                     "LISTEN_FDS (%d) in --supervised mode\n",
+                     listen_fdnames_colons + 1, listen_fd_count);
+          exit (1);
+        }
+
+      for (i = 3; i < 3 + listen_fd_count; i++)
+        {
+          int found = 0;
+          char *next = strchrnul(listen_fdnames, ':');
+          *next = '\0';
+#define match_socket(var) if (!found && strcmp (listen_fdnames, #var) == 0) \
+            {                                                           \
+              found = 1;                                                \
+              if (*fd_ ## var == GNUPG_INVALID_FD)                      \
+                {                                                       \
+                  *fd_ ## var = i;                                      \
+                  log_info (#var " socket on fd %d\n", i);              \
+                }                                                       \
+              else                                                      \
+                {                                                       \
+                  log_error ("cannot listen on more than one " #var " socket. (closing fd %d)\n", i); \
+                  close (i);                                            \
+                }                                                       \
+            }
+          match_socket(ssh);
+          match_socket(browser);
+          match_socket(extra);
+#undef match_socket
+          if (!found)
+            {
+              if (*fd == GNUPG_INVALID_FD)
+                {
+                  *fd = i;
+                  log_info ("standard socket (\"%s\") on fd %d\n",
+                            listen_fdnames, i);
+                }
+              else
+                {
+                  log_error ("cannot listen on more than one standard socket. (closing fd %d)\n", i);
+                  close (i);
+                }
+            }
+          listen_fdnames = next + 1;
+        }
+    }
+}
+
+
 /* The main entry point.  */
 int
 main (int argc, char **argv )
@@ -757,6 +934,7 @@ main (int argc, char **argv )
   int default_config =1;
   int pipe_server = 0;
   int is_daemon = 0;
+  int is_supervised = 0;
   int nodetach = 0;
   int csh_style = 0;
   char *logfile = NULL;
@@ -954,6 +1132,7 @@ main (int argc, char **argv )
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
         case oDaemon: is_daemon = 1; break;
+        case oSupervised: is_supervised = 1; break;
 
         case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
         case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
@@ -1053,9 +1232,9 @@ main (int argc, char **argv )
     bind_textdomain_codeset (PACKAGE_GT, "UTF-8");
 #endif
 
-  if (!pipe_server && !is_daemon && !gpgconf_list)
+  if (!pipe_server && !is_daemon && !gpgconf_list && !is_supervised)
     {
-     /* We have been called without any options and thus we merely
+     /* We have been called without any command and thus we merely
         check whether an agent is already running.  We do this right
         here so that we don't clobber a logfile with this check but
         print the status directly to stderr. */
@@ -1234,6 +1413,54 @@ main (int argc, char **argv )
       agent_deinit_default_ctrl (ctrl);
       xfree (ctrl);
     }
+  else if (is_supervised)
+    {
+      gnupg_fd_t fd = GNUPG_INVALID_FD;
+      gnupg_fd_t fd_extra = GNUPG_INVALID_FD;
+      gnupg_fd_t fd_browser = GNUPG_INVALID_FD;
+      gnupg_fd_t fd_ssh = GNUPG_INVALID_FD;
+
+      /* when supervised and sending logs to stderr, the process
+         supervisor should handle log entry metadata (pid, name,
+         timestamp) */
+      if (!logfile)
+        log_set_prefix (NULL, 0);
+
+      log_info ("%s %s starting in supervised mode.\n",
+                strusage(11), strusage(13) );
+
+      map_supervised_sockets (&fd, &fd_extra, &fd_browser, &fd_ssh);
+      if (fd == GNUPG_INVALID_FD)
+        {
+          log_fatal ("no standard socket provided\n");
+          exit (1);
+        }
+      /* record socket names where possible: */
+      socket_name = get_socket_path (fd);
+      socket_name_extra = get_socket_path (fd_extra);
+      if (socket_name_extra)
+        opt.extra_socket = 2;
+      socket_name_browser = get_socket_path (fd_browser);
+      if (socket_name_browser)
+        opt.browser_socket = 2;
+      socket_name_ssh = get_socket_path (fd_ssh);
+
+#ifdef HAVE_SIGPROCMASK
+      if (startup_signal_mask_valid)
+        {
+          if (sigprocmask (SIG_SETMASK, &startup_signal_mask, NULL))
+            log_error ("error restoring signal mask: %s\n",
+                       strerror (errno));
+        }
+      else
+        log_info ("no saved signal mask\n");
+#endif /*HAVE_SIGPROCMASK*/
+
+      log_debug ("FDs: std: %d extra: %d browser: %d ssh: %d\n",
+                 fd, fd_extra, fd_browser, fd_ssh);
+      handle_connections (fd, fd_extra, fd_browser, fd_ssh);
+      assuan_sock_close (fd);
+    }
   else if (!is_daemon)
     ; /* NOTREACHED */
   else
@@ -1246,6 +1473,8 @@ main (int argc, char **argv )
       pid_t pid;
 #endif
 
+      initialize_modules ();
+
       /* Remove the DISPLAY variable so that a pinentry does not
          default to a specific display.  There is still a default
          display when gpg-agent was started using --display or a
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index 0bb7db2..7aacb7b 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -158,6 +158,18 @@ As an alternative you may create a new process as a child of
 gpg-agent: @code{gpg-agent --daemon /bin/sh}.  This way you get a new
 shell with the environment setup properly; after you exit from this
 shell, gpg-agent terminates within a few seconds.
+
+ at item --supervised
+ at opindex supervised
+Run in the foreground, sending logs by default to stderr, and
+listening on provided file descriptors, which must already be bound to
+listening sockets.  This command is useful when running under systemd
+or other similar process supervision schemes.
+
+In --supervised mode, different file descriptors can be provided for
+use as different socket types (e.g. ssh, extra) as long as they are
+identified in the environment variable $LISTEN_FDNAMES (see
+sd_listen_fds(3) for more information on this convention).
 @end table
 
 @mansect options

-----------------------------------------------------------------------

Summary of changes:
 agent/gpg-agent.c  | 300 +++++++++++++++++++++++++++++++++++++++++++++++++----
 doc/gpg-agent.texi |  14 +++
 2 files changed, 293 insertions(+), 21 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list