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