scd: APP centric approach for device management.
authorNIIBE Yutaka <gniibe@fsij.org>
Wed, 28 Dec 2016 03:29:17 +0000 (12:29 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Wed, 28 Dec 2016 03:29:17 +0000 (12:29 +0900)
* scd/app.c (lock_app): Rename from lock_reader and use internal field
of APP.
(unlock_app): Likewise.
(app_dump_state): Use APP.
(application_notify_card_reset): Remove.
(check_conflict): Change API for APP, instead of SLOT.
(check_application_conflict): Likewise.
(release_application_internal): New.
(app_reset): New.
(app_new_register): New.
(select_application): Change API for APP, instead of SLOT.
(deallocate_app, release_application): Modify for manage link.
(report_change): New.
(scd_update_reader_status_file): Moved from command.c and
use APP list, instead of VREADER.
(initialize_module_command): Moved from command.c.

* scd/command.c (TEST_CARD_REMOVAL): Remove.
(IS_LOCKED): Simplify.
(vreader_table): Remove.
(vreader_slot, update_card_removed): Remove.
(do_reset): Call app_reset.
(get_current_reader): Remove.
(open_card): Add SCAN arg.
(cmd_serialno): No retry, since retry is done in lower layer in apdu.c.
No do_reset, since it is done in lower layer.
Add clearing card_removed flag.
(cmd_disconnect): Call apdu_disconnect.
(send_client_notifications): Modify for APP.
(update_reader_status_file): Remove.

--

APP is the abstraction of the card application.  For management of
cards, it is better to focus on the APP instead of the physical reader.
This change makes support of multiple card/token easier.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
scd/app-common.h
scd/app.c
scd/command.c
scd/scdaemon.h

index 21f659e..781bf46 100644 (file)
 #ifndef GNUPG_SCD_APP_COMMON_H
 #define GNUPG_SCD_APP_COMMON_H
 
-#if GNUPG_MAJOR_VERSION == 1
-# ifdef ENABLE_AGENT_SUPPORT
-# include "assuan.h"
-# endif
-#else
-# include <ksba.h>
-#endif
+#include <npth.h>
+#include <ksba.h>
 
 
 #define APP_CHANGE_FLAG_RESET    1
 struct app_local_s;  /* Defined by all app-*.c.  */
 
 struct app_ctx_s {
+  struct app_ctx_s *next;
+
+  npth_mutex_t lock;
+
   /* Number of connections currently using this application context.
      If this is not 0 the application has been initialized and the
      function pointers may be used.  Note that for unsupported
@@ -50,18 +49,12 @@ struct app_ctx_s {
   /* Used reader slot. */
   int slot;
 
-  /* If this is used by GnuPG 1.4 we need to know the assuan context
-     in case we need to divert the operation to an already running
-     agent.  This if ASSUAN_CTX is not NULL we take this as indication
-     that all operations are diverted to gpg-agent. */
-#if GNUPG_MAJOR_VERSION == 1
-  assuan_context_t assuan_ctx;
-#endif /*GNUPG_MAJOR_VERSION == 1*/
-
   unsigned char *serialno; /* Serialnumber in raw form, allocated. */
   size_t serialnolen;      /* Length in octets of serialnumber. */
   const char *apptype;
   unsigned int card_version;
+  unsigned int card_status;
+  unsigned int require_get_status:1;
   unsigned int did_chv1:1;
   unsigned int force_chv1:1;   /* True if the card does not cache CHV1. */
   unsigned int did_chv2:1;
@@ -119,20 +112,8 @@ struct app_ctx_s {
                       gpg_error_t (*pincb)(void*, const char *, char **),
                       void *pincb_arg);
   } fnc;
-
 };
 
-#if GNUPG_MAJOR_VERSION == 1
-gpg_error_t app_select_openpgp (app_t app);
-gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
-gpg_error_t app_openpgp_storekey (app_t app, int keyno,
-                          unsigned char *template, size_t template_len,
-                          time_t created_at,
-                          const unsigned char *m, size_t mlen,
-                          const unsigned char *e, size_t elen,
-                          gpg_error_t (*pincb)(void*, const char *, char **),
-                          void *pincb_arg);
-#else
 /*-- app-help.c --*/
 unsigned int app_help_count_bits (const unsigned char *a, size_t len);
 gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
@@ -142,10 +123,10 @@ size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
 /*-- app.c --*/
 void app_dump_state (void);
 void application_notify_card_reset (int slot);
-gpg_error_t check_application_conflict (ctrl_t ctrl, int slot,
-                                        const char *name);
-gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
-                                app_t *r_app);
+gpg_error_t check_application_conflict (const char *name, app_t app);
+gpg_error_t app_reset (app_t app, ctrl_t ctrl, int send_reset);
+gpg_error_t select_application (ctrl_t ctrl, const char *name, app_t *r_app,
+                                int scan);
 char *get_supported_applications (void);
 void release_application (app_t app);
 gpg_error_t app_munge_serialno (app_t app);
@@ -222,8 +203,4 @@ gpg_error_t app_select_geldkarte (app_t app);
 gpg_error_t app_select_sc_hsm (app_t app);
 
 
-#endif
-
-
-
 #endif /*GNUPG_SCD_APP_COMMON_H*/
index 6868cc3..a78d652 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -1,5 +1,5 @@
 /* app.c - Application selection.
- *     Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <npth.h>
 
 #include "scdaemon.h"
+#include "exechelp.h"
 #include "app-common.h"
 #include "iso7816.h"
 #include "apdu.h"
 #include "tlv.h"
 
-/* This table is used to keep track of locks on a per reader base.
-   The index into the table is the slot number of the reader.  The
-   mutex will be initialized on demand (one of the advantages of a
-   userland threading system). */
-static struct
-{
-  int initialized;
-  npth_mutex_t lock;
-  app_t app;        /* Application context in use or NULL. */
-} lock_table[10];
-
-
-
-static void deallocate_app (app_t app);
-
-
+static npth_mutex_t app_list_lock;
+static app_t app_top;
 \f
 static void
 print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
@@ -69,54 +56,35 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
    success; only then the unlock_reader function must be called after
    returning from the handler. */
 static gpg_error_t
-lock_reader (int slot, ctrl_t ctrl)
+lock_app (app_t app, ctrl_t ctrl)
 {
-  int res;
-
-  if (slot < 0 || slot >= DIM (lock_table))
-    return gpg_error (slot<0? GPG_ERR_INV_VALUE : GPG_ERR_RESOURCE_LIMIT);
-
-  if (!lock_table[slot].initialized)
-    {
-      res = npth_mutex_init (&lock_table[slot].lock, NULL);
-      if (res)
-        {
-          log_error ("error initializing mutex: %s\n", strerror (res));
-          return gpg_error_from_errno (res);
-        }
-      lock_table[slot].initialized = 1;
-      lock_table[slot].app = NULL;
-    }
+  gpg_error_t err;
 
-  res = npth_mutex_lock (&lock_table[slot].lock);
-  if (res)
+  err = npth_mutex_lock (&app->lock);
+  if (err)
     {
-      log_error ("failed to acquire APP lock for slot %d: %s\n",
-                 slot, strerror (res));
-      return gpg_error_from_errno (res);
+      log_error ("failed to acquire APP lock for %p: %s\n",
+                 app, strerror (err));
+      return gpg_error_from_errno (err);
     }
 
-  apdu_set_progress_cb (slot, print_progress_line, ctrl);
+  apdu_set_progress_cb (app->slot, print_progress_line, ctrl);
 
   return 0;
 }
 
 /* Release a lock on the reader.  See lock_reader(). */
 static void
-unlock_reader (int slot)
+unlock_app (app_t app)
 {
-  int res;
-
-  if (slot < 0 || slot >= DIM (lock_table)
-      || !lock_table[slot].initialized)
-    log_bug ("unlock_reader called for invalid slot %d\n", slot);
+  gpg_error_t err;
 
-  apdu_set_progress_cb (slot, NULL, NULL);
+  apdu_set_progress_cb (app->slot, NULL, NULL);
 
-  res = npth_mutex_unlock (&lock_table[slot].lock);
-  if (res)
-    log_error ("failed to release APP lock for slot %d: %s\n",
-               slot, strerror (res));
+  err = npth_mutex_unlock (&app->lock);
+  if (err)
+    log_error ("failed to release APP lock for %p: %s\n",
+               app, strerror (err));
 }
 
 
@@ -125,20 +93,12 @@ unlock_reader (int slot)
 void
 app_dump_state (void)
 {
-  int slot;
+  app_t a;
 
-  for (slot=0; slot < DIM (lock_table); slot++)
-    if (lock_table[slot].initialized)
-      {
-        log_info ("app_dump_state: slot=%d", slot);
-        if (lock_table[slot].app)
-          {
-            log_printf (" app=%p", lock_table[slot].app);
-            if (lock_table[slot].app->apptype)
-              log_printf (" type='%s'", lock_table[slot].app->apptype);
-          }
-        log_printf ("\n");
-      }
+  npth_mutex_lock (&app_list_lock);
+  for (a = app_top; a; a = a->next)
+    log_info ("app_dump_state: app=%p type='%s'\n", a, a->apptype);
+  npth_mutex_unlock (&app_list_lock);
 }
 
 /* Check wether the application NAME is allowed.  This does not mean
@@ -155,54 +115,16 @@ is_app_allowed (const char *name)
 }
 
 
-/* This may be called to tell this module about a removed or resetted card. */
-void
-application_notify_card_reset (int slot)
-{
-  if (slot < 0 || slot >= DIM (lock_table))
-    return;
-
-  /* FIXME: We are ignoring any error value here.  */
-  lock_reader (slot, NULL);
-
-  /* Release the APP, as it's not reusable any more.  */
-  if (lock_table[slot].app)
-    {
-      if (lock_table[slot].app->ref_count)
-        log_bug ("trying to release active context\n");
-
-      deallocate_app (lock_table[slot].app);
-      lock_table[slot].app = NULL;
-      log_debug ("application has been released\n");
-    }
-
-  unlock_reader (slot);
-}
-
-
-/*
- * This function is called with lock held.
- */
 static gpg_error_t
-check_conflict (int slot, const char *name)
+check_conflict (app_t app, const char *name)
 {
-  app_t app = lock_table[slot].app;
-
   if (!app || !name || (app->apptype && !ascii_strcasecmp (app->apptype, name)))
     return 0;
 
-  if (!app->ref_count)
-    {
-      lock_table[slot].app = NULL;
-      deallocate_app (app);
-      return 0;
-    }
-  else
-    {
-      log_info ("application '%s' in use by reader %d - can't switch\n",
-                app->apptype? app->apptype : "<null>", slot);
-      return gpg_error (GPG_ERR_CONFLICT);
-    }
+  log_info ("application '%s' in use - can't switch\n",
+            app->apptype? app->apptype : "<null>");
+
+  return gpg_error (GPG_ERR_CONFLICT);
 }
 
 /* This function is used by the serialno command to check for an
@@ -210,85 +132,85 @@ check_conflict (int slot, const char *name)
    used to request a specific application and the connection has
    already done a select_application. */
 gpg_error_t
-check_application_conflict (ctrl_t ctrl, int slot, const char *name)
+check_application_conflict (const char *name, app_t app)
 {
-  gpg_error_t err;
+  return check_conflict (app, name);
+}
 
-  if (slot < 0 || slot >= DIM (lock_table))
-    return gpg_error (GPG_ERR_INV_VALUE);
 
-  err = lock_reader (slot, ctrl);
-  if (err)
-    return err;
+static void
+release_application_internal (app_t app)
+{
+  if (!app->ref_count)
+    log_bug ("trying to release an already released context\n");
 
-  err = check_conflict (slot, name);
-  unlock_reader (slot);
-  return err;
+  --app->ref_count;
 }
 
-
-/* If called with NAME as NULL, select the best fitting application
-   and return a context; otherwise select the application with NAME
-   and return a context.  SLOT identifies the reader device. Returns
-   an error code and stores NULL at R_APP if no application was found
-   or no card is present. */
 gpg_error_t
-select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
+app_reset (app_t app, ctrl_t ctrl, int send_reset)
 {
   gpg_error_t err;
-  app_t app = NULL;
-  unsigned char *result = NULL;
-  size_t resultlen;
-  int want_undefined;
-
-  (void)ctrl;
 
-  *r_app = NULL;
-
-  want_undefined = (name && !strcmp (name, "undefined"));
-
-  err = lock_reader (slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
 
-  /* First check whether we already have an application to share. */
-  err = check_conflict (slot, name);
-  if (err)
+  if (send_reset)
     {
-      unlock_reader (slot);
-      return err;
-    }
-
-  app = lock_table[slot].app;
+      int sw = apdu_reset (app->slot);
+      if (sw)
+        err = gpg_error (GPG_ERR_CARD_RESET);
 
-  /* If we can reuse an application, bump the reference count and
-     return it.  */
-  if (app)
+      /* Release the same application which is used by other sessions.  */
+      send_client_notifications (app);
+    }
+  else
     {
-      if (app->slot != slot)
-        log_bug ("slot mismatch %d/%d\n", app->slot, slot);
-      app->slot = slot;
-
-      app->ref_count++;
-      *r_app = app;
-      unlock_reader (slot);
-      return 0; /* Okay: We share that one. */
+      ctrl->app_ctx = NULL;
+      release_application_internal (app);
     }
 
+  unlock_app (app);
+  return err;
+}
+
+static gpg_error_t
+app_new_register (int slot, ctrl_t ctrl, const char *name)
+{
+  gpg_error_t err = 0;
+  app_t app = NULL;
+  unsigned char *result = NULL;
+  size_t resultlen;
+  int want_undefined;
+
   /* Need to allocate a new one.  */
   app = xtrycalloc (1, sizeof *app);
   if (!app)
     {
       err = gpg_error_from_syserror ();
       log_info ("error allocating context: %s\n", gpg_strerror (err));
-      unlock_reader (slot);
       return err;
     }
+
   app->slot = slot;
+  err = npth_mutex_init (&app->lock, NULL);
+  if (err)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error initializing mutex: %s\n", strerror (err));
+      xfree (app);
+      return err;
+    }
 
+  err = lock_app (app, ctrl);
+  if (err)
+    {
+      xfree (app);
+      return err;
+    }
 
-  /* Fixme: We should now first check whether a card is at all
-     present. */
+  want_undefined = (name && !strcmp (name, "undefined"));
 
   /* Try to read the GDO file first to get a default serial number.
      We skip this if the undefined application has been requested. */
@@ -377,19 +299,87 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
       else
         log_info ("no supported card application found: %s\n",
                   gpg_strerror (err));
+      unlock_app (app);
       xfree (app);
-      unlock_reader (slot);
       return err;
     }
 
-  app->ref_count = 1;
+  app->require_get_status = 1;   /* For token, this can be 0.  */
 
-  lock_table[slot].app = app;
-  *r_app = app;
-  unlock_reader (slot);
+  npth_mutex_lock (&app_list_lock);
+  app->next = app_top;
+  app_top = app;
+  npth_mutex_unlock (&app_list_lock);
+  unlock_app (app);
   return 0;
 }
 
+/* If called with NAME as NULL, select the best fitting application
+   and return a context; otherwise select the application with NAME
+   and return a context.  Returns an error code and stores NULL at
+   R_APP if no application was found or no card is present. */
+gpg_error_t
+select_application (ctrl_t ctrl, const char *name, app_t *r_app, int scan)
+{
+  gpg_error_t err;
+  app_t app;
+  int slot;
+
+  *r_app = NULL;
+
+  if (scan
+      /* FIXME: Here, we can change code to support multiple readers.
+         For now, we only open a single reader.
+      */
+      && !app_top)
+    {
+      slot = apdu_open_reader (opt.reader_port);
+      if (slot >= 0)
+        {
+          int sw = apdu_connect (slot);
+
+          if (sw == SW_HOST_CARD_INACTIVE)
+            {
+              /* Try again.  */
+              sw = apdu_reset (slot);
+            }
+
+          if (!sw || sw == SW_HOST_ALREADY_CONNECTED)
+            err = 0;
+          else
+            err = gpg_error (GPG_ERR_ENODEV);
+        }
+      else
+        err = gpg_error (GPG_ERR_ENODEV);
+
+      if (!err)
+        err = app_new_register (slot, ctrl, name);
+      else
+        apdu_close_reader (slot);
+    }
+  else
+    err = 0;
+
+  if (!err)
+    app = app_top;
+  else
+    app = NULL;
+
+  if (app)
+    {
+      lock_app (app, ctrl);
+      err = check_conflict (app, name);
+      if (!err)
+        {
+          app->ref_count++;
+          *r_app = app;
+        }
+      unlock_app (app);
+    }
+
+  return err;
+}
+
 
 char *
 get_supported_applications (void)
@@ -425,10 +415,27 @@ get_supported_applications (void)
 }
 
 
-/* Deallocate the application. */
+/* Deallocate the application.  */
 static void
 deallocate_app (app_t app)
 {
+  app_t a, a_prev = NULL;
+
+  for (a = app_top; a; a = a->next)
+    if (a == app)
+      {
+        if (a_prev == NULL)
+          app_top = a->next;
+        else
+          a_prev->next = a->next;
+        break;
+      }
+    else
+      a_prev = a;
+
+  if (app->ref_count)
+    log_error ("trying to release context used yet (%d)\n", app->ref_count);
+
   if (app->fnc.deinit)
     {
       app->fnc.deinit (app);
@@ -447,33 +454,17 @@ deallocate_app (app_t app)
 void
 release_application (app_t app)
 {
-  int slot;
-
   if (!app)
     return;
 
-  if (!app->ref_count)
-    log_bug ("trying to release an already released context\n");
-  if (--app->ref_count)
-    return;
-
-  /* Move the reference to the application in the lock table. */
-  slot = app->slot;
-  /* FIXME: We are ignoring any error value.  */
-  lock_reader (slot, NULL);
-  if (lock_table[slot].app != app)
-    {
-      unlock_reader (slot);
-      log_bug ("app mismatch %p/%p\n", app, lock_table[slot].app);
-      deallocate_app (app);
-      return;
-    }
-
   /* We don't deallocate app here.  Instead, we keep it.  This is
      useful so that a card does not get reset even if only one session
      is using the card - this way the PIN cache and other cached data
      are preserved.  */
-  unlock_reader (slot);
+
+  lock_app (app, NULL);
+  release_application_internal (app);
+  unlock_app (app);
 }
 
 
@@ -570,11 +561,11 @@ app_write_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
   if (app->apptype && !(flags & 1))
     send_status_info (ctrl, "APPTYPE",
                       app->apptype, strlen (app->apptype), NULL, 0);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.learn_status (app, ctrl, flags);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -595,11 +586,11 @@ app_readcert (app_t app, ctrl_t ctrl, const char *certid,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.readcert)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.readcert (app, certid, cert, certlen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -628,11 +619,11 @@ app_readkey (app_t app, ctrl_t ctrl, int advanced, const char *keyid,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.readkey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err= app->fnc.readkey (app, advanced, keyid, pk, pklen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -670,11 +661,11 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
 
   if (!app->fnc.getattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err =  app->fnc.getattr (app, ctrl, name);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -693,11 +684,11 @@ app_setattr (app_t app, ctrl_t ctrl, const char *name,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.setattr)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -719,14 +710,14 @@ app_sign (app_t app, ctrl_t ctrl, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.sign)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.sign (app, keyidstr, hashalgo,
                        pincb, pincb_arg,
                        indata, indatalen,
                        outdata, outdatalen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation sign result: %s\n", gpg_strerror (err));
   return err;
@@ -751,14 +742,14 @@ app_auth (app_t app, ctrl_t ctrl, const char *keyidstr,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.auth)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.auth (app, keyidstr,
                        pincb, pincb_arg,
                        indata, indatalen,
                        outdata, outdatalen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation auth result: %s\n", gpg_strerror (err));
   return err;
@@ -786,7 +777,7 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.decipher)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.decipher (app, keyidstr,
@@ -794,7 +785,7 @@ app_decipher (app_t app, ctrl_t ctrl, const char *keyidstr,
                            indata, indatalen,
                            outdata, outdatalen,
                            r_info);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation decipher result: %s\n", gpg_strerror (err));
   return err;
@@ -817,12 +808,12 @@ app_writecert (app_t app, ctrl_t ctrl,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.writecert)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.writecert (app, ctrl, certidstr,
                             pincb, pincb_arg, data, datalen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation writecert result: %s\n", gpg_strerror (err));
   return err;
@@ -845,12 +836,12 @@ app_writekey (app_t app, ctrl_t ctrl,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.writekey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.writekey (app, ctrl, keyidstr, flags,
                            pincb, pincb_arg, keydata, keydatalen);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation writekey result: %s\n", gpg_strerror (err));
   return err;
@@ -872,12 +863,12 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.genkey)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.genkey (app, ctrl, keynostr, flags,
                          createtime, pincb, pincb_arg);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation genkey result: %s\n", gpg_strerror (err));
   return err;
@@ -896,11 +887,11 @@ app_get_challenge (app_t app, ctrl_t ctrl, size_t nbytes, unsigned char *buffer)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = iso7816_get_challenge (app->slot, nbytes, buffer);
-  unlock_reader (app->slot);
+  unlock_app (app);
   return err;
 }
 
@@ -920,12 +911,12 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.change_pin)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode,
                              pincb, pincb_arg);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation change_pin result: %s\n", gpg_strerror (err));
   return err;
@@ -948,12 +939,134 @@ app_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr,
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
   if (!app->fnc.check_pin)
     return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-  err = lock_reader (app->slot, ctrl);
+  err = lock_app (app, ctrl);
   if (err)
     return err;
   err = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg);
-  unlock_reader (app->slot);
+  unlock_app (app);
   if (opt.verbose)
     log_info ("operation check_pin result: %s\n", gpg_strerror (err));
   return err;
 }
+
+static void
+report_change (int slot, int old_status, int cur_status)
+{
+  char *homestr, *envstr;
+  char *fname;
+  char templ[50];
+  FILE *fp;
+
+  snprintf (templ, sizeof templ, "reader_%d.status", slot);
+  fname = make_filename (gnupg_homedir (), templ, NULL );
+  fp = fopen (fname, "w");
+  if (fp)
+    {
+      fprintf (fp, "%s\n",
+               (cur_status & 1)? "USABLE":
+               (cur_status & 4)? "ACTIVE":
+               (cur_status & 2)? "PRESENT": "NOCARD");
+      fclose (fp);
+    }
+  xfree (fname);
+
+  homestr = make_filename (gnupg_homedir (), NULL);
+  if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
+    log_error ("out of core while building environment\n");
+  else
+    {
+      gpg_error_t err;
+      const char *args[9], *envs[2];
+      char numbuf1[30], numbuf2[30], numbuf3[30];
+
+      envs[0] = envstr;
+      envs[1] = NULL;
+
+      sprintf (numbuf1, "%d", slot);
+      sprintf (numbuf2, "0x%04X", old_status);
+      sprintf (numbuf3, "0x%04X", cur_status);
+      args[0] = "--reader-port";
+      args[1] = numbuf1;
+      args[2] = "--old-code";
+      args[3] = numbuf2;
+      args[4] = "--new-code";
+      args[5] = numbuf3;
+      args[6] = "--status";
+      args[7] = ((cur_status & 1)? "USABLE":
+                 (cur_status & 4)? "ACTIVE":
+                 (cur_status & 2)? "PRESENT": "NOCARD");
+      args[8] = NULL;
+
+      fname = make_filename (gnupg_homedir (), "scd-event", NULL);
+      err = gnupg_spawn_process_detached (fname, args, envs);
+      if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
+        log_error ("failed to run event handler '%s': %s\n",
+                   fname, gpg_strerror (err));
+      xfree (fname);
+      xfree (envstr);
+    }
+  xfree (homestr);
+}
+
+void
+scd_update_reader_status_file (void)
+{
+  app_t a, app_next;
+
+  npth_mutex_lock (&app_list_lock);
+  for (a = app_top; a; a = app_next)
+    {
+      app_next = a->next;
+      if (a->require_get_status)
+        {
+          int sw;
+          unsigned int status;
+          sw = apdu_get_status (a->slot, 0, &status);
+
+          if (sw == SW_HOST_NO_READER)
+            {
+              /* Most likely the _reader_ has been unplugged.  */
+              status = 0;
+            }
+          else if (sw)
+            {
+              /* Get status failed.  Ignore that.  */
+              continue;
+            }
+
+          if (a->card_status != status)
+            {
+              report_change (a->slot, a->card_status, status);
+              send_client_notifications (a);
+
+              if (status == 0)
+                {
+                  log_debug ("Removal of a card: %d\n", a->slot);
+                  apdu_close_reader (a->slot);
+                  deallocate_app (a);
+                }
+              else
+                a->card_status = status;
+            }
+        }
+    }
+  npth_mutex_unlock (&app_list_lock);
+}
+
+/* This function must be called once to initialize this module.  This
+   has to be done before a second thread is spawned.  We can't do the
+   static initialization because Pth emulation code might not be able
+   to do a static init; in particular, it is not possible for W32. */
+void
+initialize_module_command (void)
+{
+  static int initialized;
+  int err;
+
+  if (!initialized)
+    {
+      err = npth_mutex_init (&app_list_lock, NULL);
+      if (!err)
+        initialized = 1;
+    }
+}
index 02bf76f..0c823d2 100644 (file)
@@ -37,7 +37,6 @@
 #include "iso7816.h"
 #include "apdu.h" /* Required for apdu_*_reader (). */
 #include "atr.h"
-#include "exechelp.h"
 #ifdef HAVE_LIBUSB
 #include "ccid-driver.h"
 #endif
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
-
-/* Macro to flag a removed card.  ENODEV is also tested to catch the
-   case of a removed reader.  */
-#define TEST_CARD_REMOVAL(c,r)                              \
-       do {                                                 \
-          int _r = (r);                                     \
-          if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
-              || gpg_err_code (_r) == GPG_ERR_CARD_REMOVED  \
-              || gpg_err_code (_r) == GPG_ERR_CARD_RESET    \
-              || gpg_err_code (_r) == GPG_ERR_ENODEV )      \
-            update_card_removed ((c)->server_local->vreader_idx, 1);      \
-       } while (0)
-
-#define IS_LOCKED(c)                                                    \
-  (locked_session                                                       \
-   && locked_session != (c)->server_local                               \
-   && (c)->server_local->vreader_idx != -1                              \
-   && locked_session->ctrl_backlink                                     \
-   && ((c)->server_local->vreader_idx                                   \
-       == locked_session->ctrl_backlink->server_local->vreader_idx))
-
-
-/* This structure is used to keep track of user readers.  To
-   eventually accommodate this structure for RFID cards, where more
-   than one card is used per reader, we name it virtual reader.  */
-struct vreader_s
-{
-  int valid;  /* True if the other objects are valid. */
-  int slot;   /* APDU slot number of the reader or -1 if not open. */
-
-  unsigned int status;  /* Last status of the reader. */
-};
+#define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local)
 
 
 /* Data used to associate an Assuan context with local server data.
@@ -116,16 +84,10 @@ struct server_local_s
   int event_signal;             /* Or 0 if not used. */
 #endif
 
-  /* Index into the vreader table (command.c) or -1 if not open. */
-  int vreader_idx;
-
   /* True if the card has been removed and a reset is required to
      continue operation. */
   int card_removed;
 
-  /* A disconnect command has been sent.  */
-  int disconnect_allowed;
-
   /* If set to true we will be terminate ourself at the end of the
      this session.  */
   int stopme;
@@ -133,10 +95,6 @@ struct server_local_s
 };
 
 
-/* The table with information on all used virtual readers.  */
-static struct vreader_s vreader_table[10];
-
-
 /* To keep track of all running sessions, we link all active server
    contexts and the anchor in this variable.  */
 static struct server_local_s *session_list;
@@ -145,88 +103,7 @@ static struct server_local_s *session_list;
    in this variable. */
 static struct server_local_s *locked_session;
 
-/* While doing a reset we need to make sure that the ticker does not
-   call scd_update_reader_status_file while we are using it. */
-static npth_mutex_t status_file_update_lock;
-
-\f
-/*-- Local prototypes --*/
-static void update_reader_status_file (int set_card_removed_flag);
-
-
 \f
-
-/* This function must be called once to initialize this module.  This
-   has to be done before a second thread is spawned.  We can't do the
-   static initialization because Pth emulation code might not be able
-   to do a static init; in particular, it is not possible for W32. */
-void
-initialize_module_command (void)
-{
-  static int initialized;
-  int err;
-
-  if (!initialized)
-    {
-      err = npth_mutex_init (&status_file_update_lock, NULL);
-      if (!err)
-        initialized = 1;
-    }
-}
-
-
-/* Helper to return the slot number for a given virtual reader index
-   VRDR.  In case on an error -1 is returned.  */
-static int
-vreader_slot (int vrdr)
-{
-  if (vrdr == -1 || !(vrdr >= 0 && vrdr < DIM(vreader_table)))
-    return -1;
-  if (!vreader_table [vrdr].valid)
-    return -1;
-  return vreader_table[vrdr].slot;
-}
-
-
-/* Update the CARD_REMOVED element of all sessions using the virtual
-   reader given by VRDR to VALUE.  */
-static void
-update_card_removed (int vrdr, int value)
-{
-  struct server_local_s *sl;
-
-  if (vrdr == -1)
-    return;
-
-  for (sl=session_list; sl; sl = sl->next_session)
-    {
-      ctrl_t ctrl = sl->ctrl_backlink;
-
-      if (ctrl && ctrl->server_local->vreader_idx == vrdr)
-        {
-          sl->card_removed = value;
-          if (value)
-            {
-              struct app_ctx_s *app = ctrl->app_ctx;
-              ctrl->app_ctx = NULL;
-              release_application (app);
-            }
-        }
-    }
-
-  /* Let the card application layer know about the removal.  */
-  if (value)
-    {
-      int slot = vreader_slot (vrdr);
-
-      log_debug ("Removal of a card: %d\n", vrdr);
-      apdu_close_reader (slot);
-      application_notify_card_reset (slot);
-      vreader_table[vrdr].slot = -1;
-    }
-}
-
-
 /* Convert the STRING into a newly allocated buffer while translating
    the hex numbers.  Stops at the first invalid character.  Blanks and
    colons are allowed to separate the hex digits.  Returns NULL on
@@ -265,61 +142,10 @@ hex_to_buffer (const char *string, size_t *r_length)
 static void
 do_reset (ctrl_t ctrl, int send_reset)
 {
-  int vrdr = ctrl->server_local->vreader_idx;
-  int slot;
-  int err;
-  struct app_ctx_s *app = ctrl->app_ctx;
-
-  if (!(vrdr == -1 || (vrdr >= 0 && vrdr < DIM(vreader_table))))
-    BUG ();
+  app_t app = ctrl->app_ctx;
 
-  /* If there is an active application, release it. */
   if (app)
-    {
-      ctrl->app_ctx = NULL;
-      release_application (app);
-    }
-
-  /* Release the same application which is used by other sessions.  */
-  if (send_reset)
-    {
-      struct server_local_s *sl;
-
-      for (sl=session_list; sl; sl = sl->next_session)
-        {
-          ctrl_t c = sl->ctrl_backlink;
-
-          if (c && c != ctrl && c->server_local->vreader_idx == vrdr)
-            {
-              struct app_ctx_s *app0 = c->app_ctx;
-              if (app0)
-                {
-                  c->app_ctx = NULL;
-                  release_application (app0);
-                }
-            }
-        }
-    }
-
-  /* If we want a real reset for the card, send the reset APDU and
-     tell the application layer about it.  */
-  slot = vreader_slot (vrdr);
-  if (slot != -1 && send_reset && !IS_LOCKED (ctrl) )
-    {
-      application_notify_card_reset (slot);
-      switch (apdu_reset (slot))
-        {
-        case 0:
-          break;
-        case SW_HOST_NO_CARD:
-        case SW_HOST_CARD_INACTIVE:
-          break;
-        default:
-          apdu_close_reader (slot);
-          vreader_table[vrdr].slot = -1;
-          break;
-        }
-    }
+    app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset);
 
   /* If we hold a lock, unlock now. */
   if (locked_session && ctrl->server_local == locked_session)
@@ -327,30 +153,7 @@ do_reset (ctrl_t ctrl, int send_reset)
       locked_session = NULL;
       log_info ("implicitly unlocking due to RESET\n");
     }
-
-  /* Reset the card removed flag for the current reader.  We need to
-     take the lock here so that the ticker thread won't concurrently
-     try to update the file.  Calling update_reader_status_file is
-     required to get hold of the new status of the card in the vreader
-     table.  */
-  err = npth_mutex_lock (&status_file_update_lock);
-  if (err)
-    {
-      log_error ("failed to acquire status_file_update lock\n");
-      ctrl->server_local->vreader_idx = -1;
-      return;
-    }
-  update_reader_status_file (0);  /* Update slot status table.  */
-  update_card_removed (vrdr, 0);  /* Clear card_removed flag.  */
-  err = npth_mutex_unlock (&status_file_update_lock);
-  if (err)
-    log_error ("failed to release status_file_update lock: %s\n",
-              strerror (err));
-
-  /* Do this last, so that the update_card_removed above does its job.  */
-  ctrl->server_local->vreader_idx = -1;
 }
-
 \f
 static gpg_error_t
 reset_notify (assuan_context_t ctx, char *line)
@@ -388,50 +191,11 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
 }
 
 
-/* Return the index of the current reader or open the reader if no
-   other sessions are using that reader.  If it is not possible to
-   open the reader -1 is returned.  Note, that we currently support
-   only one reader but most of the code (except for this function)
-   should be able to cope with several readers.  */
-static int
-get_current_reader (void)
-{
-  struct vreader_s *vr;
-
-  /* We only support one reader for now.  */
-  vr = &vreader_table[0];
-
-  /* Initialize the vreader item if not yet done. */
-  if (!vr->valid)
-    {
-      vr->slot = -1;
-      vr->valid = 1;
-    }
-
-  /* Try to open the reader. */
-  if (vr->slot == -1)
-    {
-      vr->slot = apdu_open_reader (opt.reader_port);
-
-      /* If we still don't have a slot, we have no readers.
-        Invalidate for now until a reader is attached. */
-      if (vr->slot == -1)
-       {
-         vr->valid = 0;
-       }
-    }
-
-  /* Return the vreader index or -1.  */
-  return vr->valid ? 0 : -1;
-}
-
-
 /* If the card has not yet been opened, do it.  */
 static gpg_error_t
-open_card (ctrl_t ctrl, const char *apptype)
+open_card (ctrl_t ctrl, const char *apptype, int scan)
 {
   gpg_error_t err;
-  int vrdr;
 
   /* If we ever got a card not present error code, return that.  Only
      the SERIALNO command and a reset are able to clear from that
@@ -445,43 +209,11 @@ open_card (ctrl_t ctrl, const char *apptype)
   /* If we are already initialized for one specific application we
      need to check that the client didn't requested a specific
      application different from the one in use before we continue. */
-  if (ctrl->app_ctx)
-    {
-      return check_application_conflict
-        (ctrl, vreader_slot (ctrl->server_local->vreader_idx), apptype);
-    }
+  if (!scan && ctrl->app_ctx)
+    return check_application_conflict (apptype, ctrl->app_ctx);
 
-  /* Setup the vreader and select the application.  */
-  if (ctrl->server_local->vreader_idx != -1)
-    vrdr = ctrl->server_local->vreader_idx;
-  else
-    vrdr = get_current_reader ();
-  ctrl->server_local->vreader_idx = vrdr;
-  if (vrdr == -1)
-    err = gpg_error (GPG_ERR_CARD);
-  else
-    {
-      /* Fixme: We should move the apdu_connect call to
-         select_application.  */
-      int sw;
-      int slot = vreader_slot (vrdr);
-
-      ctrl->server_local->disconnect_allowed = 0;
-      sw = apdu_connect (slot);
-      if (sw && sw != SW_HOST_ALREADY_CONNECTED)
-        {
-          if (sw == SW_HOST_NO_CARD)
-            err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
-          else if (sw == SW_HOST_CARD_INACTIVE)
-            err = gpg_error (GPG_ERR_CARD_RESET);
-          else
-            err = gpg_error (GPG_ERR_ENODEV);
-        }
-      else
-        err = select_application (ctrl, slot, apptype, &ctrl->app_ctx);
-    }
+  err = select_application (ctrl, apptype, &ctrl->app_ctx, scan);
 
-  TEST_CARD_REMOVAL (ctrl, err);
   return err;
 }
 
@@ -509,28 +241,35 @@ static gpg_error_t
 cmd_serialno (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
+  struct server_local_s *sl;
   int rc = 0;
   char *serial;
   time_t stamp;
-  int retries = 0;
 
   /* Clear the remove flag so that the open_card is able to reread it.  */
- retry:
   if (ctrl->server_local->card_removed)
     {
       if ( IS_LOCKED (ctrl) )
         return gpg_error (GPG_ERR_LOCKED);
-      do_reset (ctrl, 1);
+
+      ctrl->server_local->card_removed = 0;
     }
 
-  if ((rc = open_card (ctrl, *line? line:NULL)))
+  if ((rc = open_card (ctrl, *line? line:NULL, 1)))
     {
-      /* In case of an inactive card, retry once.  */
-      if (gpg_err_code (rc) == GPG_ERR_CARD_RESET && retries++ < 1)
-        goto retry;
+      ctrl->server_local->card_removed = 1;
       return rc;
     }
 
+  /* Success, clear the card_removed flag for all sessions.  */
+  for (sl=session_list; sl; sl = sl->next_session)
+    {
+      ctrl_t c = sl->ctrl_backlink;
+
+      if (c != ctrl)
+        c->server_local->card_removed = 0;
+    }
+
   rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
   if (rc)
     return rc;
@@ -618,7 +357,7 @@ cmd_learn (assuan_context_t ctx, char *line)
   int rc = 0;
   int only_keypairinfo = has_option (line, "--keypairinfo");
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   /* Unless the force option is used we try a shortcut by identifying
@@ -627,13 +366,15 @@ cmd_learn (assuan_context_t ctx, char *line)
      knows about this card */
   if (!only_keypairinfo)
     {
-      int slot;
       const char *reader;
       char *serial;
       time_t stamp;
+      app_t app = ctrl->app_ctx;
+
+      if (!app)
+        return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
 
-      slot = vreader_slot (ctrl->server_local->vreader_idx);
-      reader = apdu_get_reader_name (slot);
+      reader = apdu_get_reader_name (app->slot);
       if (!reader)
         return out_of_core ();
       send_status_direct (ctrl, "READER", reader);
@@ -682,7 +423,6 @@ cmd_learn (assuan_context_t ctx, char *line)
   if (!rc)
     rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -700,7 +440,7 @@ cmd_readcert (assuan_context_t ctx, char *line)
   unsigned char *cert;
   size_t ncert;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   line = xstrdup (line); /* Need a copy of the line. */
@@ -717,7 +457,6 @@ cmd_readcert (assuan_context_t ctx, char *line)
         return rc;
     }
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -743,7 +482,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
   unsigned char *pk;
   size_t pklen;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (has_option (line, "--advanced"))
@@ -804,7 +543,6 @@ cmd_readkey (assuan_context_t ctx, char *line)
  leave:
   ksba_cert_release (kc);
   xfree (cert);
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -966,10 +704,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
 
   line = skip_options (line);
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   /* We have to use a copy of the key ID because the function may use
@@ -998,7 +733,6 @@ cmd_pksign (assuan_context_t ctx, char *line)
         return rc; /* that is already an assuan error code */
     }
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1014,10 +748,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
   size_t outdatalen;
   char *keyidstr;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1046,7 +777,6 @@ cmd_pkauth (assuan_context_t ctx, char *line)
         return rc; /* that is already an assuan error code */
     }
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1063,10 +793,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
   char *keyidstr;
   unsigned int infoflags;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   keyidstr = xtrystrdup (line);
@@ -1096,7 +823,6 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
         return rc; /* that is already an assuan error code */
     }
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1120,7 +846,7 @@ cmd_getattr (assuan_context_t ctx, char *line)
   int rc;
   const char *keyword;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   keyword = line;
@@ -1135,7 +861,6 @@ cmd_getattr (assuan_context_t ctx, char *line)
      is locked.  */
   rc = app_getattr (ctrl->app_ctx, ctrl, keyword);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1163,10 +888,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
   size_t nbytes;
   char *line, *linebuf;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   /* We need to use a copy of LINE, because PIN_CB uses the same
@@ -1188,7 +910,6 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
                     (const unsigned char*)line, nbytes);
   xfree (linebuf);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1213,9 +934,6 @@ cmd_writecert (assuan_context_t ctx, char *line)
   unsigned char *certdata;
   size_t certdatalen;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
   line = skip_options (line);
 
   if (!*line)
@@ -1225,7 +943,7 @@ cmd_writecert (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1250,7 +968,6 @@ cmd_writecert (assuan_context_t ctx, char *line)
   xfree (certid);
   xfree (certdata);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1279,9 +996,6 @@ cmd_writekey (assuan_context_t ctx, char *line)
   unsigned char *keydata;
   size_t keydatalen;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
   line = skip_options (line);
 
   if (!*line)
@@ -1291,7 +1005,7 @@ cmd_writekey (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1317,7 +1031,6 @@ cmd_writekey (assuan_context_t ctx, char *line)
   xfree (keyid);
   xfree (keydata);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1357,9 +1070,6 @@ cmd_genkey (assuan_context_t ctx, char *line)
   const char *s;
   time_t timestamp;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
   force = has_option (line, "--force");
 
   if ((s=has_option_name (line, "--timestamp")))
@@ -1382,7 +1092,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1395,7 +1105,6 @@ cmd_genkey (assuan_context_t ctx, char *line)
                    timestamp, pin_cb, ctx);
   xfree (keyno);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1421,7 +1130,7 @@ cmd_random (assuan_context_t ctx, char *line)
                       "number of requested bytes missing");
   nbytes = strtoul (line, NULL, 0);
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1440,7 +1149,6 @@ cmd_random (assuan_context_t ctx, char *line)
     }
   xfree (buffer);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1466,9 +1174,6 @@ cmd_passwd (assuan_context_t ctx, char *line)
   if (has_option (line, "--nullpin"))
     flags |= APP_CHANGE_FLAG_NULLPIN;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
   line = skip_options (line);
 
   if (!*line)
@@ -1478,7 +1183,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
     line++;
   *line = 0;
 
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1492,7 +1197,6 @@ cmd_passwd (assuan_context_t ctx, char *line)
     log_error ("command passwd failed: %s\n", gpg_strerror (rc));
   xfree (chvnostr);
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1536,10 +1240,7 @@ cmd_checkpin (assuan_context_t ctx, char *line)
   int rc;
   char *idstr;
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
   if (!ctrl->app_ctx)
@@ -1557,7 +1258,6 @@ cmd_checkpin (assuan_context_t ctx, char *line)
   if (rc)
     log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
 
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -1592,8 +1292,8 @@ cmd_lock (assuan_context_t ctx, char *line)
     {
       rc = 0;
       npth_sleep (1); /* Better implement an event mechanism. However,
-                        for card operations this should be
-                        sufficient. */
+                         for card operations this should be
+                         sufficient. */
       /* FIXME: Need to check that the connection is still alive.
          This can be done by issuing status messages. */
       goto retry;
@@ -1690,20 +1390,12 @@ cmd_getinfo (assuan_context_t ctx, char *line)
   else if (!strcmp (line, "status"))
     {
       ctrl_t ctrl = assuan_get_pointer (ctx);
-      int vrdr = ctrl->server_local->vreader_idx;
+      app_t app = ctrl->app_ctx;
       char flag = 'r';
 
-      if (!ctrl->server_local->card_removed && vrdr != -1)
-       {
-         struct vreader_s *vr;
-
-         if (!(vrdr >= 0 && vrdr < DIM(vreader_table)))
-           BUG ();
+      if (!ctrl->server_local->card_removed && app)
+        flag = 'u';
 
-         vr = &vreader_table[vrdr];
-         if (vr->valid && (vr->status & 1))
-           flag = 'u';
-       }
       rc = assuan_send_data (ctx, &flag, 1);
     }
   else if (!strcmp (line, "reader_list"))
@@ -1751,7 +1443,7 @@ static gpg_error_t
 cmd_restart (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  struct app_ctx_s *app = ctrl->app_ctx;
+  app_t app = ctrl->app_ctx;
 
   (void)line;
 
@@ -1772,8 +1464,7 @@ cmd_restart (assuan_context_t ctx, char *line)
 static const char hlp_disconnect[] =
   "DISCONNECT\n"
   "\n"
-  "Disconnect the card if it is not any longer used by other\n"
-  "connections and the backend supports a disconnect operation.";
+  "Disconnect the card if the backend supports a disconnect operation.";
 static gpg_error_t
 cmd_disconnect (assuan_context_t ctx, char *line)
 {
@@ -1781,7 +1472,10 @@ cmd_disconnect (assuan_context_t ctx, char *line)
 
   (void)line;
 
-  ctrl->server_local->disconnect_allowed = 1;
+  if (!ctrl->app_ctx)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+  apdu_disconnect (ctrl->app_ctx->slot);
   return 0;
 }
 
@@ -1810,6 +1504,7 @@ static gpg_error_t
 cmd_apdu (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
+  app_t app;
   int rc;
   unsigned char *apdu;
   size_t apdulen;
@@ -1817,7 +1512,6 @@ cmd_apdu (assuan_context_t ctx, char *line)
   int handle_more;
   const char *s;
   size_t exlen;
-  int slot;
 
   if (has_option (line, "--dump-atr"))
     with_atr = 2;
@@ -1837,13 +1531,12 @@ cmd_apdu (assuan_context_t ctx, char *line)
 
   line = skip_options (line);
 
-  if ( IS_LOCKED (ctrl) )
-    return gpg_error (GPG_ERR_LOCKED);
-
-  if ((rc = open_card (ctrl, NULL)))
+  if ((rc = open_card (ctrl, NULL, 0)))
     return rc;
 
-  slot = vreader_slot (ctrl->server_local->vreader_idx);
+  app = ctrl->app_ctx;
+  if (!app)
+    return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
 
   if (with_atr)
     {
@@ -1851,7 +1544,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
       size_t atrlen;
       char hexbuf[400];
 
-      atr = apdu_get_atr (slot, &atrlen);
+      atr = apdu_get_atr (app->slot, &atrlen);
       if (!atr || atrlen > sizeof hexbuf - 2 )
         {
           rc = gpg_error (GPG_ERR_INV_CARD);
@@ -1896,7 +1589,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
       unsigned char *result = NULL;
       size_t resultlen;
 
-      rc = apdu_send_direct (slot, exlen,
+      rc = apdu_send_direct (app->slot, exlen,
                              apdu, apdulen, handle_more,
                              &result, &resultlen);
       if (rc)
@@ -1910,7 +1603,6 @@ cmd_apdu (assuan_context_t ctx, char *line)
   xfree (apdu);
 
  leave:
-  TEST_CARD_REMOVAL (ctrl, rc);
   return rc;
 }
 
@@ -2015,7 +1707,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
   else
     {
       rc = assuan_init_socket_server (ctx, INT2FD(fd),
-                                     ASSUAN_SOCKET_SERVER_ACCEPTED);
+                                      ASSUAN_SOCKET_SERVER_ACCEPTED);
     }
   if (rc)
     {
@@ -2040,10 +1732,6 @@ scd_command_handler (ctrl_t ctrl, int fd)
   ctrl->server_local->ctrl_backlink = ctrl;
   ctrl->server_local->assuan_ctx = ctx;
 
-  /* We open the reader right at startup so that the ticker is able to
-     update the status file. */
-  ctrl->server_local->vreader_idx = get_current_reader ();
-
   /* Command processing loop. */
   for (;;)
     {
@@ -2160,8 +1848,8 @@ send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
 
 
 /* Helper to send the clients a status change notification.  */
-static void
-send_client_notifications (void)
+void
+send_client_notifications (app_t app)
 {
   struct {
     pid_t pid;
@@ -2177,7 +1865,8 @@ send_client_notifications (void)
 
   for (sl=session_list; sl; sl = sl->next_session)
     {
-      if (sl->event_signal && sl->assuan_ctx)
+      if (sl->event_signal && sl->assuan_ctx
+          && sl->ctrl_backlink->app_ctx == app)
         {
           pid_t pid = assuan_get_pid (sl->assuan_ctx);
 #ifdef HAVE_W32_SYSTEM
@@ -2230,153 +1919,10 @@ send_client_notifications (void)
                 }
             }
 #endif /*!HAVE_W32_SYSTEM*/
-        }
-    }
-}
-
-
-
-/* This is the core of scd_update_reader_status_file but the caller
-   needs to take care of the locking.  */
-static void
-update_reader_status_file (int set_card_removed_flag)
-{
-  int idx;
-  unsigned int status;
-
-  /* Note, that we only try to get the status, because it does not
-     make sense to wait here for a operation to complete.  If we are
-     busy working with a card, delays in the status file update should
-     be acceptable. */
-  for (idx=0; idx < DIM(vreader_table); idx++)
-    {
-      struct vreader_s *vr = vreader_table + idx;
-      struct server_local_s *sl;
-      int sw_apdu;
 
-      if (!vr->valid || vr->slot == -1)
-        continue; /* Not valid or reader not yet open. */
-
-      sw_apdu = apdu_get_status (vr->slot, 0, &status);
-      if (sw_apdu == SW_HOST_NO_READER)
-        {
-          /* Most likely the _reader_ has been unplugged.  */
-          status = 0;
-        }
-      else if (sw_apdu)
-        {
-          /* Get status failed.  Ignore that.  */
-          continue;
+          sl->ctrl_backlink->app_ctx = NULL;
+          sl->card_removed = 1;
+          release_application (app);
         }
-
-      if (vr->status != status)
-        {
-          char *fname;
-          char templ[50];
-          FILE *fp;
-
-          log_info ("updating reader %d (%d) status: 0x%04X->0x%04X\n",
-                    idx, vr->slot, vr->status, status);
-
-         /* FIXME: Should this be IDX instead of vr->slot?  This
-            depends on how client sessions will associate the reader
-            status with their session.  */
-          snprintf (templ, sizeof templ, "reader_%d.status", vr->slot);
-          fname = make_filename (gnupg_homedir (), templ, NULL );
-          fp = fopen (fname, "w");
-          if (fp)
-            {
-              fprintf (fp, "%s\n",
-                       (status & 1)? "USABLE":
-                       (status & 4)? "ACTIVE":
-                       (status & 2)? "PRESENT": "NOCARD");
-              fclose (fp);
-            }
-          xfree (fname);
-
-          /* If a status script is executable, run it. */
-          {
-            const char *args[9], *envs[2];
-            char numbuf1[30], numbuf2[30], numbuf3[30];
-            char *homestr, *envstr;
-            gpg_error_t err;
-
-            homestr = make_filename (gnupg_homedir (), NULL);
-            if (gpgrt_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
-              log_error ("out of core while building environment\n");
-            else
-              {
-                envs[0] = envstr;
-                envs[1] = NULL;
-
-                sprintf (numbuf1, "%d", vr->slot);
-                sprintf (numbuf2, "0x%04X", vr->status);
-                sprintf (numbuf3, "0x%04X", status);
-                args[0] = "--reader-port";
-                args[1] = numbuf1;
-                args[2] = "--old-code";
-                args[3] = numbuf2;
-                args[4] = "--new-code";
-                args[5] = numbuf3;
-                args[6] = "--status";
-                args[7] = ((status & 1)? "USABLE":
-                           (status & 4)? "ACTIVE":
-                           (status & 2)? "PRESENT": "NOCARD");
-                args[8] = NULL;
-
-                fname = make_filename (gnupg_homedir (), "scd-event", NULL);
-                err = gnupg_spawn_process_detached (fname, args, envs);
-                if (err && gpg_err_code (err) != GPG_ERR_ENOENT)
-                  log_error ("failed to run event handler '%s': %s\n",
-                             fname, gpg_strerror (err));
-                xfree (fname);
-                xfree (envstr);
-              }
-            xfree (homestr);
-          }
-
-          /* Set the card removed flag for all current sessions.  */
-          if (status == 0 && set_card_removed_flag)
-            update_card_removed (idx, 1);
-
-          vr->status = status;
-
-          /* Send a signal to all clients who applied for it.  */
-          send_client_notifications ();
-        }
-
-      /* Check whether a disconnect is pending.  */
-      if (opt.card_timeout)
-        {
-          for (sl=session_list; sl; sl = sl->next_session)
-            if (!sl->disconnect_allowed)
-              break;
-          if (session_list && !sl)
-            {
-              /* FIXME: Use a real timeout.  */
-              /* At least one connection and all allow a disconnect.  */
-              log_info ("disconnecting card in reader %d (%d)\n",
-                        idx, vr->slot);
-              apdu_disconnect (vr->slot);
-            }
-        }
-
     }
 }
-
-/* This function is called by the ticker thread to check for changes
-   of the reader stati.  It updates the reader status files and if
-   requested by the caller also send a signal to the caller.  */
-void
-scd_update_reader_status_file (void)
-{
-  int err;
-  err = npth_mutex_lock (&status_file_update_lock);
-  if (err)
-    return; /* locked - give up. */
-  update_reader_status_file (1);
-  err = npth_mutex_unlock (&status_file_update_lock);
-  if (err)
-    log_error ("failed to release status_file_update lock: %s\n",
-              strerror (err));
-}
index 38aa490..e18ebfe 100644 (file)
@@ -124,6 +124,7 @@ void send_status_info (ctrl_t ctrl, const char *keyword, ...)
      GPGRT_ATTR_SENTINEL(1);
 void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
 void scd_update_reader_status_file (void);
+void send_client_notifications (app_t app);
 
 
 #endif /*SCDAEMON_H*/