hotplug_libhal.c

Go to the documentation of this file.
00001 /*
00002  * MUSCLE SmartCard Development ( http://www.linuxnet.com )
00003  *
00004  * Copyright (C) 2008-2010
00005  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
00006  *
00007  * $Id: hotplug_libhal.c 5071 2010-07-26 13:33:56Z rousseau $
00008  */
00009 
00015 #include "config.h"
00016 #if defined(HAVE_LIBHAL) && defined(USE_USB)
00017 
00018 #include <string.h>
00019 #include <stdio.h>
00020 #include <dirent.h>
00021 #include <stdlib.h>
00022 #include <libhal.h>
00023 #include <pthread.h>
00024 
00025 #include "misc.h"
00026 #include "wintypes.h"
00027 #include "pcscd.h"
00028 #include "debuglog.h"
00029 #include "parser.h"
00030 #include "readerfactory.h"
00031 #include "sys_generic.h"
00032 #include "hotplug.h"
00033 #include "utils.h"
00034 #include "strlcpycat.h"
00035 
00036 #undef DEBUG_HOTPLUG
00037 #define ADD_SERIAL_NUMBER
00038 #define ADD_INTERFACE_NAME
00039 
00040 #define FALSE           0
00041 #define TRUE            1
00042 
00043 #define UDI_BASE "/org/freedesktop/Hal/devices/"
00044 
00045 pthread_mutex_t usbNotifierMutex;
00046 
00047 static pthread_t usbNotifyThread;
00048 static int driverSize = -1;
00049 static char AraKiriHotPlug = FALSE;
00050 
00051 static DBusConnection *conn;
00052 static LibHalContext *hal_ctx;
00053 
00057 static struct _driverTracker
00058 {
00059     unsigned int manuID;
00060     unsigned int productID;
00061 
00062     char *bundleName;
00063     char *libraryPath;
00064     char *readerName;
00065     int ifdCapabilities;
00066     char *CFBundleName;
00067 } *driverTracker = NULL;
00068 #define DRIVER_TRACKER_SIZE_STEP 8
00069 
00073 static struct _readerTracker
00074 {
00075     char *udi;  
00076     char *fullName; 
00077 } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];
00078 
00079 static LONG HPReadBundleValues(void);
00080 static void HPAddDevice(LibHalContext *ctx, const char *udi);
00081 static void HPRemoveDevice(LibHalContext *ctx, const char *udi);
00082 static void HPEstablishUSBNotifications(void);
00083 
00089 #ifndef NO_LOG
00090 static const char *short_name(const char *udi)
00091 {
00092     return &udi[sizeof(UDI_BASE) - 1];
00093 } /* short_name */
00094 #endif
00095 
00096 
00097 static LONG HPReadBundleValues(void)
00098 {
00099     LONG rv;
00100     DIR *hpDir;
00101     struct dirent *currFP = NULL;
00102     char fullPath[FILENAME_MAX];
00103     char fullLibPath[FILENAME_MAX];
00104     char keyValue[TOKEN_MAX_VALUE_SIZE];
00105     int listCount = 0;
00106 
00107     hpDir = opendir(PCSCLITE_HP_DROPDIR);
00108 
00109     if (NULL == hpDir)
00110     {
00111         Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
00112         Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd.");
00113         return -1;
00114     }
00115 
00116     /* allocate a first array */
00117     driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker));
00118     if (NULL == driverTracker)
00119     {
00120         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00121         return -1;
00122     }
00123     driverSize = DRIVER_TRACKER_SIZE_STEP;
00124 
00125     while ((currFP = readdir(hpDir)) != 0)
00126     {
00127         if (strstr(currFP->d_name, ".bundle") != 0)
00128         {
00129             int alias = 0;
00130 
00131             /*
00132              * The bundle exists - let's form a full path name and get the
00133              * vendor and product ID's for this particular bundle
00134              */
00135             (void)snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist",
00136                 PCSCLITE_HP_DROPDIR, currFP->d_name);
00137             fullPath[sizeof(fullPath) - 1] = '\0';
00138 
00139             /* while we find a nth ifdVendorID in Info.plist */
00140             while (LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME,
00141                 keyValue, alias) == 0)
00142             {
00143                 driverTracker[listCount].bundleName = strdup(currFP->d_name);
00144 
00145                 /* Get ifdVendorID */
00146                 rv = LTPBundleFindValueWithKey(fullPath,
00147                     PCSCLITE_HP_MANUKEY_NAME, keyValue, alias);
00148                 if (0 == rv)
00149                     driverTracker[listCount].manuID = strtol(keyValue, NULL, 16);
00150 
00151                 /* get ifdProductID */
00152                 rv = LTPBundleFindValueWithKey(fullPath,
00153                     PCSCLITE_HP_PRODKEY_NAME, keyValue, alias);
00154                 if (0 == rv)
00155                     driverTracker[listCount].productID =
00156                         strtol(keyValue, NULL, 16);
00157 
00158                 /* get ifdFriendlyName */
00159                 rv = LTPBundleFindValueWithKey(fullPath,
00160                     PCSCLITE_HP_NAMEKEY_NAME, keyValue, alias);
00161                 if (0 == rv)
00162                     driverTracker[listCount].readerName = strdup(keyValue);
00163 
00164                 /* get CFBundleExecutable */
00165                 rv = LTPBundleFindValueWithKey(fullPath,
00166                     PCSCLITE_HP_LIBRKEY_NAME, keyValue, 0);
00167                 if (0 == rv)
00168                 {
00169                     (void)snprintf(fullLibPath, sizeof(fullLibPath),
00170                         "%s/%s/Contents/%s/%s",
00171                         PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
00172                         keyValue);
00173                     fullLibPath[sizeof(fullLibPath) - 1] = '\0';
00174                     driverTracker[listCount].libraryPath = strdup(fullLibPath);
00175                 }
00176 
00177                 /* Get ifdCapabilities */
00178                 rv = LTPBundleFindValueWithKey(fullPath,
00179                     PCSCLITE_HP_CPCTKEY_NAME, keyValue, 0);
00180                 if (0 == rv)
00181                     driverTracker[listCount].ifdCapabilities = strtol(keyValue,
00182                         NULL, 16);
00183 
00184                 /* Get CFBundleName */
00185                 rv = LTPBundleFindOptionalValueWithKey(fullPath,
00186                     PCSCLITE_HP_CFBUNDLE_NAME, keyValue, 0);
00187                 if (0 == rv)
00188                     driverTracker[listCount].CFBundleName = strdup(keyValue);
00189 
00190 #ifdef DEBUG_HOTPLUG
00191                 Log2(PCSC_LOG_INFO, "Found driver for: %s",
00192                     driverTracker[listCount].readerName);
00193 #endif
00194                 alias++;
00195 
00196                 if (NULL == driverTracker[listCount].readerName)
00197                     continue;
00198 
00199                 listCount++;
00200                 if (listCount >= driverSize)
00201                 {
00202                     int i;
00203 
00204                     /* increase the array size */
00205                     driverSize += DRIVER_TRACKER_SIZE_STEP;
00206 #ifdef DEBUG_HOTPLUG
00207                     Log2(PCSC_LOG_INFO,
00208                         "Increase driverTracker to %d entries", driverSize);
00209 #endif
00210                     driverTracker = realloc(driverTracker,
00211                         driverSize * sizeof(*driverTracker));
00212                     if (NULL == driverTracker)
00213                     {
00214                         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00215                         driverSize = -1;
00216                         return -1;
00217                     }
00218 
00219                     /* clean the newly allocated entries */
00220                     for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
00221                     {
00222                         driverTracker[i].manuID = 0;
00223                         driverTracker[i].productID = 0;
00224                         driverTracker[i].bundleName = NULL;
00225                         driverTracker[i].libraryPath = NULL;
00226                         driverTracker[i].readerName = NULL;
00227                         driverTracker[i].ifdCapabilities = 0;
00228                         driverTracker[i].CFBundleName = NULL;
00229                     }
00230                 }
00231             }
00232         }
00233     }
00234 
00235     driverSize = listCount;
00236     (void)closedir(hpDir);
00237 
00238 #ifdef DEBUG_HOTPLUG
00239     Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
00240 #endif
00241 
00242     return 0;
00243 } /* HPReadBundleValues */
00244 
00245 
00246 void HPEstablishUSBNotifications(void)
00247 {
00248     while (!AraKiriHotPlug && dbus_connection_read_write_dispatch(conn, -1))
00249     {
00250 #ifdef DEBUG_HOTPLUG
00251         Log0(PCSC_LOG_INFO);
00252 #endif
00253     }
00254 } /* HPEstablishUSBNotifications */
00255 
00256 
00257 /***
00258  * Start a thread waiting for hotplug events
00259  */
00260 LONG HPSearchHotPluggables(void)
00261 {
00262     int i;
00263 
00264     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00265     {
00266         readerTracker[i].udi = NULL;
00267         readerTracker[i].fullName = NULL;
00268     }
00269 
00270     return HPReadBundleValues();
00271 } /* HPSearchHotPluggables */
00272 
00273 
00277 LONG HPStopHotPluggables(void)
00278 {
00279     AraKiriHotPlug = TRUE;
00280 
00281     return 0;
00282 } /* HPStopHotPluggables */
00283 
00284 
00285 /*@null@*/ static struct _driverTracker *get_driver(LibHalContext *ctx,
00286     const char *udi)
00287 {
00288     DBusError error;
00289     int i;
00290     unsigned int idVendor, idProduct;
00291     static struct _driverTracker *classdriver, *driver;
00292 
00293     if (!libhal_device_property_exists(ctx, udi, "usb.vendor_id", NULL))
00294         return NULL;
00295 
00296     dbus_error_init(&error);
00297 
00298     /* Vendor ID */
00299     idVendor = libhal_device_get_property_int(ctx, udi,
00300         "usb.vendor_id", &error);
00301     if (dbus_error_is_set(&error))
00302     {
00303         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00304             error.name, error.message);
00305         dbus_error_free(&error);
00306         return NULL;
00307     }
00308 
00309     /* Product ID */
00310     idProduct = libhal_device_get_property_int(ctx, udi,
00311         "usb.product_id", &error);
00312     if (dbus_error_is_set(&error))
00313     {
00314         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00315             error.name, error.message);
00316         dbus_error_free(&error);
00317         return NULL;
00318     }
00319 
00320     Log3(PCSC_LOG_DEBUG, "Looking a driver for VID: 0x%04X, PID: 0x%04X", idVendor, idProduct);
00321 
00322     classdriver = NULL;
00323     driver = NULL;
00324     /* check if the device is supported by one driver */
00325     for (i=0; i<driverSize; i++)
00326     {
00327         if (driverTracker[i].libraryPath != NULL &&
00328             idVendor == driverTracker[i].manuID &&
00329             idProduct == driverTracker[i].productID)
00330         {
00331             if ((driverTracker[i].CFBundleName != NULL)
00332                 && (0 == strcmp(driverTracker[i].CFBundleName, "CCIDCLASSDRIVER")))
00333                 classdriver = &driverTracker[i];
00334             else
00335                 /* it is not a CCID Class driver */
00336                 driver = &driverTracker[i];
00337         }
00338     }
00339 
00340     /* if we found a specific driver */
00341     if (driver)
00342         return driver;
00343 
00344     /* else return the Class driver */
00345     return classdriver;
00346 }
00347 
00348 
00349 static void HPAddDevice(LibHalContext *ctx, const char *udi)
00350 {
00351     int i;
00352     char deviceName[MAX_DEVICENAME];
00353     struct _driverTracker *driver;
00354     char *sSerialNumber = NULL, *sInterfaceName = NULL;
00355     char fullname[MAX_READERNAME];
00356     LONG ret;
00357 
00358     driver = get_driver(ctx, udi);
00359     if (NULL == driver)
00360     {
00361         /* not a smart card reader */
00362 #ifdef DEBUG_HOTPLUG
00363         Log2(PCSC_LOG_DEBUG, "%s is not a reader", short_name(udi));
00364 #endif
00365         return;
00366     }
00367 
00368     Log2(PCSC_LOG_INFO, "Adding USB device: %s", short_name(udi));
00369 
00370     (void)snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libhal:%s",
00371         driver->manuID, driver->productID, udi);
00372     deviceName[sizeof(deviceName) -1] = '\0';
00373 
00374     /* wait until the device is visible by libusb/etc.  */
00375     (void)SYS_Sleep(1);
00376 
00377     (void)pthread_mutex_lock(&usbNotifierMutex);
00378 
00379     /* find a free entry */
00380     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00381     {
00382         if (NULL == readerTracker[i].fullName)
00383             break;
00384     }
00385 
00386     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00387     {
00388         Log2(PCSC_LOG_ERROR,
00389             "Not enough reader entries. Already found %d readers", i);
00390         (void)pthread_mutex_unlock(&usbNotifierMutex);
00391         return;
00392     }
00393 
00394     readerTracker[i].udi = strdup(udi);
00395 
00396 #ifdef ADD_INTERFACE_NAME
00397     if (libhal_device_property_exists(ctx, udi, "usb.interface.description", NULL))
00398         sInterfaceName = libhal_device_get_property_string(ctx, udi,
00399             "usb.interface.description", NULL);
00400 #endif
00401 
00402 #ifdef ADD_SERIAL_NUMBER
00403     if (libhal_device_property_exists(ctx, udi, "usb.serial", NULL))
00404         sSerialNumber = libhal_device_get_property_string(ctx, udi,
00405             "usb.serial", NULL);
00406 #endif
00407     
00408     /* name from the Info.plist file */
00409     strlcpy(fullname, driver->readerName, sizeof(fullname));
00410 
00411     /* interface name from the device (if any) */
00412     if (sInterfaceName)
00413     {
00414         strlcat(fullname, " [", sizeof(fullname));
00415         strlcat(fullname, sInterfaceName, sizeof(fullname));
00416         strlcat(fullname, "]", sizeof(fullname));
00417         libhal_free_string(sInterfaceName);
00418     }
00419 
00420     /* serial number from the device (if any) */
00421     if (sSerialNumber)
00422     {
00423         strlcat(fullname, " (", sizeof(fullname));
00424         strlcat(fullname, sSerialNumber, sizeof(fullname));
00425         strlcat(fullname, ")", sizeof(fullname));
00426         libhal_free_string(sSerialNumber);
00427     }
00428 
00429     readerTracker[i].fullName = strdup(fullname);
00430 
00431     ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00432         driver->libraryPath, deviceName);
00433     if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret))
00434     {
00435         char *parent, *device_file;
00436 
00437         /* get the parent descriptor, without the '_if0' */
00438         parent = libhal_device_get_property_string(ctx, udi,
00439             "info.parent", NULL);
00440         if (! parent)
00441             goto error;
00442 
00443         /* get the linux device file: i.e. '/dev/bus/usb/002/012' */
00444         device_file = libhal_device_get_property_string(ctx, parent,
00445             "linux.device_file", NULL);
00446         if (! device_file)
00447             goto error;
00448 
00449         /* check the format looks correct */
00450 #define LIBUSB_HEADER "/dev/bus/usb/"
00451         if (strncmp(device_file, LIBUSB_HEADER, strlen(LIBUSB_HEADER)))
00452             goto error;
00453 
00454         device_file += strlen(LIBUSB_HEADER);
00455 
00456         (void)snprintf(deviceName, sizeof(deviceName),
00457             "usb:%04x/%04x:libusb:%s",
00458             driver->manuID, driver->productID, device_file);
00459         deviceName[sizeof(deviceName) -1] = '\0';
00460 
00461         /* replace the libusb separator '/' by ':' */
00462         if ('/' == deviceName[strlen(deviceName)-3-1])
00463             deviceName[strlen(deviceName)-3-1] = ':';
00464 
00465         Log2(PCSC_LOG_INFO, "trying libusb scheme with: %s", deviceName);
00466         ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00467             driver->libraryPath, deviceName);
00468 
00469         if (SCARD_S_SUCCESS != ret)
00470         {
00471 error:
00472             Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", short_name(udi));
00473             free(readerTracker[i].fullName);
00474             readerTracker[i].fullName = NULL;
00475             free(readerTracker[i].udi);
00476             readerTracker[i].udi = NULL;
00477 
00478             (void)CheckForOpenCT();
00479         }
00480     }
00481 
00482     (void)pthread_mutex_unlock(&usbNotifierMutex);
00483 } /* HPAddDevice */
00484 
00485 
00486 static void HPRemoveDevice(/*@unused@*/ LibHalContext *ctx, const char *udi)
00487 {
00488     int i;
00489 
00490     (void)ctx;
00491     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00492     {
00493         if (readerTracker[i].udi && strcmp(readerTracker[i].udi, udi) == 0)
00494             break;
00495     }
00496     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00497     {
00498 #ifdef DEBUG_HOTPLUG
00499         Log2(PCSC_LOG_DEBUG, "USB device %s not already used", short_name(udi));
00500 #endif
00501         return;
00502     }
00503     Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", i,
00504         short_name(readerTracker[i].udi));
00505 
00506     (void)pthread_mutex_lock(&usbNotifierMutex);
00507 
00508     (void)RFRemoveReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i);
00509     free(readerTracker[i].fullName);
00510     readerTracker[i].fullName = NULL;
00511     free(readerTracker[i].udi);
00512     readerTracker[i].udi = NULL;
00513 
00514     (void)pthread_mutex_unlock(&usbNotifierMutex);
00515 
00516     return;
00517 } /* HPRemoveDevice */
00518 
00519 
00523 ULONG HPRegisterForHotplugEvents(void)
00524 {
00525     char **device_names;
00526     int i, num_devices;
00527     DBusError error;
00528 
00529     (void)pthread_mutex_init(&usbNotifierMutex, NULL);
00530 
00531     if (driverSize <= 0)
00532     {
00533         Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
00534         Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
00535         return 1;
00536     }
00537 
00538     dbus_error_init(&error);
00539     conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
00540     if (conn == NULL)
00541     {
00542         Log3(PCSC_LOG_ERROR, "error: dbus_bus_get: %s: %s",
00543             error.name, error.message);
00544         if (dbus_error_is_set(&error))
00545             dbus_error_free(&error);
00546         return 1;
00547     }
00548 
00549     hal_ctx = libhal_ctx_new();
00550     if (hal_ctx == NULL)
00551     {
00552         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_new");
00553         return 1;
00554     }
00555     if (!libhal_ctx_set_dbus_connection(hal_ctx, conn))
00556     {
00557         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_set_dbus_connection");
00558         return 1;
00559     }
00560     if (!libhal_ctx_init(hal_ctx, &error))
00561     {
00562         if (dbus_error_is_set(&error))
00563         {
00564             Log3(PCSC_LOG_ERROR, "error: libhal_ctx_init: %s: %s",
00565                 error.name, error.message);
00566             if (dbus_error_is_set(&error))
00567                 dbus_error_free(&error);
00568         }
00569         Log1(PCSC_LOG_ERROR, "Could not initialise connection to hald.");
00570         Log1(PCSC_LOG_ERROR, "Normally this means the HAL daemon (hald) is not running or not ready.");
00571         return 1;
00572     }
00573 
00574     /* callback when device added */
00575     (void)libhal_ctx_set_device_added(hal_ctx, HPAddDevice);
00576 
00577     /* callback when device removed */
00578     (void)libhal_ctx_set_device_removed(hal_ctx, HPRemoveDevice);
00579 
00580     device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error);
00581     if (device_names == NULL)
00582     {
00583         if (dbus_error_is_set(&error))
00584             dbus_error_free(&error);
00585         Log1(PCSC_LOG_ERROR, "Couldn't obtain list of devices");
00586         return 1;
00587     }
00588 
00589     /* try to add every present USB devices */
00590     for (i = 0; i < num_devices; i++)
00591         HPAddDevice(hal_ctx, device_names[i]);
00592 
00593     libhal_free_string_array(device_names);
00594 
00595     (void)ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
00596         (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, NULL);
00597 
00598     return 0;
00599 } /* HPRegisterForHotplugEvents */
00600 
00601 
00602 void HPReCheckSerialReaders(void)
00603 {
00604     /* nothing to do here */
00605 #ifdef DEBUG_HOTPLUG
00606     Log0(PCSC_LOG_ERROR);
00607 #endif
00608 } /* HPReCheckSerialReaders */
00609 
00610 #endif
00611