scdaemon monitoring usb device removal
NIIBE Yutaka
gniibe at fsij.org
Thu Dec 22 05:55:09 CET 2016
NIIBE Yutaka <gniibe at fsij.org> writes:
> I'll post the change after the release of GnuPG 2.1.17. Please
> note that this is experimental change.
Here is the expelimenta change. It assumes that the card reader is
always token.
Writing this patch, I now consider that re-organizing the scdaemon would
be better.
Currently, gpg-agent assumes a single card and asks operations to
scdaemon by serial number of the card (+ identifier/keyno like
OPENPGP.1, OPENPGP.2, and OPENPGP.3). My current idea is that
it's much simpler if we can use KEYGRIP here. Then, supporting
multiple cards will be easier.
diff --git a/scd/apdu.c b/scd/apdu.c
index 225ceee..dc473ab 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -2597,7 +2597,7 @@ ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
/* Open the reader and try to read an ATR. */
static int
-open_ccid_reader (const char *portstr)
+open_ccid_reader (const char *portstr, void *arg)
{
int err;
int slot;
@@ -2608,7 +2608,7 @@ open_ccid_reader (const char *portstr)
return -1;
slotp = reader_table + slot;
- err = ccid_open_reader (&slotp->ccid.handle, portstr,
+ err = ccid_open_reader (&slotp->ccid.handle, portstr, arg,
(const char **)&slotp->rdrname);
if (err)
{
@@ -2964,7 +2964,7 @@ open_rapdu_reader (int portno,
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, void *arg)
{
static int pcsc_api_loaded, ct_api_loaded;
int slot;
@@ -2979,7 +2979,7 @@ apdu_open_reader (const char *portstr)
int i;
const char *s;
- slot = open_ccid_reader (portstr);
+ slot = open_ccid_reader (portstr, arg);
if (slot != -1)
{
once_available = 1;
diff --git a/scd/apdu.h b/scd/apdu.h
index c79b3fd..8970b32 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -85,7 +85,7 @@ enum {
/* Note, that apdu_open_reader returns no status word but -1 on error. */
-int apdu_open_reader (const char *portstr);
+int apdu_open_reader (const char *portstr, void *arg);
int apdu_open_remote_reader (const char *portstr,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 0917105..e22827e 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -89,6 +89,7 @@
#endif /*HAVE_NPTH*/
#include <libusb.h>
+#include <poll.h>
#include "scdaemon.h"
#include "iso7816.h"
@@ -273,6 +274,7 @@ struct ccid_driver_s
ccid_set_progress_cb. */
void (*progress_cb)(void *, const char *, int, int, int);
void *progress_cb_arg;
+ void *upper_layer;
};
@@ -1547,10 +1549,49 @@ ccid_vendor_specific_init (ccid_driver_t handle)
}
+static void
+removal_cb (void *arg)
+{
+ ccid_driver_t handle = (ccid_driver_t)arg;
+
+ scd_handle_reader_removal (handle->upper_layer);
+}
+
+static void
+manage_removal_of_device (ccid_driver_t handle, int is_token)
+{
+ const struct libusb_pollfd **pfd_array;
+
+ DEBUGOUT_1 ("manage removal of device (%p)\n", handle);
+
+ /* FIXME: it's good if there is an API of libusb to get
+ corresponding FD for the specific device.
+ This works for now with a single USB device.
+ For multiple devices support, fix is needed.
+ */
+ pfd_array = libusb_get_pollfds (NULL);
+ if (pfd_array)
+ {
+ const struct libusb_pollfd **p = pfd_array;
+ do
+ {
+ DEBUGOUT_2 ("polling fd (%d): %08x\n", (*p)->fd, (*p)->events);
+ if (((*p)->events & POLLOUT))
+ {
+ /* This is fd for the USB device. */
+ scd_manage_device_removal_check ((*p)->fd, is_token, removal_cb, handle);
+ }
+ }
+ while (*++p);
+
+ libusb_free_pollfds (pfd_array);
+ }
+}
+
/* Open the reader with the internal number READERNO and return a
pointer to be used as handle in HANDLE. Returns 0 on success. */
int
-ccid_open_reader (ccid_driver_t *handle, const char *readerid,
+ccid_open_reader (ccid_driver_t *handle, const char *readerid, void *arg,
const char **rdrname_p)
{
int rc = 0;
@@ -1622,6 +1663,8 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid,
(*handle)->ep_bulk_out = ep_bulk_out;
(*handle)->ep_bulk_in = ep_bulk_in;
(*handle)->ep_intr = ep_intr;
+ (*handle)->upper_layer = arg;
+ manage_removal_of_device (*handle, 1);
}
else if (dev_fd != -1) /* Device transport. */
{
@@ -3650,7 +3693,7 @@ main (int argc, char **argv)
break;
}
- rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL);
+ rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL, NULL);
if (rc)
return 1;
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index e3aed9f..f7c46d0 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -111,7 +111,7 @@ typedef struct ccid_driver_s *ccid_driver_t;
int ccid_set_debug_level (int level);
char *ccid_get_reader_list (void);
-int ccid_open_reader (ccid_driver_t *handle, const char *readerid,
+int ccid_open_reader (ccid_driver_t *handle, const char *readerid, void *arg,
const char **rdrname_p);
int ccid_set_progress_cb (ccid_driver_t handle,
void (*cb)(void *, const char *, int, int, int),
diff --git a/scd/command.c b/scd/command.c
index f6dfa39..d5598d4 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -395,7 +395,7 @@ get_current_reader (void)
/* Try to open the reader. */
if (vr->slot == -1)
{
- vr->slot = apdu_open_reader (opt.reader_port);
+ vr->slot = apdu_open_reader (opt.reader_port, vr);
/* If we still don't have a slot, we have no readers.
Invalidate for now until a reader is attached. */
@@ -2338,3 +2338,16 @@ scd_update_reader_status_file (void)
log_error ("failed to release status_file_update lock: %s\n",
strerror (err));
}
+
+
+void
+scd_handle_reader_removal (void *arg)
+{
+ struct vreader_s *vr = arg;
+ int idx = vr - vreader_table;
+
+ if (!vr->valid || vr->slot == -1)
+ return;
+
+ update_card_removed (idx, 1);
+}
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 38e3c40..8deeb13 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -1175,6 +1175,87 @@ start_connection_thread (void *arg)
return NULL;
}
+static fd_set fdset;
+static int nfd;
+
+struct reader_list {
+ int fd;
+ int is_token;
+ callback cb;
+ void *arg;
+ struct reader_list *next;
+};
+struct reader_list *reader_list;
+
+
+/* Register/Unregister FD to monitor removal of token. */
+void
+scd_manage_device_removal_check (int fd, int is_token, callback cb, void *arg)
+{
+ struct reader_list *l;
+
+ l = xtrycalloc (1, sizeof *l);
+ if (!l)
+ {
+ log_error ("error allocating device monitor check: %s\n",
+ strerror (errno) );
+ scd_exit (2);
+ }
+
+ l->fd = fd;
+ l->is_token = is_token;
+ l->cb = cb;
+ l->arg = arg;
+ l->next = reader_list;
+ reader_list = l;
+
+ FD_SET (fd, &fdset);
+ if (fd > nfd)
+ nfd = fd;
+}
+
+static int
+usb_token_registered (void)
+{
+ struct reader_list *l;
+
+ if (reader_list == NULL)
+ return 0;
+
+ for (l = reader_list; l; l = l->next)
+ if (!l->is_token)
+ return 0;
+
+ return 1;
+}
+
+static void
+handle_device_removal (int fd)
+{
+ struct reader_list *l, *l_prev, *l_next;
+
+ l_prev = NULL;
+ for (l = reader_list; l; l = l_next)
+ {
+ l_next = l->next;
+
+ if (l->fd == fd)
+ {
+ l->cb (l->arg);
+ FD_CLR (fd, &fdset);
+
+ if (l_prev)
+ l_prev->next = l_next;
+ else
+ reader_list = l_next;
+ xfree (l);
+ break;
+ }
+ else
+ l_prev = l;
+ }
+}
+
/* Connection handler loop. Wait for connection requests and spawn a
thread after accepting a connection. LISTEN_FD is allowed to be -1
@@ -1186,13 +1267,13 @@ handle_connections (int listen_fd)
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;
@@ -1241,29 +1322,35 @@ handle_connections (int listen_fd)
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 (usb_token_registered ())
+ t = NULL;
+ else
+ {
+ 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);
+ t = &timeout;
+ }
/* 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;
#ifndef HAVE_W32_SYSTEM
- ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, &timeout, npth_sigev_sigmask());
+ ret = npth_pselect (nfd+1, &read_fdset, NULL, NULL, t, npth_sigev_sigmask());
saved_errno = errno;
while (npth_sigev_get_pending(&signo))
handle_signal (signo);
#else
- ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, &timeout, NULL, NULL);
+ ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t, NULL, NULL);
saved_errno = errno;
#endif
@@ -1314,7 +1401,18 @@ handle_connections (int listen_fd)
npth_setname_np (thread, threadname);
}
fd = -1;
- }
+
+ ret--;
+ }
+
+ if (ret)
+ {
+ int i;
+
+ for (i = 0; i < FD_SETSIZE; i++)
+ if (FD_ISSET (i, &read_fdset))
+ handle_device_removal (i);
+ }
}
cleanup ();
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 38aa490..fd0b69f 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -112,6 +112,7 @@ struct server_control_s
};
typedef struct app_ctx_s *app_t;
+typedef void (*callback) (void *);
/*-- scdaemon.c --*/
void scd_exit (int rc);
@@ -124,6 +125,8 @@ 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 scd_manage_device_removal_check (int fd, int is_token, callback cb, void *arg);
+void scd_handle_reader_removal (void *arg);
#endif /*SCDAEMON_H*/
--
More information about the Gnupg-devel
mailing list