Mac launchd socket activation

Tomas Zahradnicky tomas.zahradnicky at boxtrap.net
Fri Jun 15 19:09:05 CEST 2018


Hi everyone,

I was trying to run gpg-agent in the supervised mode from macOS launchd.
There seem to be at leasttwo problems with this approach:

  1. launchd prohibits forking [1];
  2. in order to receive sockets from launchd, its
launchd_activate_socket API must be used.

The supervised mode does not break [1] but expects sockets to be
configured from file descriptor 3 on along with LISTEN_FDNAMES and
LISTEN_FDS environment variables configured. The proper way to acquire
sockets from launchd should be by using its API. Here's our patch for
gpg-agent using launchd_activate_socket against the master revision
including a sample launchd plist file using the socket activation.

All the best,
Tomas

References
---------
1. Apple, Inc. launchd.plist excerpt:
A daemon or agent launched by launchd MUST NOT do the following in the
process directly launched by launchd:

           o   Call daemon(3).
           o   Do the moral equivalent of daemon(3) by calling fork(2)
and have the parent process exit(3) or _exit(2).

Patch
-----
diff --git a/agent/agent.h b/agent/agent.h
index 9fdbc76d3..7ff2580d8 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -175,6 +175,10 @@ struct
   /* The value of the option --s2k-count.  If this option is not given
    * or 0 an auto-calibrated value is used.  */
   unsigned long s2k_count;
+  /* Launchd activation. macOS only. When enabled, the sockets will
+   * be acquired from launchd by calling launch_activate_socket.
+   * Usable in the supervised mode. */
+  int launchd_activate_sockets;
 } opt;
 
 
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 1fdc94d0f..6dfb10f35 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -30,6 +30,9 @@
 #include <time.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#ifdef __APPLE__
+#include <launch.h>
+#endif
 #ifdef HAVE_W32_SYSTEM
 # ifndef WINVER
 #  define WINVER 0x0500  /* Same as in common/sysutils.c */
@@ -137,6 +140,7 @@ enum cmd_and_opt_values
   oS2KCount,
   oAutoExpandSecmem,
   oListenBacklog,
+  oLaunchdActivateSockets,
 
   oWriteEnvFile
 };
@@ -263,6 +267,9 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_n (oUseStandardSocket, "use-standard-socket", "@"),
   ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"),
 
+  /* Launchd activation */
+  ARGPARSE_s_n (oLaunchdActivateSockets, "launchd-activate-sockets",
N_("activate sockets from launchd")),
+
   ARGPARSE_end () /* End of list */
 };
 
@@ -732,6 +739,30 @@ map_supervised_sockets (gnupg_fd_t *r_fd,
               if (!strcmp (fdnames[i], tbl[j].label) || j == DIM(tbl)-1)
                 {
                   fd = 3 + i;
+#if __APPLE__
+                    if( opt.launchd_activate_sockets )
+                    {
+                        int* fds = NULL;
+                        size_t cnt = 0;
+                        int err = launch_activate_socket( fdnames[i],
&fds, &cnt );
+                        if( err != 0 )
+                        {
+                            log_error("cannot activate socket %s
because of error %d\n", fdnames[i], err);
+                            break;
+                        }
+                        if( cnt > 0 )
+                        {
+                            fd = fds[0];
+                            free(fds);
+                        }
+                        else
+                        {
+                            log_error("no socket returned for
activation of socket %s\n", fdnames[i]);
+                            continue;
+                        }
+                    }
+#endif
+
                   if (**tbl[j].fdaddr == -1)
                     {
                       name = gnupg_get_socket_name (fd);
@@ -834,6 +865,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int
reread)
       /* Note: When changing the next line, change also gpgconf_list.  */
       opt.ssh_fingerprint_digest = GCRY_MD_MD5;
       opt.s2k_count = 0;
+      opt.launchd_activate_sockets = 0;
       return 1;
     }
 
@@ -1260,6 +1292,10 @@ main (int argc, char **argv )
           /* Only used by the first stage command line parser.  */
           break;
 
+        case oLaunchdActivateSockets:
+          opt.launchd_activate_sockets = 1;
+          break;
+
         case oWriteEnvFile:
           obsolete_option (configname, configlineno, "write-env-file");
           break;

My ~/Library/LaunchAgents/net.boxtrap.gpg-agent.plist file
----------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>net.boxtrap.gpg-agent</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/MacGPG2/bin/gpg-agent</string>
      <string>--supervised</string>
      <string>--launchd-activate-sockets</string>
      <string>--homedir</string>
      <string>/Users/zahradt/.gnupg/</string>
      <string>--enable-ssh-support</string>
      <string>--log-file</string>
      <string>/Users/zahradt/Library/Logs/gpg-agent.log</string>
    </array>
    <key>Sockets</key>
    <dict>
      <key>std</key>
      <dict>
        <key>SockFamily</key>
        <string>Unix</string>
        <key>SockPathMode</key>
        <integer>448</integer>
        <key>SockPathName</key>
        <string>/Users/zahradt/.gnupg/S.gpg-agent</string>
        <key>SockType</key>
        <string>Stream</string>
      </dict>
      <key>browser</key>
      <dict>
        <key>SockFamily</key>
        <string>Unix</string>
        <key>SockPathMode</key>
        <integer>448</integer>
        <key>SockPathName</key>
        <string>/Users/zahradt/.gnupg/S.gpg-browser</string>
        <key>SockType</key>
        <string>Stream</string>
      </dict>
      <key>extra</key>
      <dict>
        <key>SockFamily</key>
        <string>Unix</string>
        <key>SockPathMode</key>
        <integer>448</integer>
        <key>SockPathName</key>
        <string>/Users/zahradt/.gnupg/S.gpg-extra</string>
        <key>SockType</key>
        <string>Stream</string>
      </dict>
      <key>ssh</key>
      <dict>
        <key>SockFamily</key>
        <string>Unix</string>
        <key>SockPathMode</key>
        <integer>448</integer>
        <key>SockPathName</key>
        <string>/Users/zahradt/.gnupg/S.gpg-ssh</string>
        <key>SockType</key>
        <string>Stream</string>
      </dict>
    </dict>
    <key>EnvironmentVariables</key>
    <dict>
      <key>LISTEN_FDNAMES</key>
      <string>std:browser:extra:ssh</string>
      <key>LISTEN_FDS</key>
      <string>4</string>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/zahradt/Library/Logs/gpg-agent.stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/zahradt/Library/Logs/gpg-agent.error.log</string>
    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>


More information about the Gnupg-devel mailing list