/* * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ ) * * Copyright (C) 2011 * Ludovic Rousseau * Copyright (C) 2014 * Stefani Seibold * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @file * @brief This provides a search API for hot plugable devices using libudev */ #include "config.h" #if defined(HAVE_LIBUDEV) && defined(USE_USB) #define _GNU_SOURCE /* for asprintf(3) */ #include #include #include #include #include #include #include #include #include "debuglog.h" #include "parser.h" #include "readerfactory.h" #include "sys_generic.h" #include "hotplug.h" #include "utils.h" #ifndef TEMP_FAILURE_RETRY #define TEMP_FAILURE_RETRY(expression) \ (__extension__ \ ({ long int __result; \ do __result = (long int) (expression); \ while (__result == -1L && errno == EINTR); \ __result; })) #endif #undef DEBUG_HOTPLUG #define FALSE 0 #define TRUE 1 extern char Add_Interface_In_Name; extern char Add_Serial_In_Name; static pthread_t usbNotifyThread; static int driverSize = -1; static struct udev *Udev; /** * keep track of drivers in a dynamically allocated array */ static struct _driverTracker { unsigned int manuID; unsigned int productID; char *bundleName; char *libraryPath; char *readerName; char *CFBundleName; } *driverTracker = NULL; #define DRIVER_TRACKER_SIZE_STEP 10 /* The CCID driver already supports 176 readers. * We start with a big array size to avoid reallocation. */ #define DRIVER_TRACKER_INITIAL_SIZE 200 /** * keep track of PCSCLITE_MAX_READERS_CONTEXTS simultaneous readers */ static struct _readerTracker { char *devpath; /**< device name seen by udev */ char *fullName; /**< full reader name (including serial number) */ char *sysname; /**< sysfs path */ } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS]; static LONG HPReadBundleValues(void) { LONG rv; DIR *hpDir; struct dirent *currFP = NULL; char fullPath[FILENAME_MAX]; char fullLibPath[FILENAME_MAX]; int listCount = 0; hpDir = opendir(PCSCLITE_HP_DROPDIR); if (NULL == hpDir) { Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR); Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd."); return -1; } /* allocate a first array */ driverSize = DRIVER_TRACKER_INITIAL_SIZE; driverTracker = calloc(driverSize, sizeof(*driverTracker)); if (NULL == driverTracker) { Log1(PCSC_LOG_CRITICAL, "Not enough memory"); (void)closedir(hpDir); return -1; } #define GET_KEY(key, values) \ rv = LTPBundleFindValueWithKey(&plist, key, values); \ if (rv) \ { \ Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \ fullPath); \ continue; \ } while ((currFP = readdir(hpDir)) != 0) { if (strstr(currFP->d_name, ".bundle") != 0) { unsigned int alias; list_t plist, *values; list_t *manuIDs, *productIDs, *readerNames; char *CFBundleName; char *libraryPath; /* * The bundle exists - let's form a full path name and get the * vendor and product ID's for this particular bundle */ (void)snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist", PCSCLITE_HP_DROPDIR, currFP->d_name); fullPath[sizeof(fullPath) - 1] = '\0'; rv = bundleParse(fullPath, &plist); if (rv) continue; /* get CFBundleExecutable */ GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values) libraryPath = list_get_at(values, 0); (void)snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s/Contents/%s/%s", PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH, libraryPath); fullLibPath[sizeof(fullLibPath) - 1] = '\0'; GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs) GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs) GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames) if ((list_size(manuIDs) != list_size(productIDs)) || (list_size(manuIDs) != list_size(readerNames))) { Log2(PCSC_LOG_CRITICAL, "Error parsing %s", fullPath); (void)closedir(hpDir); return -1; } /* Get CFBundleName */ rv = LTPBundleFindValueWithKey(&plist, PCSCLITE_HP_CFBUNDLE_NAME, &values); if (rv) CFBundleName = NULL; else CFBundleName = strdup(list_get_at(values, 0)); /* while we find a nth ifdVendorID in Info.plist */ for (alias=0; aliasd_name); driverTracker[listCount].libraryPath = strdup(fullLibPath); driverTracker[listCount].CFBundleName = CFBundleName; #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Found driver for: %s", driverTracker[listCount].readerName); #endif listCount++; if (listCount >= driverSize) { int i; /* increase the array size */ driverSize += DRIVER_TRACKER_SIZE_STEP; #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Increase driverTracker to %d entries", driverSize); #endif void* tmp = realloc(driverTracker, driverSize * sizeof(*driverTracker)); if (NULL == tmp) { free(driverTracker); Log1(PCSC_LOG_CRITICAL, "Not enough memory"); driverSize = -1; (void)closedir(hpDir); return -1; } driverTracker = tmp; /* clean the newly allocated entries */ for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; ireaderName); sInterfaceNumber = udev_device_get_sysattr_value(dev, "bInterfaceNumber"); if (sInterfaceNumber) bInterfaceNumber = atoi(sInterfaceNumber); else bInterfaceNumber = 0; a = asprintf(&deviceName, "usb:%04x/%04x:libudev:%d:%s", driver->manuID, driver->productID, bInterfaceNumber, devpath); if (-1 == a) { Log1(PCSC_LOG_ERROR, "asprintf() failed"); return; } /* find a free entry */ for (index=0; indexreaderName); /* interface name from the device (if any) */ if (sInterfaceName) { char *result; char *tmpInterfaceName = strdup(sInterfaceName); /* check the interface name contains only valid ASCII codes */ for (size_t i=0; ilibraryPath, deviceName); if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret)) { Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", driver->readerName); if (classdriver && driver != classdriver) { /* the reader can also be used by the a class driver */ ret = RFAddReader(fullname, PCSCLITE_HP_BASE_PORT + index, classdriver->libraryPath, deviceName); if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret)) { Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", driver->readerName); (void)CheckForOpenCT(); } } else { (void)CheckForOpenCT(); } } if (SCARD_S_SUCCESS != ret) { /* adding the reader failed */ free(readerTracker[index].devpath); readerTracker[index].devpath = NULL; free(readerTracker[index].fullName); readerTracker[index].fullName = NULL; free(readerTracker[index].sysname); readerTracker[index].sysname = NULL; } exit: free(fullname); free(deviceName); } /* HPAddDevice */ static void HPScanUSB(struct udev *udev) { struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; /* Create a list of the devices in the 'usb' subsystem. */ enumerate = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); /* For each item enumerated */ udev_list_entry_foreach(dev_list_entry, devices) { struct udev_device *dev; const char *devpath; /* Get the filename of the /sys entry for the device and create a udev_device object (dev) representing it */ devpath = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, devpath); #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_DEBUG, "Found matching USB device: %s", devpath); #endif HPAddDevice(dev); /* free device */ udev_device_unref(dev); } /* Free the enumerator object */ udev_enumerate_unref(enumerate); } static void * HPEstablishUSBNotifications(void *arg) { struct udev_monitor *udev_monitor = arg; int r; int fd; struct pollfd pfd; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); /* udev monitor file descriptor */ fd = udev_monitor_get_fd(udev_monitor); if (fd < 0) { Log2(PCSC_LOG_ERROR, "udev_monitor_get_fd() error: %d", fd); pthread_exit(NULL); } pfd.fd = fd; pfd.events = POLLIN; for (;;) { struct udev_device *dev; #ifdef DEBUG_HOTPLUG Log0(PCSC_LOG_INFO); #endif pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); /* wait for a udev event */ r = TEMP_FAILURE_RETRY(poll(&pfd, 1, -1)); if (r < 0) { Log2(PCSC_LOG_ERROR, "select(): %s", strerror(errno)); pthread_exit(NULL); } pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); dev = udev_monitor_receive_device(udev_monitor); if (dev) { const char *action = udev_device_get_action(dev); if (action) { if (!strcmp("remove", action)) { Log1(PCSC_LOG_INFO, "USB Device removed"); HPRemoveDevice(dev); } else if (!strcmp("add", action)) { Log1(PCSC_LOG_INFO, "USB Device add"); HPAddDevice(dev); } } /* free device */ udev_device_unref(dev); } } pthread_exit(NULL); } /* HPEstablishUSBNotifications */ /*** * Start a thread waiting for hotplug events */ LONG HPSearchHotPluggables(void) { int i; for (i=0; i