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

Generated on Wed Apr 30 17:11:41 2008 for pcsc-lite by  doxygen 1.5.5