diff options
author | Werner Koch <wk@gnupg.org> | 2009-07-16 17:54:59 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2009-07-16 17:54:59 +0200 |
commit | e49d808e1f02d8f01c60753022bc20664b215062 (patch) | |
tree | dd65f9e2f0eb20d949e56852c22ee3cb0aba31a9 /scd | |
parent | [g10] (diff) | |
download | gnupg2-e49d808e1f02d8f01c60753022bc20664b215062.tar.xz gnupg2-e49d808e1f02d8f01c60753022bc20664b215062.zip |
Add code to better handle unplugging of a reader.
Diffstat (limited to 'scd')
-rw-r--r-- | scd/ChangeLog | 14 | ||||
-rw-r--r-- | scd/apdu.c | 29 | ||||
-rw-r--r-- | scd/apdu.h | 1 | ||||
-rw-r--r-- | scd/app.c | 4 | ||||
-rw-r--r-- | scd/ccid-driver.c | 45 | ||||
-rw-r--r-- | scd/command.c | 14 | ||||
-rw-r--r-- | scd/scdaemon.c | 2 |
7 files changed, 94 insertions, 15 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog index fcb13e386..46b15f0c7 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,17 @@ +2009-07-16 Werner Koch <wk@g10code.com> + + * command.c (update_reader_status_file): Test for unplugged reader. + (TEST_CARD_REMOVAL): Ditto. + * app.c (select_application): Ditto. + * ccid-driver.c (bulk_out): Return CCID_DRIVER_ERR_NO_READER if a + reader was unplugged. + (struct ccid_driver_s): Turn nonnull_nad into an unsigned char. + Turn apdu_level, auto_ifsd, powered_off, has_pinpad into + bitfields. Add enodev_seen. + * apdu.c (apdu_prepare_exit): New. + (get_status_ccid): Return the status word and nut just -1. + * scdaemon.c (scd_exit): Call it. + 2009-07-13 Werner Koch <wk@g10code.com> * ccid-driver.c (struct ccid_driver_s): Add fields last_progress, diff --git a/scd/apdu.c b/scd/apdu.c index 57e82c49c..36bae89c0 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1853,7 +1853,7 @@ get_status_ccid (int slot, unsigned int *status) rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits); if (rc) - return -1; + return rc; if (bits == 0) *status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE); @@ -2523,6 +2523,33 @@ apdu_close_reader (int slot) return SW_HOST_NOT_SUPPORTED; } + +/* Function suitable for a cleanup function to close all reader. It + should not be used if the reader will be opened again. The reason + for implementing this to properly close USB devices so that they + will startup the next time without error. */ +void +apdu_prepare_exit (void) +{ + static int sentinel; + int slot; + + if (!sentinel) + { + sentinel = 1; + for (slot = 0; slot < MAX_READER; slot++) + if (reader_table[slot].used) + { + apdu_disconnect (slot); + if (reader_table[slot].close_reader) + reader_table[slot].close_reader (slot); + reader_table[slot].used = 0; + } + sentinel = 0; + } +} + + /* Shutdown a reader; that is basically the same as a close but keeps the handle ready for later use. A apdu_reset_reader or apdu_connect should be used to get it active again. */ diff --git a/scd/apdu.h b/scd/apdu.h index 03c774cc2..c47dea882 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -94,6 +94,7 @@ int apdu_open_remote_reader (const char *portstr, void *closefnc_value); int apdu_shutdown_reader (int slot); int apdu_close_reader (int slot); +void apdu_prepare_exit (void); int apdu_enum_reader (int slot, int *used); unsigned char *apdu_get_atr (int slot, size_t *atrlen); @@ -367,10 +367,10 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) } /* For certain error codes, there is no need to try more. */ - if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) + if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT + || gpg_err_code (err) == GPG_ERR_ENODEV) goto leave; - /* Figure out the application to use. */ err = gpg_error (GPG_ERR_NOT_FOUND); diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index f9b0bb599..373f55f8a 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -84,6 +84,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <time.h> #include <usb.h> @@ -244,16 +245,18 @@ struct ccid_driver_s int seqno; unsigned char t1_ns; unsigned char t1_nr; - int nonnull_nad; - int auto_ifsd; + unsigned char nonnull_nad; int max_ifsd; int ifsd; int ifsc; - int powered_off; - int has_pinpad; - int apdu_level; /* Reader supports short APDU level exchange. - With a value of 2 short and extended level is - supported.*/ + unsigned char apdu_level:2; /* Reader supports short APDU level + exchange. With a value of 2 short + and extended level is supported.*/ + unsigned int auto_ifsd:1; + unsigned int powered_off:1; + unsigned int has_pinpad:2; + unsigned int enodev_seen:1; + time_t last_progress; /* Last time we sent progress line. */ /* The progress callback and its first arg as supplied to @@ -1423,7 +1426,7 @@ scan_or_find_devices (int readerno, const char *readerid, /* Set the level of debugging to LEVEL and return the old level. -1 just returns the old level. A level of 0 disables debugging, 1 enables debugging, 2 enables additional tracing of the T=1 - protocol, 3 additionally enables debuggng for GetSlotStatus, other + protocol, 3 additionally enables debugging for GetSlotStatus, other values are not yet defined. Note that libusb may provide its own debugging feature which is @@ -1763,6 +1766,11 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, { int rc; + /* No need to continue and clutter the log withy USB error if we + ever got an ENODEV. */ + if (handle->enodev_seen) + return CCID_DRIVER_ERR_NO_READER; + if (debug_level && (!no_debug || debug_level >= 3)) { switch (msglen? msg[0]:0) @@ -1823,8 +1831,27 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen, 5000 /* ms timeout */); if (rc == msglen) return 0; +#ifdef ENODEV + if (rc == -(ENODEV)) + { + /* The Linux libusb returns a negative error value. Catch + the most important one. */ + errno = ENODEV; + rc = -1; + } +#endif /*ENODEV*/ + if (rc == -1) - DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); + { + DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno)); +#ifdef ENODEV + if (errno == ENODEV) + { + handle->enodev_seen = 1; + return CCID_DRIVER_ERR_NO_READER; + } +#endif /*ENODEV*/ + } else DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc); } diff --git a/scd/command.c b/scd/command.c index ae1f4981b..1fdcf7d51 100644 --- a/scd/command.c +++ b/scd/command.c @@ -54,12 +54,14 @@ #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) -/* Macro to flag a removed card. */ +/* Macro to flag a removed card. ENODEV is also tested to catch teh + 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_REMOVED \ + || gpg_err_code (_r) == GPG_ERR_ENODEV ) \ update_card_removed ((c)->reader_slot, 1); \ } while (0) @@ -2159,7 +2161,13 @@ update_reader_status_file (int set_card_removed_flag) continue; /* Not valid or reader not yet open. */ sw_apdu = apdu_get_status (ss->slot, 0, &status, &changed); - if (sw_apdu) + if (sw_apdu == SW_HOST_NO_READER) + { + /* Most likely the _reader_ has been unplugged. */ + status = 0; + changed = ss->changed; + } + else if (sw_apdu) { /* Get status failed. Ignore that. */ continue; diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 49f09cf62..24cb346e6 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -48,6 +48,7 @@ #include "i18n.h" #include "sysutils.h" #include "app-common.h" +#include "apdu.h" #include "ccid-driver.h" #include "mkdtemp.h" #include "gc-opt-flags.h" @@ -858,6 +859,7 @@ main (int argc, char **argv ) void scd_exit (int rc) { + apdu_prepare_exit (); #if 0 #warning no update_random_seed_file update_random_seed_file(); |