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

Generated on Thu Aug 28 20:14:41 2008 for pcsc-lite by  doxygen 1.5.6