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