pcscdaemon.c

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

Generated on Mon Aug 17 01:00:10 2009 for pcsc-lite by  doxygen 1.5.9