From ee87c653bf4b495714e8e6b024d0a8ace3a33452 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 25 Jan 2016 11:20:23 +0100 Subject: [PATCH] agent: Send PROGRESS status lines to the client. * agent/gpg-agent.c (struct progress_dispatch_s): New. (progress_dispatch_list): New. (main): Register libgcrypt pogress handler. (agent_libgcrypt_progress_cb): New. (agent_set_progress_cb): New. (unregister_progress_cb): New. (agent_deinit_default_ctrl): Call unregister. * agent/command.c (progress_cb): New. (start_command_handler): Register progress callback. -- Signed-off-by: Werner Koch --- agent/agent.h | 3 ++ agent/command.c | 18 ++++++++ agent/gpg-agent.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/agent/agent.h b/agent/agent.h index c7e14332c..c2726bbbc 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -336,6 +336,9 @@ typedef int (*lookup_ttl_t)(const char *hexgrip); /*-- gpg-agent.c --*/ void agent_exit (int rc) GPGRT_ATTR_NORETURN; /* Also implemented in other tools */ +void agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what, + int printchar, int current, int total), + ctrl_t ctrl); gpg_error_t agent_copy_startup_env (ctrl_t ctrl); const char *get_agent_socket_name (void); const char *get_agent_ssh_socket_name (void); diff --git a/agent/command.c b/agent/command.c index a09da607d..421df0044 100644 --- a/agent/command.c +++ b/agent/command.c @@ -446,6 +446,23 @@ agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid) } +/* An agent progress callback for Libgcrypt. This has been registered + * to be called via the progress dispatcher mechanism from + * gpg-agent.c */ +static void +progress_cb (ctrl_t ctrl, const char *what, int printchar, + int current, int total) +{ + if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) + ; + else if (printchar == '\n' && what && !strcmp (what, "primegen")) + agent_print_status (ctrl, "PROGRESS", "%.20s X 100 100", what); + else + agent_print_status (ctrl, "PROGRESS", "%.20s %c %d %d", + what, printchar=='\n'?'X':printchar, current, total); +} + + /* Helper to print a message while leaving a command. */ static gpg_error_t leave_cmd (assuan_context_t ctx, gpg_error_t err) @@ -3205,6 +3222,7 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd) ctrl->digest.raw_value = 0; assuan_set_io_monitor (ctx, io_monitor, NULL); + agent_set_progress_cb (progress_cb, ctrl); for (;;) { diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 3095531a5..8aab2b951 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -372,6 +372,32 @@ static pid_t parent_pid = (pid_t)(-1); /* Number of active connections. */ static int active_connections; +/* This object is used to dispatch progress messages from Libgcrypt to + * the right thread. Given that we won't have at max a few dozen + * connections at the same time using a linked list is the easiest way + * to handle this. */ +struct progress_dispatch_s +{ + struct progress_dispatch_s *next; + /* The control object of the connection. If this is NULL no + * connection is associated with this item and it is free for reuse + * by new connections. */ + ctrl_t ctrl; + + /* The thread id of (npth_self) of the connection. */ + npth_t tid; + + /* The callback set by the connection. This is similar to the + * Libgcrypt callback but with the control object passed as the + * first argument. */ + void (*cb)(ctrl_t ctrl, + const char *what, int printchar, + int current, int total); +}; +struct progress_dispatch_s *progress_dispatch_list; + + + /* Local prototypes. @@ -383,6 +409,9 @@ static gnupg_fd_t create_server_socket (char *name, int primary, int cygwin, assuan_sock_nonce_t *nonce); static void create_directories (void); +static void agent_libgcrypt_progress_cb (void *data, const char *what, + int printchar, + int current, int total); static void agent_init_default_ctrl (ctrl_t ctrl); static void agent_deinit_default_ctrl (ctrl_t ctrl); @@ -760,6 +789,7 @@ main (int argc, char **argv ) setup_libgcrypt_logging (); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); + gcry_set_progress_handler (agent_libgcrypt_progress_cb, NULL); disable_core_dumps (); @@ -1445,6 +1475,88 @@ agent_exit (int rc) } +/* This is our callback function for gcrypt progress messages. It is + set once at startup and dispatches progress messages to the + corresponding threads of the agent. */ +static void +agent_libgcrypt_progress_cb (void *data, const char *what, int printchar, + int current, int total) +{ + struct progress_dispatch_s *dispatch; + npth_t mytid = npth_self (); + + (void)data; + + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (dispatch && dispatch->cb) + dispatch->cb (dispatch->ctrl, what, printchar, current, total); +} + + +/* If a progress dispatcher callback has been associated with the + * current connection unregister it. */ +static void +unregister_progress_cb (void) +{ + struct progress_dispatch_s *dispatch; + npth_t mytid = npth_self (); + + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (dispatch) + { + dispatch->ctrl = NULL; + dispatch->cb = NULL; + } +} + + +/* Setup a progress callback CB for the current connection. Using a + * CB of NULL disables the callback. */ +void +agent_set_progress_cb (void (*cb)(ctrl_t ctrl, const char *what, + int printchar, int current, int total), + ctrl_t ctrl) +{ + struct progress_dispatch_s *dispatch, *firstfree; + npth_t mytid = npth_self (); + + firstfree = NULL; + for (dispatch = progress_dispatch_list; dispatch; dispatch = dispatch->next) + { + if (dispatch->ctrl && dispatch->tid == mytid) + break; + if (!dispatch->ctrl && !firstfree) + firstfree = dispatch; + } + if (!dispatch) /* None allocated: Reuse or allocate a new one. */ + { + if (firstfree) + { + dispatch = firstfree; + } + else if ((dispatch = xtrycalloc (1, sizeof *dispatch))) + { + dispatch->next = progress_dispatch_list; + progress_dispatch_list = dispatch; + } + else + { + log_error ("error allocating new progress dispatcher slot: %s\n", + gpg_strerror (gpg_error_from_syserror ())); + return; + } + dispatch->ctrl = ctrl; + dispatch->tid = mytid; + } + + dispatch->cb = cb; +} + + /* Each thread has its own local variables conveyed by a control structure usually identified by an argument named CTRL. This function is called immediately after allocating the control @@ -1481,6 +1593,7 @@ agent_init_default_ctrl (ctrl_t ctrl) static void agent_deinit_default_ctrl (ctrl_t ctrl) { + unregister_progress_cb (); session_env_release (ctrl->session_env); if (ctrl->lc_ctype)