scd: Fix release of transfer object.
[gnupg.git] / scd / scdaemon.c
index 9c11cad..4ab0fcf 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
 #include <assuan.h> /* malloc hooks */
 
+#ifdef HAVE_LIBUSB
+#include <libusb.h>
+#endif
+
 #include "i18n.h"
 #include "sysutils.h"
 #include "app-common.h"
@@ -52,6 +56,7 @@
 #include "ccid-driver.h"
 #include "gc-opt-flags.h"
 #include "asshelp.h"
+#include "exechelp.h"
 #include "../common/init.h"
 
 #ifndef ENAMETOOLONG
@@ -156,6 +161,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_s_s (oDisableApplication, "disable-application", "@"),
   ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen",
                 N_("use variable length input for pinpad")),
+  ARGPARSE_s_s (oHomedir,    "homedir",      "@"),
 
   ARGPARSE_end ()
 };
@@ -164,7 +170,6 @@ static ARGPARSE_OPTS opts[] = {
 /* The list of supported debug flags.  */
 static struct debug_flags_s debug_flags [] =
   {
-    { DBG_COMMAND_VALUE, "command"  },
     { DBG_MPI_VALUE    , "mpi"     },
     { DBG_CRYPTO_VALUE , "crypto"  },
     { DBG_MEMORY_VALUE , "memory"  },
@@ -224,7 +229,20 @@ static assuan_sock_nonce_t socket_nonce;
    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;
 \f
 static char *create_socket_name (char *standard_name);
 static gnupg_fd_t create_server_socket (const char *name,
@@ -232,7 +250,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 (int listen_fd);
+static void handle_connections (void);
 
 /* Pth wrapper function definitions. */
 ASSUAN_SYSTEM_NPTH_IMPL;
@@ -327,10 +345,9 @@ set_debug (const char *level)
   else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
     opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
-    opt.debug = DBG_IPC_VALUE|DBG_COMMAND_VALUE;
+    opt.debug = DBG_IPC_VALUE;
   else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
-    opt.debug = (DBG_IPC_VALUE|DBG_COMMAND_VALUE
-                 |DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
+    opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
   else if (!strcmp (level, "guru") || numok)
     {
       opt.debug = ~0;
@@ -415,22 +432,12 @@ main (int argc, char **argv )
   /* Please note that we may running SUID(ROOT), so be very CAREFUL
      when adding any stuff between here and the call to INIT_SECMEM()
      somewhere after the option parsing */
-  log_set_prefix ("scdaemon", 1|4);
+  log_set_prefix ("scdaemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
 
   /* Make sure that our subsystems are ready.  */
   i18n_init ();
   init_common_subsystems (&argc, &argv);
 
-  npth_init ();
-
-  /* Check that the libraries are suitable.  Do it here because
-     the option parsing may need services of the library */
-  if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
-    {
-      log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
-                 NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
-    }
-
   ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
 
   malloc_hooks.malloc = gcry_malloc;
@@ -440,7 +447,7 @@ main (int argc, char **argv )
   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
   assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
   assuan_sock_init ();
-  setup_libassuan_logging (&opt.debug);
+  setup_libassuan_logging (&opt.debug, NULL);
 
   setup_libgcrypt_logging ();
   gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
@@ -651,7 +658,12 @@ main (int argc, char **argv )
 
   set_debug (debug_level);
 
-  initialize_module_command ();
+  if (initialize_module_command ())
+    {
+      log_error ("initialization failed\n");
+      cleanup ();
+      exit (1);
+    }
 
   if (gpgconf_list == 2)
     scd_exit (0);
@@ -702,7 +714,7 @@ main (int argc, char **argv )
   if (logfile)
     {
       log_set_file (logfile);
-      log_set_prefix (NULL, 1|2|4);
+      log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
     }
 
   if (debug_wait && pipe_server)
@@ -731,6 +743,9 @@ main (int argc, char **argv )
       }
 #endif
 
+      npth_init ();
+      gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
       /* If --debug-allow-core-dump has been given we also need to
          switch the working directory to a place where we can actually
          write. */
@@ -755,7 +770,7 @@ main (int argc, char **argv )
 
       res = npth_attr_init (&tattr);
       if (res)
-       {
+        {
           log_error ("error allocating thread attributes: %s\n",
                      strerror (res));
           scd_exit (2);
@@ -783,7 +798,8 @@ main (int argc, char **argv )
 
       /* We run handle_connection to wait for the shutdown signal and
          to run the ticker stuff.  */
-      handle_connections (fd);
+      listen_fd = fd;
+      handle_connections ();
       if (fd != -1)
         close (fd);
     }
@@ -868,15 +884,28 @@ main (int argc, char **argv )
 
       /* This is the child. */
 
+      npth_init ();
+      gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
       /* Detach from tty and put process into a new session. */
       if (!nodetach )
         {
           /* Close stdin, stdout and stderr unless it is the log stream. */
           for (i=0; i <= 2; i++)
             {
-              if ( log_test_fd (i) && i != fd)
-                close (i);
+              if (!log_test_fd (i) && i != fd )
+                {
+                  if ( !close (i)
+                       && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+                    {
+                      log_error ("failed to open '%s': %s\n",
+                                 "/dev/null", strerror (errno));
+                      cleanup ();
+                      exit (1);
+                    }
+                }
             }
+
           if (setsid() == -1)
             {
               log_error ("setsid() failed: %s\n", strerror(errno) );
@@ -902,7 +931,8 @@ main (int argc, char **argv )
 
 #endif /*!HAVE_W32_SYSTEM*/
 
-      handle_connections (fd);
+      listen_fd = fd;
+      handle_connections ();
 
       close (fd);
     }
@@ -1171,30 +1201,99 @@ start_connection_thread (void *arg)
 }
 
 
+void
+update_fdset_for_usb (int all_have_intr_endp)
+{
+#ifdef HAVE_LIBUSB
+  const struct libusb_pollfd **pfd_array = libusb_get_pollfds (NULL);
+  const struct libusb_pollfd **p;
+#endif
+
+  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++;
+      log_debug ("USB: add %d to fdset\n", fd);
+    }
+
+  libusb_free_pollfds (pfd_array);
+#endif
+
+  /* Kick the select loop.  */
+  write (notify_fd, "", 1);
+
+  log_debug ("update_fdset_for_usb (%d): %d\n", all_have_intr_endp, nfd);
+}
+
+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;
+}
+
 /* Connection handler loop.  Wait for connection requests and spawn a
    thread after accepting a connection.  LISTEN_FD is allowed to be -1
    in which case this code will only do regular timeouts and handle
    signals. */
 static void
-handle_connections (int listen_fd)
+handle_connections (void)
 {
   npth_attr_t tattr;
   struct sockaddr_un paddr;
   socklen_t plen;
-  fd_set fdset, read_fdset;
+  fd_set read_fdset;
   int ret;
   int fd;
-  int nfd;
   struct timespec abstime;
   struct timespec curtime;
   struct timespec timeout;
+  struct timespec *t;
   int saved_errno;
 #ifndef HAVE_W32_SYSTEM
   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);
-  /* FIXME: Check error.  */
+  if (ret)
+    {
+      log_error ("npth_attr_init failed: %s\n", strerror (ret));
+      return;
+    }
+
   npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
 
 #ifndef HAVE_W32_SYSTEM
@@ -1223,6 +1322,8 @@ handle_connections (int listen_fd)
 
   for (;;)
     {
+      int max_fd;
+
       if (shutdown_pending)
         {
           if (active_connections == 0)
@@ -1234,56 +1335,78 @@ handle_connections (int listen_fd)
              used to just wait on a signal or timeout event. */
           FD_ZERO (&fdset);
           listen_fd = -1;
-       }
+        }
 
-      npth_clock_gettime (&curtime);
-      if (!(npth_timercmp (&curtime, &abstime, <)))
-       {
-         /* Timeout.  */
-         handle_tick ();
-         timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
-         timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
-         npth_timeradd (&curtime, &timeout, &abstime);
-       }
-      npth_timersub (&abstime, &curtime, &timeout);
+      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;
+        }
+      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 (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
+      ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
+                          npth_sigev_sigmask ());
       saved_errno = errno;
 
       while (npth_sigev_get_pending(&signo))
-       handle_signal (signo);
+        handle_signal (signo);
 #else
-      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
+      ret = npth_eselect (max_fd+1, &read_fdset, NULL, NULL, t, NULL, NULL);
       saved_errno = errno;
 #endif
 
       if (ret == -1 && saved_errno != EINTR)
-       {
+        {
           log_error (_("npth_pselect failed: %s - waiting 1s\n"),
                      strerror (saved_errno));
           npth_sleep (1);
-         continue;
-       }
+          continue;
+        }
 
       if (ret <= 0)
-       /* Timeout.  Will be handled when calculating the next timeout.  */
-       continue;
+        /* 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);
+          ret--;
+        }
 
       if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
-       {
+        {
           ctrl_t ctrl;
 
           plen = sizeof paddr;
-         fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
-         if (fd == -1)
-           {
-             log_error ("accept failed: %s\n", strerror (errno));
-           }
+          fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
+          if (fd == -1)
+            {
+              log_error ("accept failed: %s\n", strerror (errno));
+            }
           else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
             {
               log_error ("error allocating connection control data: %s\n",
@@ -1293,13 +1416,12 @@ handle_connections (int listen_fd)
           else
             {
               char threadname[50];
-             npth_t thread;
+              npth_t thread;
 
-              snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd);
-              threadname[sizeof threadname -1] = 0;
+              snprintf (threadname, sizeof threadname, "conn fd=%d", fd);
               ctrl->thread_startup.fd = INT2FD (fd);
               ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
-             if (ret)
+              if (ret)
                 {
                   log_error ("error spawning connection handler: %s\n",
                              strerror (ret));
@@ -1307,12 +1429,25 @@ handle_connections (int listen_fd)
                   close (fd);
                 }
               else
-               npth_setname_np (thread, threadname);
+                npth_setname_np (thread, threadname);
             }
-          fd = -1;
-       }
+
+          ret--;
+        }
+
+#ifdef HAVE_LIBUSB
+      if (ret)
+        {
+          struct timeval tv = {0, 0};
+
+          log_debug ("scd main: USB handle events\n");
+          libusb_handle_events_timeout_completed (NULL, &tv, NULL);
+        }
+#endif
     }
 
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
   cleanup ();
   log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
   npth_attr_destroy (&tattr);