New version. Instead of reimplementing the gpg-agent logic, "gpgconf
--list-dir agent-socket" is used. If the gpgconf fails, the code falls
back to the original solution to stay compatible with old Gnupg
versions.

[[[
Find gpg-agent socket using gpgconf if possible.
This allows detection of socket with Gnupg >= 2.1.13
which changed the default socket path to /run/user/UID/gnupg

* subversion/libsvn_subr/gpg_agent.c
    (find_gpgconf_agent_socket): new function to find
    gpg-agent socket using gpgconf
    (find_running_gpg_agent): use find_gpgconf_agent_socket
    to detect socket when possible.
]]]
Index: libsvn_subr/gpg_agent.c
===================================================================
*** libsvn_subr/gpg_agent.c     (revision 1793701)
--- libsvn_subr/gpg_agent.c     (working copy)
***************
*** 64,73 ****
--- 64,76 ----
  #include <sys/socket.h>
  #include <sys/un.h>
  
+ #include <apr_file_io.h>
  #include <apr_pools.h>
+ #include <apr_strings.h>
  #include "svn_auth.h"
  #include "svn_config.h"
  #include "svn_error.h"
+ #include "svn_io.h"
  #include "svn_pools.h"
  #include "svn_cmdline.h"
  #include "svn_checksum.h"
*************** bye_gpg_agent(int sd)
*** 225,230 ****
--- 228,280 ----
    close(sd);
  }
  
+ /* Find gpg-agent socket location using gpgconf. Returns the path to socket, 
or
+  * NULL if the socket path cannot be determined using gpgconf.
+  */
+ static const char *
+ find_gpgconf_agent_socket(apr_pool_t *pool)
+ {
+   apr_pool_t *scratch_pool;
+   apr_proc_t proc;
+   svn_stringbuf_t *line;
+   svn_error_t *err;
+   const char *gpgargv[] = { "gpgconf", "--list-dir", "agent-socket", NULL };
+ 
+   scratch_pool = svn_pool_create(pool);
+ 
+   /* execute "gpgconf --list-dir agent-socket" */
+   err = svn_io_start_cmd3(&proc, NULL, "gpgconf", (const char* const*)gpgargv,
+                           NULL, TRUE, FALSE, NULL, TRUE, NULL, FALSE, NULL,
+                           scratch_pool);
+   if (err != SVN_NO_ERROR)
+     {
+       svn_pool_destroy(scratch_pool);
+       svn_error_clear(err);
+       return NULL;
+     }
+ 
+   /* read the gpgconf output */
+   err = svn_io_file_readline(proc.out, &line, NULL, NULL, APR_SIZE_MAX, pool,
+                              scratch_pool);
+   if (err != SVN_NO_ERROR)
+     {
+       svn_pool_destroy(scratch_pool);
+       svn_error_clear(err);
+       return NULL;
+     }
+   apr_file_close(proc.out);
+   err = svn_io_wait_for_cmd(&proc, "gpgconf", NULL, NULL, scratch_pool);
+   if (err != SVN_NO_ERROR)
+     {
+       svn_pool_destroy(scratch_pool);
+       svn_error_clear(err);
+       return NULL;
+     }
+ 
+   svn_pool_destroy(scratch_pool);
+   return line->data;
+ }
+ 
  /* Locate a running GPG Agent, and return an open file descriptor
   * for communication with the agent in *NEW_SD. If no running agent
   * can be found, set *NEW_SD to -1. */
*************** find_running_gpg_agent(int *new_sd, apr_
*** 242,278 ****
  
    *new_sd = -1;
  
!   /* This implements the method of finding the socket as described in
!    * the gpg-agent man page under the --use-standard-socket option.
!    * The manage page says the standard socket is "named 'S.gpg-agent' located
!    * in the home directory."  GPG's home directory is either the directory
!    * specified by $GNUPGHOME or ~/.gnupg. */
!   gpg_agent_info = getenv("GPG_AGENT_INFO");
!   if (gpg_agent_info != NULL)
!     {
!       apr_array_header_t *socket_details;
  
!       /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
!        * The path to the socket, the pid of the gpg-agent process and
!        * finally the version of the protocol the agent talks. */
!       socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
!                                          pool);
!       socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
!     }
!   else if ((gnupghome = getenv("GNUPGHOME")) != NULL)
!     {
!       const char *homedir = svn_dirent_canonicalize(gnupghome, pool);
!       socket_name = svn_dirent_join(homedir, "S.gpg-agent", pool);
!     }
!   else
      {
!       const char *homedir = svn_user_get_homedir(pool);
  
!       if (!homedir)
!         return SVN_NO_ERROR;
  
!       socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
!                                          "S.gpg-agent", SVN_VA_NULL);
      }
  
    if (socket_name != NULL)
--- 292,334 ----
  
    *new_sd = -1;
  
!   /* Query socket location using gpgconf if possible */
!   socket_name = find_gpgconf_agent_socket(pool);
  
!   /* fallback to the old method used with Gnupg 1.x */
!   if (socket_name == NULL)
      {
!       /* This implements the method of finding the socket as described in
!        * the gpg-agent man page under the --use-standard-socket option.
!        * The manage page says the standard socket is "named 'S.gpg-agent' 
located
!        * in the home directory."  GPG's home directory is either the directory
!        * specified by $GNUPGHOME or ~/.gnupg. */
!       if ((gpg_agent_info = getenv("GPG_AGENT_INFO")) != NULL)
!         {
!           apr_array_header_t *socket_details;
  
!           /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
!            * The path to the socket, the pid of the gpg-agent process and
!            * finally the version of the protocol the agent talks. */
!           socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
!                                              pool);
!           socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
!         }
!       else if ((gnupghome = getenv("GNUPGHOME")) != NULL)
!         {
!           const char *homedir = svn_dirent_canonicalize(gnupghome, pool);
!           socket_name = svn_dirent_join(homedir, "S.gpg-agent", pool);
!         }
!       else
!         {
!           const char *homedir = svn_user_get_homedir(pool);
  
!           if (!homedir)
!             return SVN_NO_ERROR;
! 
!           socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
!                                              "S.gpg-agent", SVN_VA_NULL);
!         }
      }
  
    if (socket_name != NULL)

Reply via email to