gpgscm: Merge 'opexe_2'.
[gnupg.git] / scd / scdaemon.c
index 14e0b05..26e89dd 100644 (file)
 
 #include <assuan.h> /* malloc hooks */
 
-#ifdef HAVE_LIBUSB
-#include <libusb.h>
-#endif
-
-#include "i18n.h"
-#include "sysutils.h"
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
 #include "app-common.h"
 #include "iso7816.h"
 #include "apdu.h"
 #include "ccid-driver.h"
-#include "gc-opt-flags.h"
-#include "asshelp.h"
-#include "exechelp.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/asshelp.h"
+#include "../common/exechelp.h"
 #include "../common/init.h"
 
 #ifndef ENAMETOOLONG
 
 enum cmd_and_opt_values
 { aNull = 0,
-  oCsh           = 'c',
-  oQuiet         = 'q',
-  oSh            = 's',
-  oVerbose       = 'v',
+  oCsh            = 'c',
+  oQuiet          = 'q',
+  oSh             = 's',
+  oVerbose        = 'v',
 
   oNoVerbose = 500,
   aGPGConfList,
@@ -103,7 +99,6 @@ enum cmd_and_opt_values
   oDenyAdmin,
   oDisableApplication,
   oEnablePinpadVarlen,
-  oDebugDisableTicker
 };
 
 
@@ -119,18 +114,17 @@ static ARGPARSE_OPTS opts[] = {
                 N_("run in multi server mode (foreground)")),
   ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
-  ARGPARSE_s_n (oQuiet,        "quiet", N_("be somewhat more quiet")),
-  ARGPARSE_s_n (oSh,   "sh", N_("sh-style command output")),
-  ARGPARSE_s_n (oCsh,  "csh", N_("csh-style command output")),
+  ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+  ARGPARSE_s_n (oSh,    "sh", N_("sh-style command output")),
+  ARGPARSE_s_n (oCsh,   "csh", N_("csh-style command output")),
   ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
-  ARGPARSE_s_s (oDebug,        "debug", "@"),
+  ARGPARSE_s_s (oDebug, "debug", "@"),
   ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
   ARGPARSE_s_s (oDebugLevel, "debug-level" ,
                 N_("|LEVEL|set the debugging level to LEVEL")),
   ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
   ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
   ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"),
-  ARGPARSE_s_n (oDebugDisableTicker, "debug-disable-ticker", "@"),
   ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
   ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
   ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
@@ -194,8 +188,13 @@ static struct debug_flags_s debug_flags [] =
 #define DEFAULT_PCSC_DRIVER "libpcsclite.so"
 #endif
 
-/* The timer tick used for housekeeping stuff.  We poll every 500ms to
-   let the user immediately know a status change.
+/* The timer tick used to check card removal.
+
+   We poll every 500ms to let the user immediately know a status
+   change.
+
+   For a card reader with an interrupt endpoint, this timer is not
+   used with the internal CCID driver.
 
    This is not too good for power saving but given that there is no
    easy way to block on card status changes it is the best we can do.
@@ -225,24 +224,12 @@ static char *redir_socket_name;
    POSIX systems). */
 static assuan_sock_nonce_t socket_nonce;
 
-/* Debug flag to disable the ticker.  The ticker is in fact not
-   disabled but it won't perform any ticker specific actions. */
-static int ticker_disabled;
-
-/* Set of FD to select.  */
-static fd_set fdset;
-
-/* Max FD to select.  */
-static int nfd;
-
-/* Set if all usb devices have INTERRUPT endpoints.  */
-static int usb_all_have_intr_endp;
-
-/* FD to listen incomming connections.  */
-static int listen_fd;
-
-/* FD to notify update of usb devices.  */
-static int notify_fd;
+#ifdef HAVE_W32_SYSTEM
+static HANDLE the_event;
+#else
+/* PID to notify update of usb devices.  */
+static pid_t main_thread_pid;
+#endif
 \f
 static char *create_socket_name (char *standard_name);
 static gnupg_fd_t create_server_socket (const char *name,
@@ -250,7 +237,7 @@ static gnupg_fd_t create_server_socket (const char *name,
                                         assuan_sock_nonce_t *nonce);
 
 static void *start_connection_thread (void *arg);
-static void handle_connections (void);
+static void handle_connections (int listen_fd);
 
 /* Pth wrapper function definitions. */
 ASSUAN_SYSTEM_NPTH_IMPL;
@@ -474,13 +461,13 @@ main (int argc, char **argv )
         parse_debug++;
       else if (pargs.r_opt == oOptions)
         { /* yes there is one, so we do not try the default one, but
-            read the option file when it is encountered at the
-            commandline */
+             read the option file when it is encountered at the
+             commandline */
           default_config = 0;
-       }
-       else if (pargs.r_opt == oNoOptions)
+        }
+        else if (pargs.r_opt == oNoOptions)
           default_config = 0; /* --no-options */
-       else if (pargs.r_opt == oHomedir)
+        else if (pargs.r_opt == oHomedir)
           gnupg_set_homedir (pargs.r.ret_str);
     }
 
@@ -515,16 +502,16 @@ main (int argc, char **argv )
               if( parse_debug )
                 log_info (_("Note: no default option file '%s'\n"),
                           configname );
-           }
+            }
           else
             {
               log_error (_("option file '%s': %s\n"),
                          configname, strerror(errno) );
               exit(2);
-           }
+            }
           xfree (configname);
           configname = NULL;
-       }
+        }
       if (parse_debug && configname )
         log_info (_("reading options from '%s'\n"), configname );
       default_config = 0;
@@ -559,7 +546,6 @@ main (int argc, char **argv )
           ccid_set_debug_level (ccid_set_debug_level (-1)+1);
 #endif /*HAVE_LIBUSB*/
           break;
-        case oDebugDisableTicker: ticker_disabled = 1; break;
         case oDebugLogTid:
           log_set_pid_suffix_cb (tid_log_callback);
           break;
@@ -571,10 +557,10 @@ main (int argc, char **argv )
           /* config files may not be nested (silently ignore them) */
           if (!configfp)
             {
-               xfree(configname);
-               configname = xstrdup(pargs.r.ret_str);
-               goto next_pass;
-           }
+                xfree(configname);
+                configname = xstrdup(pargs.r.ret_str);
+                goto next_pass;
+            }
           break;
         case oNoGreeting: nogreeting = 1; break;
         case oNoVerbose: opt.verbose = 0; break;
@@ -606,12 +592,12 @@ main (int argc, char **argv )
           add_to_strlist (&opt.disabled_applications, pargs.r.ret_str);
           break;
 
-       case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
+        case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
 
         default:
           pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
           break;
-       }
+        }
     }
   if (configfp)
     {
@@ -674,7 +660,7 @@ main (int argc, char **argv )
       char *filename_esc;
 
       if (config_filename)
-       filename = xstrdup (config_filename);
+        filename = xstrdup (config_filename);
       else
         filename = make_filename (gnupg_homedir (),
                                   SCDAEMON_NAME EXTSEP_S "conf", NULL);
@@ -798,8 +784,7 @@ main (int argc, char **argv )
 
       /* We run handle_connection to wait for the shutdown signal and
          to run the ticker stuff.  */
-      listen_fd = fd;
-      handle_connections ();
+      handle_connections (fd);
       if (fd != -1)
         close (fd);
     }
@@ -931,8 +916,7 @@ main (int argc, char **argv )
 
 #endif /*!HAVE_W32_SYSTEM*/
 
-      listen_fd = fd;
-      handle_connections ();
+      handle_connections (fd);
 
       close (fd);
     }
@@ -1016,6 +1000,10 @@ handle_signal (int signo)
       log_info ("SIGUSR2 received - no action defined\n");
       break;
 
+    case SIGCONT:
+      /* Nothing.  */
+      break;
+
     case SIGTERM:
       if (!shutdown_pending)
         log_info ("SIGTERM received - shutting down ...\n");
@@ -1029,7 +1017,7 @@ handle_signal (int signo)
           log_info ("%s %s stopped\n", strusage(11), strusage(13) );
           cleanup ();
           scd_exit (0);
-       }
+        }
       break;
 
     case SIGINT:
@@ -1046,14 +1034,6 @@ handle_signal (int signo)
 #endif /*!HAVE_W32_SYSTEM*/
 
 
-static void
-handle_tick (void)
-{
-  if (!ticker_disabled)
-    scd_update_reader_status_file ();
-}
-
-
 /* Create a name for the socket.  We check for valid characters as
    well as against a maximum allowed length for a unix domain socket
    is done.  The function terminates the process in case of an error.
@@ -1136,7 +1116,7 @@ create_server_socket (const char *name, char **r_redir_name,
  if (rc == -1)
     {
       log_error (_("error binding socket to '%s': %s\n"),
-                unaddr->sun_path,
+                 unaddr->sun_path,
                  gpg_strerror (gpg_error_from_syserror ()));
       assuan_sock_close (fd);
       scd_exit (2);
@@ -1178,6 +1158,8 @@ start_connection_thread (void *arg)
       return NULL;
     }
 
+  active_connections++;
+
   scd_init_default_ctrl (ctrl);
   if (opt.verbose)
     log_info (_("handler for fd %d started\n"),
@@ -1197,64 +1179,31 @@ start_connection_thread (void *arg)
 
   scd_deinit_default_ctrl (ctrl);
   xfree (ctrl);
+
+  if (--active_connections == 0)
+    scd_kick_the_loop ();
+
   return NULL;
 }
 
 
 void
-update_fdset_for_usb (int scanned, int all_have_intr_endp)
+scd_kick_the_loop (void)
 {
-#ifdef HAVE_LIBUSB
-  const struct libusb_pollfd **pfd_array = libusb_get_pollfds (NULL);
-  const struct libusb_pollfd **p;
-#endif
-
-  if (scanned)
-    usb_all_have_intr_endp = all_have_intr_endp;
-
-  FD_ZERO (&fdset);
-  nfd = 0;
-
-  if (listen_fd != -1)
-    {
-      FD_SET (listen_fd, &fdset);
-      nfd = listen_fd;
-    }
-
-#ifdef HAVE_LIBUSB
-  for (p = pfd_array; *p; p++)
-    {
-      int fd = (*p)->fd;
-
-      FD_SET (fd, &fdset);
-      if (nfd < fd)
-        nfd = fd;
-      p++;
-    }
-
-  libusb_free_pollfds (pfd_array);
-#endif
+  int ret;
 
   /* Kick the select loop.  */
-  write (notify_fd, "", 1);
-
-  log_debug ("update_fdset_for_usb (%d, %d): %d %lx\n",
-             scanned, all_have_intr_endp, nfd, fdset.fds_bits[0]);
-}
-
-static int
-need_tick (void)
-{
-  if (shutdown_pending)
-    return 1;
-
-  if (listen_fd != -1 && nfd == listen_fd)
-    return 1;
-
-  if (usb_all_have_intr_endp)
-    return 0;
-
-  return 1;
+#ifdef HAVE_W32_SYSTEM
+  ret = SetEvent (the_event);
+  if (ret == 0)
+    log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
+               w32_strerror (-1));
+#else
+  ret = kill (main_thread_pid, SIGCONT);
+  if (ret < 0)
+    log_error ("SetEvent for scd_kick_the_loop failed: %s\n",
+               gpg_strerror (gpg_error_from_syserror ()));
+#endif
 }
 
 /* Connection handler loop.  Wait for connection requests and spawn a
@@ -1262,31 +1211,24 @@ need_tick (void)
    in which case this code will only do regular timeouts and handle
    signals. */
 static void
-handle_connections (void)
+handle_connections (int listen_fd)
 {
   npth_attr_t tattr;
   struct sockaddr_un paddr;
   socklen_t plen;
-  fd_set read_fdset;
+  fd_set fdset, read_fdset;
+  int nfd;
   int ret;
   int fd;
-  struct timespec abstime;
-  struct timespec curtime;
   struct timespec timeout;
   struct timespec *t;
   int saved_errno;
-#ifndef HAVE_W32_SYSTEM
+#ifdef HAVE_W32_SYSTEM
+  HANDLE events[2];
+  unsigned int events_set;
+#else
   int signo;
 #endif
-  int pipe_fd[2];
-
-  ret = gnupg_create_pipe (pipe_fd);
-  if (ret)
-    {
-      log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
-      return;
-    }
-  notify_fd = pipe_fd[1];
 
   ret = npth_attr_init(&tattr);
   if (ret)
@@ -1297,14 +1239,40 @@ handle_connections (void)
 
   npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
 
-#ifndef HAVE_W32_SYSTEM
+#ifdef HAVE_W32_SYSTEM
+  {
+    HANDLE h, h2;
+    SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+
+    events[0] = the_event = INVALID_HANDLE_VALUE;
+    events[1] = INVALID_HANDLE_VALUE;
+    h = CreateEvent (&sa, TRUE, FALSE, NULL);
+    if (!h)
+      log_error ("can't create scd event: %s\n", w32_strerror (-1) );
+    else if (!DuplicateHandle (GetCurrentProcess(), h,
+                               GetCurrentProcess(), &h2,
+                               EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
+      {
+        log_error ("setting synchronize for scd_kick_the_loop failed: %s\n",
+                   w32_strerror (-1) );
+        CloseHandle (h);
+      }
+    else
+      {
+        CloseHandle (h);
+        events[0] = the_event = h2;
+      }
+  }
+#else
   npth_sigev_init ();
   npth_sigev_add (SIGHUP);
   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 ();
 #endif
 
   FD_ZERO (&fdset);
@@ -1315,15 +1283,9 @@ handle_connections (void)
       nfd = listen_fd;
     }
 
-  npth_clock_gettime (&curtime);
-  timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
-  timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
-  npth_timeradd (&curtime, &timeout, &abstime);
-  /* We only require abstime here.  The others will be reused.  */
-
   for (;;)
     {
-      int max_fd;
+      int periodical_check;
 
       if (shutdown_pending)
         {
@@ -1338,44 +1300,33 @@ handle_connections (void)
           listen_fd = -1;
         }
 
-      if (need_tick ())
-        {
-          npth_clock_gettime (&curtime);
-          if (!(npth_timercmp (&curtime, &abstime, <)))
-            {
-              /* Timeout.  */
-              timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
-              timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
-              npth_timeradd (&curtime, &timeout, &abstime);
-            }
-          npth_timersub (&abstime, &curtime, &timeout);
-          t = &timeout;
-        }
+      periodical_check = scd_update_reader_status_file ();
+
+      timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+      timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+
+      if (shutdown_pending || periodical_check)
+        t = &timeout;
       else
         t = NULL;
 
-      handle_tick ();
-
       /* POSIX says that fd_set should be implemented as a structure,
          thus a simple assignment is fine to copy the entire set.  */
       read_fdset = fdset;
 
-      FD_SET (pipe_fd[0], &read_fdset);
-      if (nfd < pipe_fd[0])
-        max_fd = pipe_fd[0];
-      else
-        max_fd = nfd;
-
 #ifndef HAVE_W32_SYSTEM
-      ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
+      ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t,
                           npth_sigev_sigmask ());
       saved_errno = errno;
 
       while (npth_sigev_get_pending(&signo))
         handle_signal (signo);
 #else
-      ret = npth_eselect (max_fd+1, &read_fdset, NULL, NULL, t, NULL, NULL);
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t,
+                          events, &events_set);
       saved_errno = errno;
+      if (events_set & 1)
+        continue;
 #endif
 
       if (ret == -1 && saved_errno != EINTR)
@@ -1390,13 +1341,6 @@ handle_connections (void)
         /* Timeout.  Will be handled when calculating the next timeout.  */
         continue;
 
-      if (FD_ISSET (pipe_fd[0], &read_fdset))
-        {
-          char buf[256];
-
-          read (pipe_fd[0], buf, sizeof buf);
-        }
-
       if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
         {
           ctrl_t ctrl;
@@ -1431,22 +1375,21 @@ handle_connections (void)
               else
                 npth_setname_np (thread, threadname);
             }
-
-          ret--;
         }
-
-#ifdef HAVE_LIBUSB
-      if (ret)
-        {
-          struct timeval tv = {0, 0};
-          libusb_handle_events_timeout_completed (NULL, &tv, NULL);
-        }
-#endif
     }
 
-  close (pipe_fd[0]);
-  close (pipe_fd[1]);
+#ifdef HAVE_W32_SYSTEM
+  if (the_event != INVALID_HANDLE_VALUE)
+    CloseHandle (the_event);
+#endif
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);
 }
+
+/* Return the number of active connections. */
+int
+get_active_connection_count (void)
+{
+  return active_connections;
+}