scd fix
authorWerner Koch <wk@gnupg.org>
Wed, 17 Mar 2010 12:47:57 +0000 (12:47 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 17 Mar 2010 12:47:57 +0000 (12:47 +0000)
ChangeLog
Makefile.am
patches/gnupg2-2.0.14/02-scd-no-service.patch [new file with mode: 0755]

index 8168e58..a5416db 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2010-03-17  Werner Koch  <wk@g10code.com>
+
+       * patches/gnupg2-2.0.14/02-scd-no-service.patch: New.
+
 2010-03-16  Emanuel Schuetze  <emanuel.schuetze@intevation.de>
 
        * configure.ac: Change compendium-de date and version (3.0.0-rc1)
index 34ce938..6f00faa 100644 (file)
@@ -57,6 +57,7 @@ EXTRA_DIST = autogen.sh README.SVN ONEWS \
        patches/gnupg2-2.0.12/10-inv-sgnr.patch \
        patches/gnupg2-2.0.12/11-photoid-sk.patch \
        patches/gnupg2-2.0.14/01-encode-s2k.patch \
+       patches/gnupg2-2.0.14/02-scd-no-service.patch \
        patches/dirmngr-1.0.3/01-ldaphttp.patch \
        patches/gpgol-0.9.91/01-gpgme.patch \
         patches/gpa-0.9.0/01-title-prop.patch \
diff --git a/patches/gnupg2-2.0.14/02-scd-no-service.patch b/patches/gnupg2-2.0.14/02-scd-no-service.patch
new file mode 100755 (executable)
index 0000000..ec8ec2c
--- /dev/null
@@ -0,0 +1,281 @@
+#! /bin/sh
+patch -p0 -f $* < $0
+exit $?
+
+
+common/
+2010-03-17  Werner Koch  <wk@g10code.com>
+
+       * asshelp.c (start_new_gpg_agent) [W32]: Use a named mutex to
+       avoid starting two agents.
+
+agent/
+2010-03-17  Werner Koch  <wk@g10code.com>
+
+       * call-scd.c (unlock_scd): Send a BYE under certain conditions.
+
+scd/
+2010-02-19  Werner Koch  <wk@g10code.com>
+       * command.c (open_card): Return GPG_ERR_NOT_OPERATIONAL if no
+       card services are available.
+       (get_reader_slot): Detect no services status.
+       (cmd_serialno): No reset if there are no services.
+       (scd_command_handler): Stop scdaemon in that case.
+       * apdu.c (pcsc_no_service): New.
+       (open_pcsc_reader_direct): Set it.
+       (apdu_open_reader): Add arg R_NO_SERVICE.
+
+
+
+Index: common/asshelp.c
+===================================================================
+--- common/asshelp.c   (revision 5295)
++++ common/asshelp.c   (working copy)
+@@ -223,25 +230,68 @@
+                and thus there is no need for the GPG_AGENT_INFO
+                envvar.  This is possible as we don't have a real unix
+                domain socket but use a plain file and thus there is no
+-               need to care about non-local file systems. */
++               need to care about non-local file systems.  We use a
++               named mutex to interlock the spawning.  There is just
++               one problem with that: If gpg-agent needs more than 3
++               seconds to come up and listen on the socket we might
++               still spawn another agent.  However this is no serious
++               problem because an agent detects this and handles it.
++               Thus the mutex merely helps to save resources in the
++               most common cases.  */
+             const char *argv[3];
++            HANDLE mutex;
++            int waitrc;
+             argv[0] = "--daemon";
+             argv[1] = "--use-standard-socket"; 
+             argv[2] = NULL;  
+-            rc = gnupg_spawn_process_detached (agent_program, argv, NULL);
+-            if (rc)
+-              log_debug ("failed to start agent `%s': %s\n",
+-                         agent_program, gpg_strerror (rc));
+-            else
++            mutex = CreateMutex (NULL, FALSE, "GnuPG_spawn_agent_sentinel");
++            if (!mutex)
++              {
++                log_error ("failed to create the spawn_agent mutex: %s\n",
++                           w32_strerror (-1));
++                rc = gpg_error (GPG_ERR_GENERAL);
++              }
++            else if ((waitrc = WaitForSingleObject (mutex, 5000))
++                     == WAIT_OBJECT_0)
+               {
+-                /* Give the agent some time to prepare itself. */
+-                gnupg_sleep (3);
+-                /* Now try again to connect the agent.  */
+                 rc = assuan_socket_connect (&ctx, sockname, 0);
++                if (rc)
++                  {
++                    /* Still not available.  */
++                    rc = gnupg_spawn_process_detached (agent_program,
++                                                       argv, NULL);
++                    if (rc)
++                      log_debug ("failed to start agent `%s': %s\n",
++                                 agent_program, gpg_strerror (rc));
++                    else
++                      {
++                        /* Give the agent some time to prepare itself. */
++                        gnupg_sleep (3);
++                        /* Now try again to connect the agent.  */
++                        rc = assuan_socket_connect (&ctx, sockname, 0);
++                      }
++                  }
++                if (!ReleaseMutex (mutex))
++                  log_error ("failed to release the spawn_agent mutex: %s\n",
++                             w32_strerror (-1));
+               }
+-          }
++            else if (waitrc == WAIT_TIMEOUT)
++              {
++                log_info ("error waiting for the spawn_agent mutex: timeout\n");
++                rc = gpg_error (GPG_ERR_GENERAL);
++              }
++            else
++              {
++                log_debug ("error waiting for the spawn_agent mutex: "
++                           "(code=%d) %s\n", waitrc, w32_strerror (-1));
++                rc = gpg_error (GPG_ERR_GENERAL);
++              }
++            
++            if (mutex)
++              CloseHandle (mutex);
++        }
+ #else /*!HAVE_W32_SYSTEM*/
+           {
+             const char *pgmname;
+
+Index: scd/apdu.h
+===================================================================
+--- scd/apdu.h (revision 5295)
++++ scd/apdu.h (working copy)
+@@ -80,8 +80,8 @@
+ #define APDU_CARD_ACTIVE   (4)    /* Card is active.  */
+-/* Note , that apdu_open_reader returns no status word but -1 on error. */
+-int apdu_open_reader (const char *portstr);
++/* Note, that apdu_open_reader returns no status word but -1 on error. */
++int apdu_open_reader (const char *portstr, int *r_no_service);
+ int apdu_open_remote_reader (const char *portstr,
+                              const unsigned char *cookie, size_t length,
+                              int (*readfnc) (void *opaque,
+Index: scd/command.c
+===================================================================
+--- scd/command.c      (revision 5295)
++++ scd/command.c      (working copy)
+@@ -70,6 +70,10 @@
+       && (c)->reader_slot == locked_session->ctrl_backlink->reader_slot)
++/* Flag indicating that the reader has been disabled.  */
++static int reader_disabled;
++
++
+ /* This structure is used to keep track of open readers (slots). */
+ struct slot_status_s 
+ {
+@@ -394,7 +398,15 @@
+   /* Try to open the reader. */
+   if (ss->slot == -1)
+-    ss->slot = apdu_open_reader (opt.reader_port);
++    {
++      int no_service_flag;
++      ss->slot = apdu_open_reader (opt.reader_port, &no_service_flag);
++      if (no_service_flag)
++        {
++          log_info ("no card services - disabling scdaemon\n");
++          reader_disabled = 1;
++        }
++    }
+   /* Return the slot_table index.  */
+   return 0;
+@@ -409,6 +421,9 @@
+   gpg_error_t err;
+   int slot;
++  if (reader_disabled)
++    return gpg_error (GPG_ERR_NOT_OPERATIONAL);
++
+   /* 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
+      state. */
+@@ -441,7 +456,7 @@
+     slot = get_reader_slot ();
+   ctrl->reader_slot = slot;
+   if (slot == -1)
+-    err = gpg_error (GPG_ERR_CARD);
++    err = gpg_error (reader_disabled? GPG_ERR_NOT_OPERATIONAL: GPG_ERR_CARD);
+   else
+     {
+       /* Fixme: We should move the apdu_connect call to
+@@ -495,7 +510,7 @@
+   time_t stamp;
+   /* Clear the remove flag so that the open_card is able to reread it.  */
+-  if (ctrl->server_local->card_removed)
++  if (!reader_disabled && ctrl->server_local->card_removed)
+     {
+       if ( IS_LOCKED (ctrl) )
+         return gpg_error (GPG_ERR_LOCKED);
+@@ -1995,7 +2010,7 @@
+           BUG ();
+       sl->next_session = ctrl->server_local->next_session;
+     }
+-  stopme = ctrl->server_local->stopme;
++  stopme = ctrl->server_local->stopme || reader_disabled;
+   xfree (ctrl->server_local);
+   ctrl->server_local = NULL;
+Index: scd/apdu.c
+===================================================================
+--- scd/apdu.c (revision 5295)
++++ scd/apdu.c (working copy)
+@@ -287,7 +287,10 @@
+ long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
+                                      unsigned long timeout);
++/* Flag set if PC/SC returned the no-service error.  */
++static int pcsc_no_service;
++
+ /*  Prototypes.  */
+ static int pcsc_get_status (int slot, unsigned int *status);
+ static int reset_pcsc_reader (int slot);
+@@ -1487,8 +1490,11 @@
+       log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
+                  pcsc_error_string (err), err);
+       reader_table[slot].used = 0;
++      if (err == 0x8010001d)
++        pcsc_no_service = 1;
+       return -1;
+     }
++  pcsc_no_service = 0;
+   err = pcsc_list_readers (reader_table[slot].pcsc.context,
+                            NULL, NULL, &nreader);
+@@ -2321,14 +2327,18 @@
+    error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
+    the first USB reader.  For PC/SC the first listed reader). */
+ int
+-apdu_open_reader (const char *portstr)
++apdu_open_reader (const char *portstr, int *r_no_service)
+ {
+   static int pcsc_api_loaded, ct_api_loaded;
++  int slot;
++  if (r_no_service)
++    *r_no_service = 0;
++
+ #ifdef HAVE_LIBUSB
+   if (!opt.disable_ccid)
+     {
+-      int slot, i;
++      int i;
+       const char *s;
+       slot = open_ccid_reader (portstr);
+@@ -2458,7 +2468,11 @@
+       pcsc_api_loaded = 1;
+     }
+-  return open_pcsc_reader (portstr);
++  slot = open_pcsc_reader (portstr);
++  if (slot == -1 && r_no_service && pcsc_no_service)
++    *r_no_service = 1;
++
++  return slot;
+ }
+Index: agent/call-scd.c
+===================================================================
+--- agent/call-scd.c   (revision 5295)
++++ agent/call-scd.c   (working copy)
+@@ -176,6 +176,17 @@
+ static int 
+ unlock_scd (ctrl_t ctrl, int rc)
+ {
++  if (gpg_err_code (rc) == GPG_ERR_NOT_OPERATIONAL
++      && gpg_err_source (rc) == GPG_ERR_SOURCE_SCD)
++    {
++      /* If the SCdaemon returned this error, it detected a major
++         problem, like no reader connected wants to stop.  To finish
++         this we need to stop the connection.  This simulates an
++         explicit killing of the SCdaemon.  */
++      assuan_transact (primary_scd_ctx, "BYE",
++                       NULL, NULL, NULL, NULL, NULL, NULL);
++    }
++      
+   if (ctrl->scd_local->locked != 1)
+     {
+       log_error ("unlock_scd: invalid lock count (%d)\n",