agent: Send PROGRESS status lines to the client.
authorWerner Koch <wk@gnupg.org>
Mon, 25 Jan 2016 10:20:23 +0000 (11:20 +0100)
committerWerner Koch <wk@gnupg.org>
Mon, 25 Jan 2016 10:20:23 +0000 (11:20 +0100)
* 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 <wk@gnupg.org>
agent/agent.h
agent/command.c
agent/gpg-agent.c

index c7e1433..c2726bb 100644 (file)
@@ -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);
index a09da60..421df00 100644 (file)
@@ -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 (;;)
     {
index 3095531..8aab2b9 100644 (file)
@@ -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;
+
+
+
 \f
 /*
    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)