pcsc-lite  1.8.26
hotplug_libusb.c
Go to the documentation of this file.
1 /*
2  * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3  *
4  * Copyright (C) 2001-2004
5  * David Corcoran <corcoran@musclecard.com>
6  * Copyright (C) 2003-2011
7  * Ludovic Rousseau <ludovic.rousseau@free.fr>
8  * Copyright (C) 2003
9  * Toni Andjelkovic <toni@soth.at>
10  * Copyright (C) 2003-2004
11  * Damien Sauveron <damien.sauveron@labri.fr>
12  *
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
15 are met:
16 
17 1. Redistributions of source code must retain the above copyright
18  notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20  notice, this list of conditions and the following disclaimer in the
21  documentation and/or other materials provided with the distribution.
22 3. The name of the author may not be used to endorse or promote products
23  derived from this software without specific prior written permission.
24 
25 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
42 #include "config.h"
43 #ifdef HAVE_LIBUSB
44 
45 #include <string.h>
46 #include <sys/types.h>
47 #include <stdio.h>
48 #include <dirent.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <errno.h>
54 #include <libusb.h>
55 #include <pthread.h>
56 #include <signal.h>
57 
58 #include "misc.h"
59 #include "wintypes.h"
60 #include "pcscd.h"
61 #include "debuglog.h"
62 #include "parser.h"
63 #include "readerfactory.h"
64 #include "winscard_msg.h"
65 #include "sys_generic.h"
66 #include "hotplug.h"
67 #include "utils.h"
68 
69 #undef DEBUG_HOTPLUG
70 
71 /* format is "%d:%d:%d", bus_number, device_address, interface */
72 #define BUS_DEVICE_STRSIZE 10+1+10+1+10+1
73 
74 #define READER_ABSENT 0
75 #define READER_PRESENT 1
76 #define READER_FAILED 2
77 
78 #define FALSE 0
79 #define TRUE 1
80 
81 extern char Add_Serial_In_Name;
82 
83 /* we use the default libusb context */
84 #define ctx NULL
85 
86 pthread_mutex_t usbNotifierMutex;
87 
88 static pthread_t usbNotifyThread;
89 static int driverSize = -1;
90 static char AraKiriHotPlug = FALSE;
91 static int rescan_pipe[] = { -1, -1 };
92 extern int HPForceReaderPolling;
93 
94 /* values of ifdCapabilities bits */
95 #define IFD_GENERATE_HOTPLUG 1
96 
100 static struct _driverTracker
101 {
102  unsigned int manuID;
103  unsigned int productID;
104 
105  char *bundleName;
106  char *libraryPath;
107  char *readerName;
108  int ifdCapabilities;
109 } *driverTracker = NULL;
110 #define DRIVER_TRACKER_SIZE_STEP 8
111 
115 static struct _readerTracker
116 {
117  char status;
118  char bus_device[BUS_DEVICE_STRSIZE];
119  char *fullName;
120 } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];
121 
122 static LONG HPAddHotPluggable(struct libusb_device *dev,
123  struct libusb_device_descriptor desc,
124  const char bus_device[], int interface,
125  struct _driverTracker *driver);
126 static LONG HPRemoveHotPluggable(int reader_index);
127 
128 static LONG HPReadBundleValues(void)
129 {
130  LONG rv;
131  DIR *hpDir;
132  struct dirent *currFP = NULL;
133  char fullPath[FILENAME_MAX];
134  char fullLibPath[FILENAME_MAX];
135  int listCount = 0;
136 
137  hpDir = opendir(PCSCLITE_HP_DROPDIR);
138 
139  if (hpDir == NULL)
140  {
141  Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
142  Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd.");
143  return -1;
144  }
145 
146  /* allocate a first array */
147  driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker));
148  if (NULL == driverTracker)
149  {
150  Log1(PCSC_LOG_CRITICAL, "Not enough memory");
151  return -1;
152  }
153  driverSize = DRIVER_TRACKER_SIZE_STEP;
154 
155 #define GET_KEY(key, values) \
156  rv = LTPBundleFindValueWithKey(&plist, key, values); \
157  if (rv) \
158  { \
159  Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \
160  fullPath); \
161  continue; \
162  }
163 
164  while ((currFP = readdir(hpDir)) != 0)
165  {
166  if (strstr(currFP->d_name, ".bundle") != 0)
167  {
168  unsigned int alias;
169  list_t plist, *values;
170  list_t *manuIDs, *productIDs, *readerNames;
171  char *libraryPath;
172  int ifdCapabilities;
173 
174  /*
175  * The bundle exists - let's form a full path name and get the
176  * vendor and product ID's for this particular bundle
177  */
178  snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist",
179  PCSCLITE_HP_DROPDIR, currFP->d_name);
180  fullPath[sizeof(fullPath) - 1] = '\0';
181 
182  rv = bundleParse(fullPath, &plist);
183  if (rv)
184  continue;
185 
186  /* get CFBundleExecutable */
187  GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values)
188  libraryPath = list_get_at(values, 0);
189  (void)snprintf(fullLibPath, sizeof(fullLibPath),
190  "%s/%s/Contents/%s/%s",
191  PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
192  libraryPath);
193  fullLibPath[sizeof(fullLibPath) - 1] = '\0';
194 
195  /* Get ifdCapabilities */
196  GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values)
197  ifdCapabilities = strtol(list_get_at(values, 0), NULL, 16);
198 
199  GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs)
200  GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs)
201  GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames)
202 
203  /* while we find a nth ifdVendorID in Info.plist */
204  for (alias=0; alias<list_size(manuIDs); alias++)
205  {
206  char *value;
207 
208  /* variables entries */
209  value = list_get_at(manuIDs, alias);
210  driverTracker[listCount].manuID = strtol(value, NULL, 16);
211 
212  value = list_get_at(productIDs, alias);
213  driverTracker[listCount].productID = strtol(value, NULL, 16);
214 
215  driverTracker[listCount].readerName = strdup(list_get_at(readerNames, alias));
216 
217  /* constant entries for a same driver */
218  driverTracker[listCount].bundleName = strdup(currFP->d_name);
219  driverTracker[listCount].libraryPath = strdup(fullLibPath);
220  driverTracker[listCount].ifdCapabilities = ifdCapabilities;
221 
222 #ifdef DEBUG_HOTPLUG
223  Log2(PCSC_LOG_INFO, "Found driver for: %s",
224  driverTracker[listCount].readerName);
225 #endif
226  listCount++;
227  if (listCount >= driverSize)
228  {
229  int i;
230 
231  /* increase the array size */
232  driverSize += DRIVER_TRACKER_SIZE_STEP;
233 #ifdef DEBUG_HOTPLUG
234  Log2(PCSC_LOG_INFO,
235  "Increase driverTracker to %d entries", driverSize);
236 #endif
237  void* tmp = realloc(driverTracker,
238  driverSize * sizeof(*driverTracker));
239  if (NULL == tmp)
240  {
241  free(driverTracker);
242  Log1(PCSC_LOG_CRITICAL, "Not enough memory");
243  driverSize = -1;
244  closedir(hpDir);
245  return -1;
246  }
247  driverTracker = tmp;
248 
249  /* clean the newly allocated entries */
250  for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
251  {
252  driverTracker[i].manuID = 0;
253  driverTracker[i].productID = 0;
254  driverTracker[i].bundleName = NULL;
255  driverTracker[i].libraryPath = NULL;
256  driverTracker[i].readerName = NULL;
257  driverTracker[i].ifdCapabilities = 0;
258  }
259  }
260  }
261  bundleRelease(&plist);
262  }
263  }
264 
265  driverSize = listCount;
266  closedir(hpDir);
267 
268  if (driverSize == 0)
269  {
270  Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
271  Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
272  }
273 #ifdef DEBUG_HOTPLUG
274  else
275  Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
276 #endif
277 
278  return driverSize;
279 }
280 
281 static void HPRescanUsbBus(void)
282 {
283  int i, j;
284  char bus_device[BUS_DEVICE_STRSIZE];
285  libusb_device **devs, *dev;
286  ssize_t cnt;
287 
288  for (i=0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
289  /* clear rollcall */
290  readerTracker[i].status = READER_ABSENT;
291 
292  cnt = libusb_get_device_list(ctx, &devs);
293  if (cnt < 0)
294  {
295  Log1(PCSC_LOG_CRITICAL, "libusb_get_device_list() failed\n");
296  return;
297  }
298 
299  /* For each USB device */
300  cnt = 0;
301  while ((dev = devs[cnt++]) != NULL)
302  {
303  struct libusb_device_descriptor desc;
304  struct libusb_config_descriptor *config_desc;
305  uint8_t bus_number = libusb_get_bus_number(dev);
306  uint8_t device_address = libusb_get_device_address(dev);
307 
308  int r = libusb_get_device_descriptor(dev, &desc);
309  if (r < 0)
310  {
311  Log3(PCSC_LOG_ERROR, "failed to get device descriptor for %d/%d",
312  bus_number, device_address);
313  continue;
314  }
315 
316  r = libusb_get_active_config_descriptor(dev, &config_desc);
317  if (r < 0)
318  {
319  Log3(PCSC_LOG_ERROR, "failed to get device config for %d/%d",
320  bus_number, device_address);
321  continue;
322  }
323 
324  /* check if the device is supported by one driver */
325  for (i=0; i<driverSize; i++)
326  {
327  if (driverTracker[i].libraryPath != NULL &&
328  desc.idVendor == driverTracker[i].manuID &&
329  desc.idProduct == driverTracker[i].productID)
330  {
331  int interface;
332 
333 #ifdef DEBUG_HOTPLUG
334  Log3(PCSC_LOG_DEBUG, "Found matching USB device: %d:%d",
335  bus_number, device_address);
336 #endif
337 
338  for (interface = 0; interface < config_desc->bNumInterfaces;
339  interface++)
340  {
341  int newreader;
342 
343  /* A known device has been found */
344  snprintf(bus_device, BUS_DEVICE_STRSIZE, "%d:%d:%d",
345  bus_number, device_address, interface);
346  bus_device[BUS_DEVICE_STRSIZE - 1] = '\0';
347  newreader = TRUE;
348 
349  /* Check if the reader is a new one */
350  for (j=0; j<PCSCLITE_MAX_READERS_CONTEXTS; j++)
351  {
352  if (strncmp(readerTracker[j].bus_device,
353  bus_device, BUS_DEVICE_STRSIZE) == 0)
354  {
355  /* The reader is already known */
356  readerTracker[j].status = READER_PRESENT;
357  newreader = FALSE;
358 #ifdef DEBUG_HOTPLUG
359  Log2(PCSC_LOG_DEBUG, "Refresh USB device: %s",
360  bus_device);
361 #endif
362  break;
363  }
364  }
365 
366  /* New reader found */
367  if (newreader)
368  {
369  if (config_desc->bNumInterfaces > 1)
370  HPAddHotPluggable(dev, desc, bus_device,
371  interface, &driverTracker[i]);
372  else
373  HPAddHotPluggable(dev, desc, bus_device,
374  -1, &driverTracker[i]);
375  }
376  }
377  }
378  }
379  libusb_free_config_descriptor(config_desc);
380  }
381 
382  /*
383  * check if all the previously found readers are still present
384  */
385  for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
386  {
387  if ((readerTracker[i].status == READER_ABSENT) &&
388  (readerTracker[i].fullName != NULL))
389  HPRemoveHotPluggable(i);
390  }
391 
392  if (AraKiriHotPlug)
393  {
394  int retval;
395 
396  for (i=0; i<driverSize; i++)
397  {
398  /* free strings allocated by strdup() */
399  free(driverTracker[i].bundleName);
400  free(driverTracker[i].libraryPath);
401  free(driverTracker[i].readerName);
402  }
403  free(driverTracker);
404 
405  Log1(PCSC_LOG_INFO, "Hotplug stopped");
406  pthread_exit(&retval);
407  }
408 
409  /* free the libusb allocated list & devices */
410  libusb_free_device_list(devs, 1);
411 }
412 
413 static void HPEstablishUSBNotifications(int pipefd[2])
414 {
415  int i, do_polling;
416  int r;
417  char c = 42; /* magic value */
418 
419  r = libusb_init(ctx);
420  if (r < 0)
421  {
422  Log2(PCSC_LOG_CRITICAL, "libusb_init failed: %d", r);
423  /* emergency exit */
424  kill(getpid(), SIGTERM);
425  return;
426  }
427 
428  /* scan the USB bus for devices at startup */
429  HPRescanUsbBus();
430 
431  /* signal that the initially connected readers are now visible */
432  write(pipefd[1], &c, 1);
433 
434  /* if at least one driver do not have IFD_GENERATE_HOTPLUG */
435  do_polling = FALSE;
436  for (i=0; i<driverSize; i++)
437  if (driverTracker[i].libraryPath)
438  if ((driverTracker[i].ifdCapabilities & IFD_GENERATE_HOTPLUG) == 0)
439  {
440  Log2(PCSC_LOG_INFO,
441  "Driver %s does not support IFD_GENERATE_HOTPLUG. Using active polling instead.",
442  driverTracker[i].bundleName);
443  if (HPForceReaderPolling < 1)
444  HPForceReaderPolling = 1;
445  break;
446  }
447 
448  if (HPForceReaderPolling)
449  {
450  Log2(PCSC_LOG_INFO,
451  "Polling forced every %d second(s)", HPForceReaderPolling);
452  do_polling = TRUE;
453  }
454 
455  if (do_polling)
456  {
457  while (!AraKiriHotPlug)
458  {
459  SYS_Sleep(HPForceReaderPolling);
460  HPRescanUsbBus();
461  }
462  }
463  else
464  {
465  char dummy;
466 
467  pipe(rescan_pipe);
468  while (read(rescan_pipe[0], &dummy, sizeof(dummy)) > 0)
469  {
470  Log1(PCSC_LOG_INFO, "Reload serial configuration");
471  HPRescanUsbBus();
472 #ifdef USE_SERIAL
473  RFReCheckReaderConf();
474 #endif
475  Log1(PCSC_LOG_INFO, "End reload serial configuration");
476  }
477  close(rescan_pipe[0]);
478  rescan_pipe[0] = -1;
479  }
480 }
481 
482 LONG HPSearchHotPluggables(void)
483 {
484  int i;
485 
486  for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
487  {
488  readerTracker[i].status = READER_ABSENT;
489  readerTracker[i].bus_device[0] = '\0';
490  readerTracker[i].fullName = NULL;
491  }
492 
493  if (HPReadBundleValues() > 0)
494  {
495  int pipefd[2];
496  char c;
497 
498  if (pipe(pipefd) == -1)
499  {
500  Log2(PCSC_LOG_ERROR, "pipe: %s", strerror(errno));
501  return -1;
502  }
503 
504  ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
505  (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, pipefd);
506 
507  /* Wait for initial readers to setup */
508  read(pipefd[0], &c, 1);
509 
510  /* cleanup pipe fd */
511  close(pipefd[0]);
512  close(pipefd[1]);
513  }
514 
515  return 0;
516 }
517 
518 LONG HPStopHotPluggables(void)
519 {
520  AraKiriHotPlug = TRUE;
521  if (rescan_pipe[1] >= 0)
522  {
523  close(rescan_pipe[1]);
524  rescan_pipe[1] = -1;
525  }
526 
527  return 0;
528 }
529 
530 static LONG HPAddHotPluggable(struct libusb_device *dev,
531  struct libusb_device_descriptor desc,
532  const char bus_device[], int interface,
533  struct _driverTracker *driver)
534 {
535  int i;
536  char deviceName[MAX_DEVICENAME];
537 
538  Log2(PCSC_LOG_INFO, "Adding USB device: %s", bus_device);
539 
540  if (interface >= 0)
541  snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libhal:/org/freedesktop/Hal/devices/usb_device_%04x_%04x_serialnotneeded_if%d",
542  desc.idVendor, desc.idProduct, desc.idVendor, desc.idProduct,
543  interface);
544  else
545  snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libusb-1.0:%s",
546  desc.idVendor, desc.idProduct, bus_device);
547 
548  deviceName[sizeof(deviceName) -1] = '\0';
549 
550  pthread_mutex_lock(&usbNotifierMutex);
551 
552  /* find a free entry */
553  for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
554  {
555  if (readerTracker[i].fullName == NULL)
556  break;
557  }
558 
560  {
561  Log2(PCSC_LOG_ERROR,
562  "Not enough reader entries. Already found %d readers", i);
563  pthread_mutex_unlock(&usbNotifierMutex);
564  return 0;
565  }
566 
567  strncpy(readerTracker[i].bus_device, bus_device,
568  sizeof(readerTracker[i].bus_device));
569  readerTracker[i].bus_device[sizeof(readerTracker[i].bus_device) - 1] = '\0';
570 
571  if (Add_Serial_In_Name && desc.iSerialNumber)
572  {
573  libusb_device_handle *device;
574  int ret;
575 
576  ret = libusb_open(dev, &device);
577  if (ret < 0)
578  {
579  Log2(PCSC_LOG_ERROR, "libusb_open failed: %d", ret);
580  }
581  else
582  {
583  unsigned char serialNumber[MAX_READERNAME];
584 
585  ret = libusb_get_string_descriptor_ascii(device, desc.iSerialNumber,
586  serialNumber, MAX_READERNAME);
587  libusb_close(device);
588 
589  if (ret < 0)
590  {
591  Log2(PCSC_LOG_ERROR,
592  "libusb_get_string_descriptor_ascii failed: %d", ret);
593  readerTracker[i].fullName = strdup(driver->readerName);
594  }
595  else
596  {
597  char fullname[MAX_READERNAME];
598 
599  snprintf(fullname, sizeof(fullname), "%s (%s)",
600  driver->readerName, serialNumber);
601  readerTracker[i].fullName = strdup(fullname);
602  }
603  }
604  }
605  else
606  readerTracker[i].fullName = strdup(driver->readerName);
607 
608  if (RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
609  driver->libraryPath, deviceName) == SCARD_S_SUCCESS)
610  readerTracker[i].status = READER_PRESENT;
611  else
612  {
613  readerTracker[i].status = READER_FAILED;
614 
615  (void)CheckForOpenCT();
616  }
617 
618  pthread_mutex_unlock(&usbNotifierMutex);
619 
620  return 1;
621 } /* End of function */
622 
623 static LONG HPRemoveHotPluggable(int reader_index)
624 {
625  pthread_mutex_lock(&usbNotifierMutex);
626 
627  Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", reader_index,
628  readerTracker[reader_index].bus_device);
629 
630  RFRemoveReader(readerTracker[reader_index].fullName,
631  PCSCLITE_HP_BASE_PORT + reader_index);
632  free(readerTracker[reader_index].fullName);
633  readerTracker[reader_index].status = READER_ABSENT;
634  readerTracker[reader_index].bus_device[0] = '\0';
635  readerTracker[reader_index].fullName = NULL;
636 
637  pthread_mutex_unlock(&usbNotifierMutex);
638 
639  return 1;
640 } /* End of function */
641 
645 ULONG HPRegisterForHotplugEvents(void)
646 {
647  (void)pthread_mutex_init(&usbNotifierMutex, NULL);
648  return 0;
649 }
650 
651 void HPReCheckSerialReaders(void)
652 {
653  Log0(PCSC_LOG_INFO);
654  if (rescan_pipe[1] >= 0)
655  {
656  char dummy = 0;
657  write(rescan_pipe[1], &dummy, sizeof(dummy));
658  }
659 }
660 
661 #endif
662 
debuglog.h
This handles debugging.
SCARD_S_SUCCESS
#define SCARD_S_SUCCESS
No error was encountered.
Definition: pcsclite.h:107
sys_generic.h
This handles abstract system level calls.
readerfactory.h
This keeps track of a list of currently available reader structures.
SYS_Sleep
int SYS_Sleep(int)
Makes the current process sleep for some seconds.
Definition: sys_unix.c:53
list_t
list object
Definition: simclist.h:181
winscard_msg.h
This defines some structures and #defines to be used over the transport layer.
pcscd.h
This keeps a list of defines for pcsc-lite.
PCSCLITE_MAX_READERS_CONTEXTS
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader)
Definition: pcsclite.h:284
parser.h
Reads lexical config files and updates database.
wintypes.h
This keeps a list of Windows(R) types.
hotplug.h
This provides a search API for hot pluggble devices.