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)