Updated patch. It parses "gpgconf --list-dir" output to find gpg-agent socket.
Now it also unescapes the path according to gpgconf man page which says the path is percent-escaped. I'm not sure about using svn_cstring_split on svn_stringbuf_t data member though, but I could not find a similar functionality for string buffer. [[[ 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,10 +64,13 @@ #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" @@ -225,6 +228,65 @@ 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_proc_t proc; + svn_stringbuf_t *line; + svn_error_t *err; + svn_boolean_t eof; + const char* agent_socket; + const char* const gpgargv[] = { "gpgconf", "--list-dir", NULL }; + + /* execute "gpgconf --list-dir" */ + err = svn_io_start_cmd3(&proc, NULL, "gpgconf", gpgargv, + NULL /* env */, TRUE /* inherit */, + FALSE /* infile_pipe */, NULL /* infile */, + TRUE /* outfile_pipe */, NULL /* outfile */, + FALSE /* errfile_pipe */, NULL /* errfile */, + pool); + if (err != SVN_NO_ERROR) + { + svn_error_clear(err); + return NULL; + } + + /* Parse the gpgconf output. + * The format of output is a list of directories and sockets witch each + * directory/socket listen on a separate line in format "field:/some/path" */ + eof = FALSE; + while (((err = svn_io_file_readline(proc.out, &line, NULL, &eof, APR_SIZE_MAX, + pool, pool)) == SVN_NO_ERROR) + && !eof) + { + if (strncmp(line->data, "agent-socket:", strlen("agent-socket:")) == 0) + { + apr_array_header_t *dir_details; + dir_details = svn_cstring_split(line->data, ":", TRUE, pool); + /* note: unescape_assuan modifies dir_details in place */ + agent_socket = unescape_assuan(APR_ARRAY_IDX(dir_details, 1, char*)); + break; + } + } + if (err != SVN_NO_ERROR) + { + svn_error_clear(err); + return NULL; + } + apr_file_close(proc.out); + err = svn_io_wait_for_cmd(&proc, "gpgconf", NULL, NULL, pool); + if (err != SVN_NO_ERROR) + { + svn_error_clear(err); + return NULL; + } + + return agent_socket; +} + /* 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. */ @@ -242,37 +304,43 @@ *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; + /* Query socket location using gpgconf if possible */ + socket_name = find_gpgconf_agent_socket(pool); - /* 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) + /* fallback to the old method used with Gnupg 1.x */ + if (socket_name == 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); + /* 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; - if (!homedir) - return SVN_NO_ERROR; + /* 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); + socket_name = svn_dirent_join_many(pool, homedir, ".gnupg", + "S.gpg-agent", SVN_VA_NULL); + } } if (socket_name != NULL)