pcsc-lite  1.8.11
hotplug_linux.c
Go to the documentation of this file.
1 /*
2  * MUSCLE SmartCard Development ( http://pcsclite.alioth.debian.org/pcsclite.html )
3  *
4  * Copyright (C) 2001-2003
5  * David Corcoran <corcoran@musclecard.com>
6  * Copyright (C) 2002-2011
7  * Ludovic Rousseau <ludovic.rousseau@free.fr>
8  *
9  * The USB code was based partly on Johannes Erdfelt
10  * libusb code found at libusb.sourceforge.net
11  *
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15 
16 1. Redistributions of source code must retain the above copyright
17  notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19  notice, this list of conditions and the following disclaimer in the
20  documentation and/or other materials provided with the distribution.
21 3. The name of the author may not be used to endorse or promote products
22  derived from this software without specific prior written permission.
23 
24 Changes to this license can be made only by the copyright author with
25 explicit written consent.
26 
27 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37  *
38  * $Id: hotplug_linux.c 6851 2014-02-14 15:43:32Z rousseau $
39  */
40 
46 #include "config.h"
47 #include <string.h>
48 
49 #if defined(__linux__) && !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUDEV)
50 #include <sys/types.h>
51 #include <stdio.h>
52 #include <dirent.h>
53 #include <fcntl.h>
54 #include <time.h>
55 #include <stdlib.h>
56 #include <unistd.h>
57 #include <pthread.h>
58 
59 #include "misc.h"
60 #include "pcsclite.h"
61 #include "pcscd.h"
62 #include "debuglog.h"
63 #include "parser.h"
64 #include "readerfactory.h"
65 #include "winscard_msg.h"
66 #include "sys_generic.h"
67 #include "hotplug.h"
68 #include "utils.h"
69 
70 #undef DEBUG_HOTPLUG
71 #define PCSCLITE_USB_PATH "/proc/bus/usb"
72 
73 #define FALSE 0
74 #define TRUE 1
75 
76 pthread_mutex_t usbNotifierMutex;
77 
78 struct usb_device_descriptor
79 {
80  u_int8_t bLength;
81  u_int8_t bDescriptorType;
82  u_int16_t bcdUSB;
83  u_int8_t bDeviceClass;
84  u_int8_t bDeviceSubClass;
85  u_int8_t bDeviceProtocol;
86  u_int8_t bMaxPacketSize0;
87  u_int16_t idVendor;
88  u_int16_t idProduct;
89  u_int16_t bcdDevice;
90  u_int8_t iManufacturer;
91  u_int8_t iProduct;
92  u_int8_t iSerialNumber;
93  u_int8_t bNumConfigurations;
94 }
95 __attribute__ ((packed));
96 
97 static LONG HPAddHotPluggable(int, unsigned long);
98 static LONG HPRemoveHotPluggable(int, unsigned long);
99 static LONG HPReadBundleValues(void);
100 static void HPEstablishUSBNotifications(void);
101 
102 static pthread_t usbNotifyThread;
103 static int AraKiriHotPlug = FALSE;
104 static int bundleSize = 0;
105 
109 static struct _bundleTracker
110 {
111  long manuID;
112  long productID;
113 
114  struct _deviceNumber {
115  int id;
116  char status;
117  } deviceNumber[PCSCLITE_MAX_READERS_CONTEXTS];
118 
119  char *bundleName;
120  char *libraryPath;
121  char *readerName;
122 }
123 bundleTracker[PCSCLITE_MAX_READERS_CONTEXTS];
124 
125 static LONG HPReadBundleValues(void)
126 {
127  LONG rv;
128  DIR *hpDir;
129  struct dirent *currFP = 0;
130  char fullPath[FILENAME_MAX];
131  char fullLibPath[FILENAME_MAX];
132  unsigned int listCount = 0;
133 
134  hpDir = opendir(PCSCLITE_HP_DROPDIR);
135 
136  if (hpDir == NULL)
137  {
138  Log1(PCSC_LOG_INFO,
139  "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
140  Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd.");
141  return -1;
142  }
143 
144 #define GET_KEY(key, values) \
145  rv = LTPBundleFindValueWithKey(&plist, key, values); \
146  if (rv) \
147  { \
148  Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \
149  fullPath); \
150  continue; \
151  }
152 
153  while ((currFP = readdir(hpDir)) != 0)
154  {
155  if (strstr(currFP->d_name, ".bundle") != 0)
156  {
157  unsigned int alias;
158  list_t plist, *values;
159  list_t *manuIDs, *productIDs, *readerNames;
160  char *libraryPath;
161 
162  /*
163  * The bundle exists - let's form a full path name and get the
164  * vendor and product ID's for this particular bundle
165  */
166  snprintf(fullPath, FILENAME_MAX, "%s/%s/Contents/Info.plist",
167  PCSCLITE_HP_DROPDIR, currFP->d_name);
168  fullPath[FILENAME_MAX - 1] = '\0';
169 
170  rv = bundleParse(fullPath, &plist);
171  if (rv)
172  continue;
173 
174  /* get CFBundleExecutable */
175  GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values)
176  libraryPath = list_get_at(values, 0);
177  (void)snprintf(fullLibPath, sizeof(fullLibPath),
178  "%s/%s/Contents/%s/%s",
179  PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
180  libraryPath);
181  fullLibPath[sizeof(fullLibPath) - 1] = '\0';
182 
183  GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values)
184  GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs)
185  GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs)
186  GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames)
187 
188  /* while we find a nth ifdVendorID in Info.plist */
189  for (alias=0; alias<list_size(manuIDs); alias++)
190  {
191  char *value;
192 
193  /* variables entries */
194  value = list_get_at(manuIDs, alias);
195  bundleTracker[listCount].manuID = strtol(value, NULL, 16);
196 
197  value = list_get_at(productIDs, alias);
198  bundleTracker[listCount].productID = strtol(value, NULL, 16);
199 
200  bundleTracker[listCount].readerName = strdup(list_get_at(readerNames, alias));
201 
202  /* constant entries for a same driver */
203  bundleTracker[listCount].bundleName = strdup(currFP->d_name);
204  bundleTracker[listCount].libraryPath = strdup(fullLibPath);
205 
206 #ifdef DEBUG_HOTPLUG
207  Log2(PCSC_LOG_INFO, "Found driver for: %s",
208  bundleTracker[listCount].readerName);
209 #endif
210  listCount++;
211 
212  if (listCount >= COUNT_OF(bundleTracker))
213  {
214  Log2(PCSC_LOG_CRITICAL, "Too many readers declared. Maximum is %zd", COUNT_OF(bundleTracker));
215  goto end;
216  }
217  }
218  bundleRelease(&plist);
219  }
220  }
221 
222 end:
223  bundleSize = listCount;
224 
225  if (bundleSize == 0)
226  {
227  Log1(PCSC_LOG_INFO,
228  "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
229  Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
230  }
231 
232  closedir(hpDir);
233  return 0;
234 }
235 
236 static void HPEstablishUSBNotifications(void)
237 {
238 
239  int i, j, usbDeviceStatus;
240  DIR *dir, *dirB;
241  struct dirent *entry, *entryB;
242  int deviceNumber;
243  int suspectDeviceNumber;
244  char dirpath[FILENAME_MAX];
245  char filename[FILENAME_MAX];
246  int fd, ret;
247  struct usb_device_descriptor usbDescriptor;
248 
249  usbDeviceStatus = 0;
250  suspectDeviceNumber = 0;
251 
252  while (1)
253  {
254  for (i = 0; i < bundleSize; i++)
255  {
256  usbDeviceStatus = 0;
257  suspectDeviceNumber = 0;
258 
259  for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
260  /* clear rollcall */
261  bundleTracker[i].deviceNumber[j].status = 0;
262 
263  dir = NULL;
264  dir = opendir(PCSCLITE_USB_PATH);
265  if (dir == NULL)
266  {
267  Log1(PCSC_LOG_ERROR,
268  "Cannot open USB path directory: " PCSCLITE_USB_PATH);
269  return;
270  }
271 
272  entry = NULL;
273  while ((entry = readdir(dir)) != 0)
274  {
275 
276  /*
277  * Skip anything starting with a
278  */
279  if (entry->d_name[0] == '.')
280  continue;
281  if (!strchr("0123456789",
282  entry->d_name[strlen(entry->d_name) - 1]))
283  {
284  continue;
285  }
286 
287  snprintf(dirpath, sizeof dirpath, "%s/%s",
288  PCSCLITE_USB_PATH, entry->d_name);
289 
290  dirB = opendir(dirpath);
291 
292  if (dirB == NULL)
293  {
294  Log2(PCSC_LOG_ERROR,
295  "USB path seems to have disappeared %s", dirpath);
296  closedir(dir);
297  return;
298  }
299 
300  while ((entryB = readdir(dirB)) != NULL)
301  {
302  /*
303  * Skip anything starting with a
304  */
305  if (entryB->d_name[0] == '.')
306  continue;
307 
308  /* Get the device number so we can distinguish
309  multiple readers */
310  snprintf(filename, sizeof filename, "%s/%s",
311  dirpath, entryB->d_name);
312  deviceNumber = atoi(entryB->d_name);
313 
314  fd = open(filename, O_RDONLY);
315  if (fd < 0)
316  continue;
317 
318  ret = read(fd, (void *) &usbDescriptor,
319  sizeof(usbDescriptor));
320 
321  close(fd);
322 
323  if (ret < 0)
324  continue;
325 
326  /*
327  * Device is found and we don't know about it
328  */
329 
330  if (usbDescriptor.idVendor == bundleTracker[i].manuID &&
331  usbDescriptor.idProduct == bundleTracker[i].productID &&
332  usbDescriptor.idVendor !=0 &&
333  usbDescriptor.idProduct != 0)
334  {
335  for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
336  {
337  if (bundleTracker[i].deviceNumber[j].id == deviceNumber &&
338  bundleTracker[i].deviceNumber[j].id != 0)
339  {
340  bundleTracker[i].deviceNumber[j].status = 1; /* i'm here */
341  break;
342  }
343  }
344 
345  if (j == PCSCLITE_MAX_READERS_CONTEXTS)
346  {
347  usbDeviceStatus = 1;
348  suspectDeviceNumber = deviceNumber;
349  }
350  }
351 
352  } /* End of while */
353 
354  closedir(dirB);
355 
356  } /* End of while */
357 
358 
359  if (usbDeviceStatus == 1)
360  {
361  pthread_mutex_lock(&usbNotifierMutex);
362 
363  for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
364  {
365  if (bundleTracker[i].deviceNumber[j].id == 0)
366  break;
367  }
368 
369  if (j == PCSCLITE_MAX_READERS_CONTEXTS)
370  Log1(PCSC_LOG_ERROR,
371  "Too many identical readers plugged in");
372  else
373  {
374  HPAddHotPluggable(i, j+1);
375  bundleTracker[i].deviceNumber[j].id = suspectDeviceNumber;
376  }
377 
378  pthread_mutex_unlock(&usbNotifierMutex);
379  }
380  else
381  if (usbDeviceStatus == 0)
382  {
383 
384  for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
385  {
386  if (bundleTracker[i].deviceNumber[j].id != 0 &&
387  bundleTracker[i].deviceNumber[j].status == 0)
388  {
389  pthread_mutex_lock(&usbNotifierMutex);
390  HPRemoveHotPluggable(i, j+1);
391  bundleTracker[i].deviceNumber[j].id = 0;
392  pthread_mutex_unlock(&usbNotifierMutex);
393  }
394  }
395  }
396  else
397  {
398  /*
399  * Do nothing - no USB devices found
400  */
401  }
402 
403  if (dir)
404  closedir(dir);
405 
406  } /* End of for..loop */
407 
408  SYS_Sleep(1);
409  if (AraKiriHotPlug)
410  {
411  int retval;
412 
413  Log1(PCSC_LOG_INFO, "Hotplug stopped");
414  pthread_exit(&retval);
415  }
416 
417  } /* End of while loop */
418 }
419 
420 LONG HPSearchHotPluggables(void)
421 {
422  int i, j;
423 
424  for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
425  {
426  bundleTracker[i].productID = 0;
427  bundleTracker[i].manuID = 0;
428 
429  for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
430  bundleTracker[i].deviceNumber[j].id = 0;
431  }
432 
433  HPReadBundleValues();
434 
435  ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
436  (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, 0);
437 
438  return 0;
439 }
440 
441 LONG HPStopHotPluggables(void)
442 {
443  AraKiriHotPlug = TRUE;
444 
445  return 0;
446 }
447 
448 static LONG HPAddHotPluggable(int i, unsigned long usbAddr)
449 {
450  /* NOTE: The deviceName is an empty string "" until someone implements
451  * the code to get it */
452  RFAddReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr,
453  bundleTracker[i].libraryPath, "");
454 
455  return 1;
456 } /* End of function */
457 
458 static LONG HPRemoveHotPluggable(int i, unsigned long usbAddr)
459 {
460  RFRemoveReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr);
461 
462  return 1;
463 } /* End of function */
464 
468 ULONG HPRegisterForHotplugEvents(void)
469 {
470  (void)pthread_mutex_init(&usbNotifierMutex, NULL);
471  return 0;
472 }
473 
474 void HPReCheckSerialReaders(void)
475 {
476 }
477 
478 #endif /* __linux__ && !HAVE_LIBUSB */
list object
Definition: simclist.h:181
This handles abstract system level calls.
int SYS_Sleep(int)
Makes the current process sleep for some seconds.
Definition: sys_unix.c:70
Reads lexical config files and updates database.
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader)
Definition: pcsclite.h:221
This defines some structures and #defines to be used over the transport layer.
This keeps a list of defines for pcsc-lite.
This keeps a list of defines for pcsc-lite.
This keeps track of a list of currently available reader structures.
This provides a search API for hot pluggble devices.
int bundleParse(const char *fileName, list_t *l)
Parse a Info.plist file and file a list.
Definition: tokenparser.c:1962
void bundleRelease(list_t *l)
Free the list created by bundleParse()
Definition: tokenparser.c:2020
This handles debugging.