chiark / gitweb /
asshelp.c: add a lot of debug logging
[gnupg2.git] / agent / gpg-agent.c
index f5ecea544cb92f8293bdc89830fe788691d9191a..0e53549479f56044d4b26a9de2482193a41ceddd 100644 (file)
@@ -382,6 +382,9 @@ static char *current_logfile;
    watched. */
 static pid_t parent_pid = (pid_t)(-1);
 
+/* Record the pid of the main thread, for easier signalling */
+static pid_t main_thread_pid = (pid_t)(-1);
+
 /* Number of active connections.  */
 static int active_connections;
 
@@ -2020,7 +2023,7 @@ get_agent_scd_notify_event (void)
                                  GetCurrentProcess(), &h2,
                                  EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
         {
-          log_error ("setting syncronize for scd notify event failed: %s\n",
+          log_error ("setting synchronize for scd notify event failed: %s\n",
                      w32_strerror (-1) );
           CloseHandle (h);
         }
@@ -2264,17 +2267,32 @@ create_directories (void)
 }
 
 
+static int
+need_tick (void)
+{
+#ifdef HAVE_W32_SYSTEM
+  /* We do not know how to interrupt the select loop on Windows, so we
+     always need a short tick there. */
+  return 1;
+#else
+  /* if we were invoked like "gpg-agent cmd arg1 arg2" then we need to
+     watch our parent. */
+  if (parent_pid != (pid_t)(-1))
+    return 1;
+  /* if scdaemon is running, we need to check that it's alive */
+  if (agent_scd_check_running ())
+    return 1;
+  /* otherwise, nothing fine-grained to do. */
+  return 0;
+#endif /*HAVE_W32_SYSTEM*/
+}
+
 
 /* This is the worker for the ticker.  It is called every few seconds
    and may only do fast operations. */
 static void
 handle_tick (void)
 {
-  static time_t last_minute;
-
-  if (!last_minute)
-    last_minute = time (NULL);
-
   /* Check whether the scdaemon has died and cleanup in this case. */
   agent_scd_check_aliveness ();
 
@@ -2293,16 +2311,6 @@ handle_tick (void)
         }
     }
 #endif /*HAVE_W32_SYSTEM*/
-
-  /* Code to be run from time to time.  */
-#if CHECK_OWN_SOCKET_INTERVAL > 0
-  if (last_minute + CHECK_OWN_SOCKET_INTERVAL <= time (NULL))
-    {
-      check_own_socket ();
-      last_minute = time (NULL);
-    }
-#endif
-
 }
 
 
@@ -2337,7 +2345,7 @@ agent_sigusr2_action (void)
 
 #ifndef HAVE_W32_SYSTEM
 /* The signal handler for this program.  It is expected to be run in
-   its own trhead and not in the context of a signal handler.  */
+   its own thread and not in the context of a signal handler.  */
 static void
 handle_signal (int signo)
 {
@@ -2361,6 +2369,10 @@ handle_signal (int signo)
       agent_sigusr2_action ();
       break;
 
+      /* nothing to do here, just take an extra cycle on the select loop */
+    case SIGCONT:
+      break;
+
     case SIGTERM:
       if (!shutdown_pending)
         log_info ("SIGTERM received - shutting down ...\n");
@@ -2699,6 +2711,22 @@ start_connection_thread_ssh (void *arg)
 }
 
 
+void interrupt_main_thread_loop (void)
+{
+#ifndef HAVE_W32_SYSTEM
+  kill (main_thread_pid, SIGCONT);
+#endif
+}
+
+/* helper function for readability: test whether a given struct
+   timespec is set to all-zeros */
+static inline int
+tv_is_set (struct timespec tv)
+{
+  return tv.tv_sec || tv.tv_nsec;
+}
+
+
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  */
 static void
@@ -2716,9 +2744,11 @@ handle_connections (gnupg_fd_t listen_fd,
   gnupg_fd_t fd;
   int nfd;
   int saved_errno;
+  int idx;
   struct timespec abstime;
   struct timespec curtime;
   struct timespec timeout;
+  struct timespec *select_timeout;
 #ifdef HAVE_W32_SYSTEM
   HANDLE events[2];
   unsigned int events_set;
@@ -2734,6 +2764,14 @@ handle_connections (gnupg_fd_t listen_fd,
     { "browser", start_connection_thread_browser },
     { "ssh",    start_connection_thread_ssh   }
   };
+  struct {
+    struct timespec interval;
+    void (*func) (void);
+    struct timespec next;
+  } timertbl[] = {
+    { { TIMERTICK_INTERVAL, 0 }, handle_tick },
+    { { CHECK_OWN_SOCKET_INTERVAL, 0 }, check_own_socket }
+  };
 
 
   ret = npth_attr_init(&tattr);
@@ -2748,8 +2786,10 @@ handle_connections (gnupg_fd_t listen_fd,
   npth_sigev_add (SIGUSR1);
   npth_sigev_add (SIGUSR2);
   npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGCONT);
   npth_sigev_add (SIGTERM);
   npth_sigev_fini ();
+  main_thread_pid = getpid ();
 #else
 # ifdef HAVE_W32CE_SYSTEM
   /* Use a dummy event. */
@@ -2761,6 +2801,7 @@ handle_connections (gnupg_fd_t listen_fd,
 # endif
 #endif
 
+
   if (disable_check_own_socket)
     my_inotify_fd = -1;
   else if ((err = gnupg_inotify_watch_socket (&my_inotify_fd, socket_name)))
@@ -2823,9 +2864,6 @@ handle_connections (gnupg_fd_t listen_fd,
   listentbl[2].l_fd = listen_fd_browser;
   listentbl[3].l_fd = listen_fd_ssh;
 
-  npth_clock_gettime (&abstime);
-  abstime.tv_sec += TIMERTICK_INTERVAL;
-
   for (;;)
     {
       /* Shutdown test.  */
@@ -2854,18 +2892,52 @@ handle_connections (gnupg_fd_t listen_fd,
          thus a simple assignment is fine to copy the entire set.  */
       read_fdset = fdset;
 
+      /* avoid a fine-grained timer if we don't need one: */
+      timertbl[0].interval.tv_sec = need_tick () ? TIMERTICK_INTERVAL : 0;
+      /* avoid waking up to check sockets if we can count on inotify */
+      timertbl[1].interval.tv_sec = (my_inotify_fd == -1) ? CHECK_OWN_SOCKET_INTERVAL : 0;
+
+      /* loop through all timers, fire any registered functions, and
+         plan next timer to trigger */
       npth_clock_gettime (&curtime);
-      if (!(npth_timercmp (&curtime, &abstime, <)))
-       {
-         /* Timeout.  */
-         handle_tick ();
-         npth_clock_gettime (&abstime);
-         abstime.tv_sec += TIMERTICK_INTERVAL;
-       }
-      npth_timersub (&abstime, &curtime, &timeout);
+      abstime.tv_sec = abstime.tv_nsec = 0;
+      for (idx=0; idx < DIM(timertbl); idx++)
+        {
+          /* schedule any unscheduled timers */
+          if ((!tv_is_set (timertbl[idx].next)) && tv_is_set (timertbl[idx].interval))
+            npth_timeradd (&timertbl[idx].interval, &curtime, &timertbl[idx].next);
+          /* if a timer is due, fire it ... */
+          if (tv_is_set (timertbl[idx].next))
+            {
+              if (!(npth_timercmp (&curtime, &timertbl[idx].next, <)))
+                {
+                  timertbl[idx].func ();
+                  npth_clock_gettime (&curtime);
+                  /* ...and reschedule it, if desired: */
+                  if (tv_is_set (timertbl[idx].interval))
+                    npth_timeradd (&timertbl[idx].interval, &curtime, &timertbl[idx].next);
+                  else
+                    timertbl[idx].next.tv_sec = timertbl[idx].next.tv_nsec = 0;
+                }
+            }
+          /* accumulate next timer to come due in abstime: */
+          if (tv_is_set (timertbl[idx].next) &&
+              ((!tv_is_set (abstime)) ||
+               (npth_timercmp (&abstime, &timertbl[idx].next, >))))
+            abstime = timertbl[idx].next;
+        }
+      /* choose a timeout for the select loop: */
+      if (tv_is_set (abstime))
+        {
+          npth_timersub (&abstime, &curtime, &timeout);
+          select_timeout = &timeout;
+        }
+      else
+          select_timeout = NULL;
+      
 
 #ifndef HAVE_W32_SYSTEM
-      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, select_timeout,
                           npth_sigev_sigmask ());
       saved_errno = errno;
 
@@ -2875,7 +2947,7 @@ handle_connections (gnupg_fd_t listen_fd,
           handle_signal (signo);
       }
 #else
-      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout,
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, select_timeout,
                           events, &events_set);
       saved_errno = errno;
 
@@ -2898,7 +2970,6 @@ handle_connections (gnupg_fd_t listen_fd,
 
       if (!shutdown_pending)
         {
-          int idx;
           ctrl_t ctrl;
           npth_t thread;