pcscdaemon.c

Go to the documentation of this file.
00001 /*
00002  * MUSCLE SmartCard Development ( http://www.linuxnet.com )
00003  *
00004  * Copyright (C) 1999-2005
00005  *  David Corcoran <corcoran@linuxnet.com>
00006  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
00007  *
00008  * $Id: pcscdaemon.c 2335 2007-01-11 18:05:12Z rousseau $
00009  */
00010 
00020 #include "config.h"
00021 #include <time.h>
00022 #include <syslog.h>
00023 #include <signal.h>
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <errno.h>
00027 #include <stdio.h>
00028 #include <unistd.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #ifdef HAVE_GETOPT_H
00032 #include <getopt.h>
00033 #endif
00034 
00035 #include "misc.h"
00036 #include "pcsclite.h"
00037 #include "debuglog.h"
00038 #include "winscard_msg.h"
00039 #include "winscard_svc.h"
00040 #include "sys_generic.h"
00041 #include "thread_generic.h"
00042 #include "hotplug.h"
00043 #include "readerfactory.h"
00044 #include "configfile.h"
00045 #include "powermgt_generic.h"
00046 
00047 #ifndef TRUE
00048 #define TRUE 1
00049 #define FALSE 0
00050 #endif
00051 
00052 char AraKiri = FALSE;
00053 static char Init = TRUE;
00054 int HPForceReaderPolling = 0;
00055 
00056 /*
00057  * Some internal functions
00058  */
00059 void SVCServiceRunLoop(void);
00060 void SVCClientCleanup(psharedSegmentMsg);
00061 void at_exit(void);
00062 void clean_temp_files(void);
00063 void signal_reload(int sig);
00064 void signal_trap(int);
00065 void print_version (void);
00066 void print_usage (char const * const);
00067 
00068 PCSCLITE_MUTEX usbNotifierMutex;
00069 
00070 #ifdef USE_RUN_PID
00071 pid_t GetDaemonPid(void);
00072 pid_t GetDaemonPid(void)
00073 {
00074     FILE *f;
00075     pid_t pid;
00076 
00077     /* pids are only 15 bits but 4294967296
00078      * (32 bits in case of a new system use it) is on 10 bytes
00079      */
00080     if ((f = fopen(USE_RUN_PID, "rb")) != NULL)
00081     {
00082 #define PID_ASCII_SIZE 11
00083         char pid_ascii[PID_ASCII_SIZE];
00084 
00085         fgets(pid_ascii, PID_ASCII_SIZE, f);
00086         fclose(f);
00087 
00088         pid = atoi(pid_ascii);
00089     }
00090     else
00091     {
00092         Log2(PCSC_LOG_CRITICAL, "Can't open " USE_RUN_PID ": %s",
00093             strerror(errno));
00094         return -1;
00095     }
00096 
00097     return pid;
00098 } /* GetDaemonPid */
00099 #endif
00100 
00101 int SendHotplugSignal(void)
00102 {
00103 #ifdef USE_RUN_PID
00104     pid_t pid;
00105 
00106     pid = GetDaemonPid();
00107 
00108     if (pid != -1)
00109     {
00110         Log2(PCSC_LOG_INFO, "Send hotplug signal to pcscd (pid=%d)", pid);
00111         if (kill(pid, SIGUSR1) < 0)
00112         {
00113             Log3(PCSC_LOG_CRITICAL, "Can't signal pcscd (pid=%d): %s",
00114                 pid, strerror(errno));
00115             return EXIT_FAILURE ;
00116         }
00117     }
00118 #endif
00119 
00120     return EXIT_SUCCESS;
00121 } /* SendHotplugSignal */
00122 
00123 /*
00124  * Cleans up messages still on the queue when a client dies
00125  */
00126 void SVCClientCleanup(psharedSegmentMsg msgStruct)
00127 {
00128     /*
00129      * May be implemented in future releases
00130      */
00131 }
00132 
00141 void SVCServiceRunLoop(void)
00142 {
00143     int rsp;
00144     LONG rv;
00145     DWORD dwClientID;   /* Connection ID used to reference the Client */
00146 
00147     rsp = 0;
00148     rv = 0;
00149 
00150     /*
00151      * Initialize the comm structure
00152      */
00153     rsp = SHMInitializeCommonSegment();
00154 
00155     if (rsp == -1)
00156     {
00157         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00158         exit(-1);
00159     }
00160 
00161     /*
00162      * Initialize the contexts structure
00163      */
00164     rv = ContextsInitialize();
00165 
00166     if (rv == -1)
00167     {
00168         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00169         exit(-1);
00170     }
00171 
00172     /*
00173      * Solaris sends a SIGALRM and it is annoying
00174      */
00175 
00176     signal(SIGALRM, SIG_IGN);
00177     signal(SIGPIPE, SIG_IGN);
00178     signal(SIGHUP, SIG_IGN);    /* needed for Solaris. The signal is sent
00179                  * when the shell is existed */
00180 
00181     /*
00182      * This function always returns zero
00183      */
00184     rsp = SYS_MutexInit(&usbNotifierMutex);
00185 
00186     /*
00187      * Set up the search for USB/PCMCIA devices
00188      */
00189     HPSearchHotPluggables();
00190     HPRegisterForHotplugEvents();
00191 
00192     /*
00193      * Set up the power management callback routine
00194      */
00195     PMRegisterForPowerEvents();
00196 
00197     while (TRUE)
00198     {
00199         switch (rsp = SHMProcessEventsServer(&dwClientID, 0))
00200         {
00201 
00202         case 0:
00203             Log2(PCSC_LOG_DEBUG, "A new context thread creation is requested: %d", dwClientID);
00204             rv = CreateContextThread(&dwClientID);
00205 
00206             if (rv != SCARD_S_SUCCESS)
00207             {
00208                 Log1(PCSC_LOG_ERROR, "Problem during the context thread creation");
00209                 AraKiri = TRUE;
00210             }
00211 
00212             break;
00213 
00214         case -1:
00215             Log1(PCSC_LOG_ERROR, "Error in SHMProcessEventsServer");
00216             break;
00217 
00218         case -2:
00219             /* Nothing to do in case of a syscall interrupted
00220              * It happens when SIGUSR1 (reload) or SIGINT (Ctrl-C) is received
00221              * We just try again */
00222             break;
00223 
00224         default:
00225             Log2(PCSC_LOG_ERROR, "SHMProcessEventsServer unknown retval: %d",
00226                 rsp);
00227             break;
00228         }
00229 
00230         if (AraKiri)
00231         {
00232             /* stop the hotpug thread and waits its exit */
00233             HPStopHotPluggables();
00234             SYS_Sleep(1);
00235 
00236             /* now stop all the drivers */
00237             RFCleanupReaders(1);
00238         }
00239     }
00240 }
00241 
00242 int main(int argc, char **argv)
00243 {
00244     int rv;
00245     char setToForeground;
00246     char HotPlug;
00247     char *newReaderConfig;
00248     struct stat fStatBuf;
00249     int opt;
00250 #ifdef HAVE_GETOPT_LONG
00251     int option_index = 0;
00252     static struct option long_options[] = {
00253         {"config", 1, 0, 'c'},
00254         {"foreground", 0, 0, 'f'},
00255         {"help", 0, 0, 'h'},
00256         {"version", 0, 0, 'v'},
00257         {"apdu", 0, 0, 'a'},
00258         {"debug", 0, 0, 'd'},
00259         {"info", 0, 0, 0},
00260         {"error", 0, 0, 'e'},
00261         {"critical", 0, 0, 'C'},
00262         {"hotplug", 0, 0, 'H'},
00263         {"force-reader-polling", optional_argument, 0, 0},
00264         {0, 0, 0, 0}
00265     };
00266 #endif
00267 #define OPT_STRING "c:fdhvaeCH"
00268 
00269     rv = 0;
00270     newReaderConfig = NULL;
00271     setToForeground = FALSE;
00272     HotPlug = FALSE;
00273 
00274     /*
00275      * test the version
00276      */
00277     if (strcmp(PCSCLITE_VERSION_NUMBER, VERSION) != 0)
00278     {
00279         printf("BUILD ERROR: The release version number PCSCLITE_VERSION_NUMBER\n");
00280         printf("  in pcsclite.h (%s) does not match the release version number\n",
00281             PCSCLITE_VERSION_NUMBER);
00282         printf("  generated in config.h (%s) (see configure.in).\n", VERSION);
00283 
00284         return EXIT_FAILURE;
00285     }
00286 
00287     /*
00288      * By default we create a daemon (not connected to any output)
00289      * so log to syslog to have error messages.
00290      */
00291     DebugLogSetLogType(DEBUGLOG_SYSLOG_DEBUG);
00292 
00293     /*
00294      * Handle any command line arguments
00295      */
00296 #ifdef  HAVE_GETOPT_LONG
00297     while ((opt = getopt_long (argc, argv, OPT_STRING, long_options, &option_index)) != -1) {
00298 #else
00299     while ((opt = getopt (argc, argv, OPT_STRING)) != -1) {
00300 #endif
00301         switch (opt) {
00302 #ifdef  HAVE_GETOPT_LONG
00303             case 0:
00304                 if (strcmp(long_options[option_index].name,
00305                     "force-reader-polling") == 0)
00306                     HPForceReaderPolling = optarg ? abs(atoi(optarg)) : 1;
00307                 break;
00308 #endif
00309             case 'c':
00310                 Log2(PCSC_LOG_INFO, "using new config file: %s", optarg);
00311                 newReaderConfig = optarg;
00312                 break;
00313 
00314             case 'f':
00315                 setToForeground = TRUE;
00316                 /* debug to stderr instead of default syslog */
00317                 DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG);
00318                 Log1(PCSC_LOG_INFO,
00319                     "pcscd set to foreground with debug send to stderr");
00320                 break;
00321 
00322             case 'd':
00323                 DebugLogSetLevel(PCSC_LOG_DEBUG);
00324                 break;
00325 
00326             case 'e':
00327                 DebugLogSetLevel(PCSC_LOG_ERROR);
00328                 break;
00329 
00330             case 'C':
00331                 DebugLogSetLevel(PCSC_LOG_CRITICAL);
00332                 break;
00333 
00334             case 'h':
00335                 print_usage (argv[0]);
00336                 return EXIT_SUCCESS;
00337 
00338             case 'v':
00339                 print_version ();
00340                 return EXIT_SUCCESS;
00341 
00342             case 'a':
00343                 DebugLogSetCategory(DEBUG_CATEGORY_APDU);
00344                 break;
00345 
00346             case 'H':
00347                 /* debug to stderr instead of default syslog */
00348                 DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG);
00349                 HotPlug = TRUE;
00350                 break;
00351 
00352             default:
00353                 print_usage (argv[0]);
00354                 return EXIT_FAILURE;
00355         }
00356 
00357     }
00358 
00359     if (argv[optind])
00360     {
00361         printf("Unknown option: %s\n\n", argv[optind]);
00362         print_usage(argv[0]);
00363         return EXIT_SUCCESS;
00364     }
00365 
00366     /*
00367      * test the presence of /var/run/pcsc.pub
00368      */
00369 
00370     rv = SYS_Stat(PCSCLITE_PUBSHM_FILE, &fStatBuf);
00371 
00372     if (rv == 0)
00373     {
00374 #ifdef USE_RUN_PID
00375         pid_t pid;
00376 
00377         /* read the pid file to get the old pid and test if the old pcscd is
00378          * still running
00379          */
00380         pid = GetDaemonPid();
00381 
00382         if (pid != -1)
00383         {
00384             if (HotPlug)
00385                 return SendHotplugSignal();
00386 
00387             if (kill(pid, 0) == 0)
00388             {
00389                 Log1(PCSC_LOG_CRITICAL,
00390                     "file " PCSCLITE_PUBSHM_FILE " already exists.");
00391                 Log2(PCSC_LOG_CRITICAL,
00392                     "Another pcscd (pid: %d) seems to be running.", pid);
00393                 return EXIT_FAILURE;
00394             }
00395             else
00396                 /* the old pcscd is dead. make some cleanup */
00397                 clean_temp_files();
00398         }
00399         else
00400 #endif
00401         {
00402             if (HotPlug)
00403             {
00404 #ifdef USE_RUN_PID
00405                 Log1(PCSC_LOG_CRITICAL, "file " USE_RUN_PID " do not exist");
00406 #else
00407                 Log1(PCSC_LOG_CRITICAL,
00408                     "pcscd was not configured with --enable-runpid=FILE");
00409 #endif
00410                 Log1(PCSC_LOG_CRITICAL, "Hotplug failed");
00411                 return EXIT_FAILURE;
00412             }
00413 
00414             Log1(PCSC_LOG_CRITICAL,
00415                 "file " PCSCLITE_PUBSHM_FILE " already exists.");
00416             Log1(PCSC_LOG_CRITICAL,
00417                 "Maybe another pcscd is running?");
00418 #ifdef USE_RUN_PID
00419             Log1(PCSC_LOG_CRITICAL,
00420                 "I can't read process pid from " USE_RUN_PID);
00421 #endif
00422             Log1(PCSC_LOG_CRITICAL,
00423                 "Remove " PCSCLITE_PUBSHM_FILE " and " PCSCLITE_CSOCK_NAME);
00424             Log1(PCSC_LOG_CRITICAL,
00425                 "if pcscd is not running to clear this message.");
00426             return EXIT_FAILURE;
00427         }
00428     }
00429     else
00430         if (HotPlug)
00431         {
00432             Log1(PCSC_LOG_CRITICAL, "Hotplug failed: pcscd is not running");
00433             return EXIT_FAILURE;
00434         }
00435 
00436     /*
00437      * If this is set to one the user has asked it not to fork
00438      */
00439     if (!setToForeground)
00440     {
00441         if (SYS_Daemon(0, 0))
00442             Log2(PCSC_LOG_CRITICAL, "SYS_Daemon() failed: %s",
00443                 strerror(errno));
00444     }
00445 
00446     /*
00447      * cleanly remove /tmp/pcsc when exiting
00448      */
00449     signal(SIGQUIT, signal_trap);
00450     signal(SIGTERM, signal_trap);
00451     signal(SIGINT, signal_trap);
00452     signal(SIGHUP, signal_trap);
00453 
00454 #ifdef USE_RUN_PID
00455     /*
00456      * Record our pid to make it easier
00457      * to kill the correct pcscd
00458      */
00459     {
00460         FILE *f;
00461 
00462         if ((f = fopen(USE_RUN_PID, "wb")) != NULL)
00463         {
00464             fprintf(f, "%u\n", (unsigned) getpid());
00465             fclose(f);
00466         }
00467     }
00468 #endif
00469 
00470     /*
00471      * If PCSCLITE_IPC_DIR does not exist then create it
00472      */
00473     rv = SYS_Stat(PCSCLITE_IPC_DIR, &fStatBuf);
00474     if (rv < 0)
00475     {
00476         rv = SYS_Mkdir(PCSCLITE_IPC_DIR, S_ISVTX | S_IRWXO | S_IRWXG | S_IRWXU);
00477         if (rv != 0)
00478         {
00479             Log2(PCSC_LOG_CRITICAL,
00480                 "cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno));
00481             return EXIT_FAILURE;
00482         }
00483     }
00484 
00485     /* cleanly remove /var/run/pcsc.* files when exiting */
00486     if (atexit(at_exit))
00487         Log2(PCSC_LOG_CRITICAL, "atexit() failed: %s", strerror(errno));
00488 
00489     /*
00490      * Allocate memory for reader structures
00491      */
00492     RFAllocateReaderSpace();
00493 
00494     /*
00495      * Grab the information from the reader.conf
00496      */
00497     if (newReaderConfig)
00498     {
00499         rv = RFStartSerialReaders(newReaderConfig);
00500         if (rv != 0)
00501         {
00502             Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig,
00503                 strerror(errno));
00504             at_exit();
00505         }
00506     }
00507     else
00508     {
00509         rv = RFStartSerialReaders(PCSCLITE_READER_CONFIG);
00510 
00511 #if 0
00512         if (rv == 1)
00513         {
00514             Log1(PCSC_LOG_INFO,
00515                 "warning: no " PCSCLITE_READER_CONFIG " found");
00516             /*
00517              * Token error in file
00518              */
00519         }
00520         else
00521 #endif
00522             if (rv == -1)
00523                 at_exit();
00524     }
00525 
00526     /*
00527      * Set the default globals
00528      */
00529     g_rgSCardT0Pci.dwProtocol = SCARD_PROTOCOL_T0;
00530     g_rgSCardT1Pci.dwProtocol = SCARD_PROTOCOL_T1;
00531     g_rgSCardRawPci.dwProtocol = SCARD_PROTOCOL_RAW;
00532 
00533     Log1(PCSC_LOG_INFO, "pcsc-lite " VERSION " daemon ready.");
00534 
00535     /*
00536      * post initialistion
00537      */
00538     Init = FALSE;
00539 
00540     /*
00541      * signal_trap() does just set a global variable used by the main loop
00542      */
00543     signal(SIGQUIT, signal_trap);
00544     signal(SIGTERM, signal_trap);
00545     signal(SIGINT, signal_trap);
00546     signal(SIGHUP, signal_trap);
00547 
00548     signal(SIGUSR1, signal_reload);
00549 
00550     SVCServiceRunLoop();
00551 
00552     Log1(PCSC_LOG_ERROR, "SVCServiceRunLoop returned");
00553     return EXIT_FAILURE;
00554 }
00555 
00556 void at_exit(void)
00557 {
00558     Log1(PCSC_LOG_INFO, "cleaning " PCSCLITE_IPC_DIR);
00559 
00560     clean_temp_files();
00561 
00562     SYS_Exit(EXIT_SUCCESS);
00563 }
00564 
00565 void clean_temp_files(void)
00566 {
00567     int rv;
00568 
00569     rv = SYS_Unlink(PCSCLITE_PUBSHM_FILE);
00570     if (rv != 0)
00571         Log2(PCSC_LOG_ERROR, "Cannot unlink " PCSCLITE_PUBSHM_FILE ": %s",
00572             strerror(errno));
00573 
00574     rv = SYS_Unlink(PCSCLITE_CSOCK_NAME);
00575     if (rv != 0)
00576         Log2(PCSC_LOG_ERROR, "Cannot unlink " PCSCLITE_CSOCK_NAME ": %s",
00577             strerror(errno));
00578 
00579 #ifdef USE_RUN_PID
00580     rv = SYS_Unlink(USE_RUN_PID);
00581     if (rv != 0)
00582         Log2(PCSC_LOG_ERROR, "Cannot unlink " USE_RUN_PID ": %s",
00583             strerror(errno));
00584 #endif
00585 }
00586 
00587 void signal_reload(int sig)
00588 {
00589     static int rescan_ongoing = FALSE;
00590 
00591     if (AraKiri)
00592         return;
00593 
00594     Log1(PCSC_LOG_INFO, "Reload serial configuration");
00595     if (rescan_ongoing)
00596     {
00597         Log1(PCSC_LOG_INFO, "Rescan already ongoing");
00598         return;
00599     }
00600 
00601     rescan_ongoing = TRUE;
00602 
00603     HPReCheckSerialReaders();
00604 
00605     rescan_ongoing = FALSE;
00606     Log1(PCSC_LOG_INFO, "End reload serial configuration");
00607 } /* signal_reload */
00608 
00609 void signal_trap(int sig)
00610 {
00611     /* the signal handler is called several times for the same Ctrl-C */
00612     if (AraKiri == FALSE)
00613     {
00614         Log1(PCSC_LOG_INFO, "Preparing for suicide");
00615         AraKiri = TRUE;
00616 
00617         /* if still in the init/loading phase the AraKiri will not be
00618          * seen by the main event loop
00619          */
00620         if (Init)
00621         {
00622             Log1(PCSC_LOG_INFO, "Suicide during init");
00623             at_exit();
00624         }
00625     }
00626 }
00627 
00628 void print_version (void)
00629 {
00630     printf("%s version %s.\n",  PACKAGE, VERSION);
00631     printf("Copyright (C) 1999-2002 by David Corcoran <corcoran@linuxnet.com>.\n");
00632     printf("Copyright (C) 2001-2005 by Ludovic Rousseau <ludovic.rousseau@free.fr>.\n");
00633     printf("Copyright (C) 2003-2004 by Damien Sauveron <sauveron@labri.fr>.\n");
00634     printf("Report bugs to <sclinux@linuxnet.com>.\n");
00635 }
00636 
00637 void print_usage (char const * const progname)
00638 {
00639     printf("Usage: %s options\n", progname);
00640     printf("Options:\n");
00641 #ifdef HAVE_GETOPT_LONG
00642     printf("  -a, --apdu        log APDU commands and results\n");
00643     printf("  -c, --config      path to reader.conf\n");
00644     printf("  -f, --foreground  run in foreground (no daemon),\n");
00645     printf("            send logs to stderr instead of syslog\n");
00646     printf("  -h, --help        display usage information\n");
00647     printf("  -H, --hotplug     ask the daemon to rescan the avaiable readers\n");
00648     printf("  -v, --version     display the program version number\n");
00649     printf("  -d, --debug       display lower level debug messages\n");
00650     printf("      --info        display info level debug messages (default level)\n");
00651     printf("  -e  --error       display error level debug messages\n");
00652     printf("  -C  --critical    display critical only level debug messages\n");
00653     printf("  --force-reader-polling ignore the IFD_GENERATE_HOTPLUG reader capability\n");
00654 #else
00655     printf("  -a    log APDU commands and results\n");
00656     printf("  -c    path to reader.conf\n");
00657     printf("  -f    run in foreground (no daemon), send logs to stderr instead of syslog\n");
00658     printf("  -d    display debug messages. Output may be:\n");
00659     printf("  -h    display usage information\n");
00660     printf("  -H    ask the daemon to rescan the avaiable readers\n");
00661     printf("  -v    display the program version number\n");
00662 #endif
00663 }
00664 

Generated on Mon Mar 5 22:33:02 2007 for pcsc-lite by  doxygen 1.4.7