--- agent/gpg-agent.c.old	2016-01-10 07:26:21.000000000 +0100
+++ agent/gpg-agent.c	2016-01-10 07:28:04.000000000 +0100
@@ -47,6 +47,79 @@
 #include <unistd.h>
 #include <signal.h>
 #include <pth.h>
+#ifdef __APPLE_LAUNCHD__
+# include <launch.h>
+
+extern char **environ;
+
+# define GPG_ENV_SOCKET_AGENT "GPG_AGENT_INFO"
+# define GPG_ENV_SOCKET_SSH "SSH_AUTH_SOCK"
+# define GPG_ENV_PID_SSH "SSH_AGENT_PID"
+# define GPG_SOCKET_NAME_AGENT "Listeners_agent"
+# define GPG_SOCKET_NAME_SSH "Listeners_ssh"
+
+# define PASS_ENV_VAR_TO_LAUNCHD(KEY, VALUE)                                               \
+do                                                                                         \
+  {                                                                                        \
+    launch_data_t resp, msg, tmp, tmp_value;                                               \
+    msg = launch_data_alloc (LAUNCH_DATA_DICTIONARY);                                      \
+    tmp = launch_data_alloc (LAUNCH_DATA_DICTIONARY);                                      \
+                                                                                           \
+    if (!VALUE || !KEY || (strlen (KEY) == 0)) {                                           \
+      log_error ("invalid key or value given: `" #KEY "', `" #VALUE "'.\n");               \
+      exit (1);                                                                            \
+    }                                                                                      \
+                                                                                           \
+    char *value_start = strchr (VALUE, '=');                                               \
+    if (!value_start)                                                                      \
+      value_start = VALUE;                                                                 \
+    else                                                                                   \
+      ++value_start;                                                                       \
+                                                                                           \
+    tmp_value = launch_data_new_string (value_start); /* Skip variable name                \
+                                                       * and equal sign */                 \
+    launch_data_dict_insert (tmp, tmp_value, KEY);                                         \
+    launch_data_dict_insert (msg, tmp, "SetUserEnvironment");                              \
+                                                                                           \
+    resp = launch_msg (msg);                                                               \
+                                                                                           \
+    if (!resp) {                                                                           \
+      log_error ("failed to pass environment variable `" KEY "' to launchd: %s\n",         \
+                 strerror (errno));                                                        \
+      exit (1);                                                                            \
+    }                                                                                      \
+    launch_data_free (resp);                                                               \
+    launch_data_free (msg); /* Do NOT launch_data_free() neither on tmp, nor tmp_value */  \
+  }                                                                                        \
+while (0)
+
+# define REMOVE_ENV_VAR_FROM_LAUNCHD(ENV_VAR_NAME)                                                \
+do                                                                                                \
+  {                                                                                               \
+    launch_data_t resp, msg, tmp;                                                                 \
+    msg = launch_data_alloc (LAUNCH_DATA_DICTIONARY);                                             \
+                                                                                                  \
+    if (!ENV_VAR_NAME || (strlen (ENV_VAR_NAME) == 0)) {                                          \
+      log_error ("invalid environment variable name given: `" #ENV_VAR_NAME "'.\n");              \
+      exit (1);                                                                                   \
+    }                                                                                             \
+                                                                                                  \
+    tmp = launch_data_new_string (ENV_VAR_NAME);                                                  \
+    launch_data_dict_insert (msg, tmp, "UnsetUserEnvironment");                                   \
+                                                                                                  \
+    resp = launch_msg (msg);                                                                      \
+                                                                                                  \
+    if (!resp) {                                                                                  \
+      log_error ("failed to remove environment variable `" ENV_VAR_NAME "' from launchd: %s\n",   \
+                 strerror (errno));                                                               \
+    }                                                                                             \
+    launch_data_free (resp);                                                                      \
+    launch_data_free (msg); /* Do NOT launch_data_free() on tmp */                                \
+  }                                                                                               \
+while (0)
+
+
+#endif // __APPLE_LAUNCHD__
 
 #define JNLIB_NEED_LOG_LOGV
 #define JNLIB_NEED_AFLOCAL
@@ -85,6 +158,9 @@
   oLogFile,
   oServer,
   oDaemon,
+#ifdef __APPLE_LAUNCHD__
+  oLaunchd,
+#endif
   oBatch,
 
   oPinentryProgram,
@@ -134,6 +210,9 @@
   { 301, NULL, 0, N_("@Options:\n ") },
 
   { oDaemon,   "daemon",     0, N_("run in daemon mode (background)") },
+#ifdef __APPLE_LAUNCHD__
+  { oLaunchd,  "launchd",    0, N_("run controlled by launchd (foreground)") },
+#endif
   { oServer,   "server",     0, N_("run in server mode (foreground)") },
   { oVerbose, "verbose",     0, N_("verbose") },
   { oQuiet,	"quiet",     0, N_("be somewhat more quiet") },
@@ -478,6 +557,19 @@
 {
   remove_socket (socket_name);
   remove_socket (socket_name_ssh);
+
+#ifdef __APPLE_LAUNCHD__
+  /* Remove environment variables from launchd. */
+  /* launchd handles this. */
+  /*
+  REMOVE_ENV_VAR_FROM_LAUNCHD (GPG_ENV_SOCKET_AGENT);
+
+  if (opt.ssh_support) {
+    REMOVE_ENV_VAR_FROM_LAUNCHD (GPG_ENV_SOCKET_SSH);
+    REMOVE_ENV_VAR_FROM_LAUNCHD (GPG_ENV_PID_SSH);
+  }
+  */
+#endif // __APPLE_LAUNCHD__
 }
 
 
@@ -603,6 +695,9 @@
   int nogreeting = 0;
   int pipe_server = 0;
   int is_daemon = 0;
+#ifdef __APPLE_LAUNCHD__
+  int launchd_child = 0;
+#endif
   int nodetach = 0;
   int csh_style = 0;
   char *logfile = NULL;
@@ -823,6 +918,9 @@
         case oSh: csh_style = 0; break;
         case oServer: pipe_server = 1; break;
         case oDaemon: is_daemon = 1; break;
+#ifdef __APPLE_LAUNCHD__
+        case oLaunchd: launchd_child = 1; break;
+#endif
 
         case oDisplay: default_display = xstrdup (pargs.r.ret_str); break;
         case oTTYname: default_ttyname = xstrdup (pargs.r.ret_str); break;
@@ -865,6 +963,19 @@
         default : pargs.err = configfp? 1:2; break;
 	}
     }
+
+  /* When running under launchd control, only start for real users, i.e., UID >= 500
+   *
+   * Do this check early to avoid filling logs. */
+
+#ifdef __APPLE_LAUNCHD__
+  if (launchd_child && geteuid () < 500)
+    {
+      log_error ("launchd only supported for real users - i.e., UID >= 500.\nTrying to start for user ID %d\n", geteuid ());
+      exit (1);
+    }
+#endif
+
   if (configfp)
     {
       fclose( configfp );
@@ -992,7 +1103,11 @@
   /* If this has been called without any options, we merely check
      whether an agent is already running.  We do this here so that we
      don't clobber a logfile but print it directly to stderr. */
+#ifdef __APPLE_LAUNCHD__
+  if (!pipe_server && !is_daemon && !launchd_child)
+#else
   if (!pipe_server && !is_daemon)
+#endif
     {
       log_set_prefix (NULL, JNLIB_LOG_WITH_PREFIX);
       check_for_running_agent (0, 0);
@@ -1054,6 +1169,209 @@
       agent_deinit_default_ctrl (ctrl);
       xfree (ctrl);
     }
+#ifdef __APPLE_LAUNCHD__
+  else if (launchd_child)
+    { /* launchd-compatible mode */
+      gnupg_fd_t fd, fd_ssh = GNUPG_INVALID_FD;
+      pid_t pid;
+
+      /* 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
+       * client requested this using an OPTION command. Note, that we
+       * don't do this when running in reverse daemon mode (i.e., when
+       * execcuting the program given as arguments). */
+
+      if (!opt.keep_display && !argc)
+        unsetenv ("DISPLAY");
+
+      fflush (NULL);
+      pid = getpid ();
+
+      /* Must leave SSH support enabled - #46113. */
+      /* opt.ssh_support = 0; */
+
+      /* Fetch socket from launchd. */
+      launch_data_t checkin_request, checkin_response;
+
+      /* EHLO launchd */
+      if ((checkin_request = launch_data_new_string (LAUNCH_KEY_CHECKIN)) == NULL) {
+        log_error ("unable to create launchd checkin string.\n");
+        exit (1);
+      }
+
+      /* any answer */
+      if ((checkin_response = launch_msg (checkin_request)) == NULL) {
+        log_error ("unable to obtain launchd checkin answer.\n");
+        exit (1);
+      }
+
+      /* not 250 :( */
+      if (LAUNCH_DATA_ERRNO == launch_data_get_type (checkin_response)) {
+        int cur_errno = errno;
+        log_error ("launchd checkin failed: %s\n", strerror (cur_errno));
+        exit (1);
+      }
+
+      /* 250 */
+      launch_data_t socket_dict = launch_data_dict_lookup (checkin_response, LAUNCH_JOBKEY_SOCKETS);
+      if (socket_dict == NULL) {
+        log_error ("no sockets returned by launchd.\n");
+        exit (1);
+      }
+
+      {
+        size_t need_sockets = 1;
+
+        if (opt.ssh_support)
+          ++need_sockets;
+
+        size_t got_sockets = launch_data_dict_get_count (socket_dict);
+
+        if (got_sockets < need_sockets) {
+          log_error ("launchd returned less sockets than necessary. "
+                     "needed: %zu, given: %zu.\n", need_sockets, got_sockets);
+          exit (1);
+        }
+
+        if (got_sockets > need_sockets)
+          log_info ("launchd returned more sockets than needed - ignoring extraneous ones.\n");
+
+        /* Fetch FD array. */
+        launch_data_t data_array_agent, data_array_ssh;
+        if ((data_array_agent = launch_data_dict_lookup (socket_dict, GPG_SOCKET_NAME_AGENT)) == NULL) {
+          log_error ("No agent socket defined in launchd plist file.\n");
+          exit (1);
+        }
+
+        if (opt.ssh_support &&
+            ((data_array_ssh = launch_data_dict_lookup (socket_dict, GPG_SOCKET_NAME_SSH)) == NULL)) {
+          log_error ("No ssh socket defined in launchd plist file.\n");
+          exit (1);
+        }
+
+        size_t fd_count_agent, fd_count_ssh;
+        if ((fd_count_agent = launch_data_array_get_count (data_array_agent)) > 1)
+          log_info ("launchd returned more than one file descriptor for the agent socket - ignoring extraneous ones.\n");
+        else if (fd_count_agent == 0) {
+          log_error ("No file descriptor returned for the agent socket.\n");
+          exit (1);
+        }
+        /* This scope is merely here to easily get rid of "cur". */
+        {
+          launch_data_t cur = launch_data_array_get_index (data_array_agent, 0);
+          fd = launch_data_get_fd (cur);
+        }
+
+        if (opt.ssh_support) {
+          if ((fd_count_ssh = launch_data_array_get_count (data_array_ssh)) > 1)
+            log_info ("launchd returned more than one file descriptor for the SSH socket - ignoring extraneous ones.\n");
+          else if (fd_count_ssh == 0) {
+            log_error ("No file descriptor returned for the SSH socket.\n");
+            exit (1);
+          }
+          /* This scope is merely here to easily get rid of "cur". */
+          {
+            launch_data_t cur = launch_data_array_get_index (data_array_ssh, 0);
+            fd_ssh = launch_data_get_fd (cur);
+          }
+        }
+      }
+
+      char *gpg_socket = getenv (GPG_ENV_SOCKET_AGENT);
+      if (!gpg_socket) {
+        log_error ("No agent socket environment variable defined by launchd.\n");
+        exit (1);
+      }
+
+      char *ssh_socket = getenv (GPG_ENV_SOCKET_SSH);
+      if (opt.ssh_support && !ssh_socket) {
+        log_error ("No SSH socket environment variable defined by launchd.\n");
+        exit (1);
+      }
+
+      socket_name = strndup (gpg_socket, strlen (gpg_socket));
+      if (opt.ssh_support)
+        socket_name_ssh = strndup (ssh_socket, strlen (ssh_socket));
+
+      /* Remove internal environment variables from launchd. */
+      /* launchd handles this. */
+      /*
+      REMOVE_ENV_VAR_FROM_LAUNCHD (GPG_ENV_SOCKET_AGENT);
+      if (opt.ssh_support)
+        REMOVE_ENV_VAR_FROM_LAUNCHD (GPG_ENV_SOCKET_SSH);
+      */
+
+      launch_data_free (checkin_response);
+      launch_data_free (checkin_request);
+
+      char *infostr, *infostr_ssh_sock, *infostr_ssh_pid;
+
+      /* Create the info string: <name>:<pid>:<protocol_version> */
+      if (asprintf (&infostr, GPG_ENV_SOCKET_AGENT "=%s:%lu:1",
+                    socket_name, (ulong)pid ) < 0) {
+        log_error ("out of core\n");
+        kill (pid, SIGTERM);
+        exit (1);
+      }
+
+      if (opt.ssh_support) {
+        if (asprintf (&infostr_ssh_sock, GPG_ENV_SOCKET_SSH "=%s",
+                      socket_name_ssh) < 0) {
+          log_error ("out of core\n");
+          exit (1);
+        }
+        if (asprintf (&infostr_ssh_pid, GPG_ENV_PID_SSH "=%u",
+                      pid) < 0) {
+          log_error ("out of core\n");
+          exit (1);
+        }
+      }
+
+      if (env_file_name) {
+        FILE *fp;
+
+        fp = fopen (env_file_name, "w");
+        if (!fp)
+          log_error (_("error creating `%s': %s\n"),
+                       env_file_name, strerror (errno));
+        else {
+          fputs (infostr, fp);
+          putc ('\n', fp);
+
+          if (opt.ssh_support) {
+            fputs (infostr_ssh_sock, fp);
+            putc ('\n', fp);
+            fputs (infostr_ssh_pid, fp);
+            putc ('\n', fp);
+          }
+
+          fclose (fp);
+        }
+      }
+
+      /* Pass environment variables back to launchd. */
+      /* launchd handles this. */
+      /* PASS_ENV_VAR_TO_LAUNCHD (GPG_ENV_SOCKET_AGENT, infostr);
+
+      if (opt.ssh_support) {
+        PASS_ENV_VAR_TO_LAUNCHD (GPG_ENV_SOCKET_SSH, infostr_ssh_sock);
+        PASS_ENV_VAR_TO_LAUNCHD (GPG_ENV_PID_SSH, infostr_ssh_pid);
+      }
+      */
+
+      {
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+
+      handle_connections (fd, fd_ssh);
+    }
+#endif // __APPLE_LAUNCHD__
   else if (!is_daemon)
     ; /* NOTREACHED */
   else
