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 1827 2006-01-24 14:49:52Z 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 extern char ReCheckSerialReaders;
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 /*
00071  * Cleans up messages still on the queue when a client dies 
00072  */
00073 void SVCClientCleanup(psharedSegmentMsg msgStruct)
00074 {
00075     /*
00076      * May be implemented in future releases 
00077      */
00078 }
00079 
00088 void SVCServiceRunLoop(void)
00089 {
00090     int rsp;
00091     LONG rv;
00092     DWORD dwClientID;   /* Connection ID used to reference the Client */
00093     
00094     rsp = 0;
00095     rv = 0;
00096 
00097     /*
00098      * Initialize the comm structure 
00099      */
00100     rsp = SHMInitializeCommonSegment();
00101 
00102     if (rsp == -1)
00103     {
00104         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00105         exit(-1);
00106     }
00107 
00108     /*
00109      * Initialize the contexts structure 
00110      */
00111     rv = ContextsInitialize();
00112 
00113     if (rv == -1)
00114     {
00115         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00116         exit(-1);
00117     }
00118 
00119     /*
00120      * Solaris sends a SIGALRM and it is annoying 
00121      */
00122 
00123     signal(SIGALRM, SIG_IGN);
00124     signal(SIGPIPE, SIG_IGN);
00125     signal(SIGHUP, SIG_IGN);    /* needed for Solaris. The signal is sent
00126                  * when the shell is existed */
00127 
00128     /*
00129      * This function always returns zero 
00130      */
00131     rsp = SYS_MutexInit(&usbNotifierMutex);
00132 
00133     /*
00134      * Set up the search for USB/PCMCIA devices 
00135      */
00136     HPSearchHotPluggables();
00137     HPRegisterForHotplugEvents();
00138 
00139     /*
00140      * Set up the power management callback routine
00141      */
00142     PMRegisterForPowerEvents();
00143 
00144     while (TRUE)
00145     {
00146 
00147         switch (rsp = SHMProcessEventsServer(&dwClientID, 0))
00148         {
00149 
00150         case 0:
00151             Log2(PCSC_LOG_DEBUG, "A new context thread creation is requested: %d", dwClientID);
00152             rv = CreateContextThread(&dwClientID);
00153 
00154             if (rv != SCARD_S_SUCCESS)
00155             {
00156                 Log1(PCSC_LOG_ERROR, "Problem during the context thread creation");
00157                 AraKiri = TRUE;
00158             }
00159 
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             /* do not display if we are exiting or re-reading the config */
00172             if ((!AraKiri) && (!ReCheckSerialReaders))
00173                 Log1(PCSC_LOG_ERROR, "Error in SHMProcessEventsServer");
00174             break;
00175 
00176         default:
00177             Log2(PCSC_LOG_ERROR, "SHMProcessEventsServer unknown retval: %d",
00178                 rsp);
00179             break;
00180         }
00181 
00182         if (AraKiri)
00183         {
00184             /* stop the hotpug thread and waits its exit */
00185             HPStopHotPluggables();
00186             SYS_Sleep(1);
00187 
00188             /* now stop all the drivers */
00189             RFCleanupReaders(1);
00190         }
00191     }
00192 }
00193 
00194 int main(int argc, char **argv)
00195 {
00196     int rv;
00197     char setToForeground;
00198     char *newReaderConfig;
00199     struct stat fStatBuf;
00200     int opt;
00201 #ifdef HAVE_GETOPT_LONG
00202     int option_index = 0;
00203     static struct option long_options[] = {
00204         {"config", 1, 0, 'c'},
00205         {"foreground", 0, 0, 'f'},
00206         {"help", 0, 0, 'h'},
00207         {"version", 0, 0, 'v'},
00208         {"apdu", 0, 0, 'a'},
00209         {"debug", 0, 0, 'd'},
00210         {"info", 0, 0, 0},
00211         {"error", 0, 0, 'e'},
00212         {"critical", 0, 0, 'C'},
00213         {0, 0, 0, 0}
00214     };
00215 #endif
00216     
00217     rv = 0;
00218     newReaderConfig = 0;
00219     setToForeground = FALSE;
00220 
00221     /*
00222      * test the version 
00223      */
00224     if (strcmp(PCSCLITE_VERSION_NUMBER, VERSION) != 0)
00225     {
00226         printf("BUILD ERROR: The release version number PCSCLITE_VERSION_NUMBER\n");
00227         printf("  in pcsclite.h (%s) does not match the release version number\n",
00228             PCSCLITE_VERSION_NUMBER);
00229         printf("  generated in config.h (%s) (see configure.in).\n", VERSION);
00230 
00231         return EXIT_FAILURE;
00232     }
00233 
00234     /*
00235      * By default we create a daemon (not connected to any output)
00236      * so log to syslog to have error messages.
00237      */
00238     DebugLogSetLogType(DEBUGLOG_SYSLOG_DEBUG);
00239 
00240     /*
00241      * Handle any command line arguments 
00242      */
00243 #ifdef  HAVE_GETOPT_LONG
00244     while ((opt = getopt_long (argc, argv, "c:fdhvaeC", long_options, &option_index)) != -1) {
00245 #else
00246     while ((opt = getopt (argc, argv, "c:fdhvaeC")) != -1) {
00247 #endif
00248         switch (opt) {
00249             case 'c':
00250                 Log2(PCSC_LOG_INFO, "using new config file: %s", optarg);
00251                 newReaderConfig = optarg;
00252                 break;
00253 
00254             case 'f':
00255                 setToForeground = TRUE;
00256                 /* debug to stderr instead of default syslog */
00257                 DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG);
00258                 Log1(PCSC_LOG_INFO,
00259                     "pcscd set to foreground with debug send to stderr");
00260                 break;
00261 
00262             case 'd':
00263                 DebugLogSetLevel(PCSC_LOG_DEBUG);
00264                 break;
00265 
00266             case 'e':
00267                 DebugLogSetLevel(PCSC_LOG_ERROR);
00268                 break;
00269 
00270             case 'C':
00271                 DebugLogSetLevel(PCSC_LOG_CRITICAL);
00272                 break;
00273 
00274             case 'h':
00275                 print_usage (argv[0]);
00276                 return EXIT_SUCCESS;
00277 
00278             case 'v':
00279                 print_version ();
00280                 return EXIT_SUCCESS;
00281 
00282             case 'a':
00283                 DebugLogSetCategory(DEBUG_CATEGORY_APDU);
00284                 break;
00285 
00286             default:
00287                 print_usage (argv[0]);
00288                 return EXIT_FAILURE;
00289         }
00290 
00291     }
00292     
00293     if (argv[optind])
00294     {
00295         printf("Unknown option: %s\n\n", argv[optind]);
00296         print_usage(argv[0]);
00297         return EXIT_SUCCESS;
00298     }
00299 
00300     /*
00301      * test the presence of /var/run/pcsc.pub
00302      */
00303 
00304     rv = SYS_Stat(PCSCLITE_PUBSHM_FILE, &fStatBuf);
00305 
00306     if (rv == 0)
00307     {
00308 #ifdef USE_RUN_PID
00309 
00310         /* read the pid file to get the old pid and test if the old pcscd is
00311          * still running 
00312          */
00313         FILE *f;
00314         /* pids are only 15 bits but 4294967296
00315          * (32 bits in case of a new system use it) is on 10 bytes
00316          */
00317 #define PID_ASCII_SIZE 11
00318         char pid_ascii[PID_ASCII_SIZE];
00319         int pid;
00320 
00321         if ((f = fopen(USE_RUN_PID, "rb")) != NULL)
00322         {
00323             fgets(pid_ascii, PID_ASCII_SIZE, f);
00324             fclose(f);
00325 
00326             pid = atoi(pid_ascii);
00327 
00328             if (kill(pid, 0) == 0)
00329             {
00330                 Log1(PCSC_LOG_CRITICAL,
00331                     "file " PCSCLITE_PUBSHM_FILE " already exists.");
00332                 Log2(PCSC_LOG_CRITICAL,
00333                     "Another pcscd (pid: %d) seems to be running.", pid);
00334                 return EXIT_FAILURE;
00335             }
00336             else
00337                 /* the old pcscd is dead. make some cleanup */
00338                 clean_temp_files();
00339         }
00340         else
00341         {
00342             Log1(PCSC_LOG_CRITICAL,
00343                 "file " PCSCLITE_PUBSHM_FILE " already exists.");
00344             Log1(PCSC_LOG_CRITICAL,
00345                 "Maybe another pcscd is running?");
00346             Log1(PCSC_LOG_CRITICAL, 
00347                 "I can't read process pid from " USE_RUN_PID);
00348             Log1(PCSC_LOG_CRITICAL,
00349                 "Remove " PCSCLITE_PUBSHM_FILE " and " PCSCLITE_CSOCK_NAME);
00350             Log1(PCSC_LOG_CRITICAL,
00351                 "if pcscd is not running to clear this message.");
00352             return EXIT_FAILURE;
00353         }
00354 #else
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             "Remove " PCSCLITE_PUBSHM_FILE " and " PCSCLITE_CSOCK_NAME);
00361         Log1(PCSC_LOG_CRITICAL,
00362             "if pcscd is not running to clear this message.");
00363         return EXIT_FAILURE;
00364 #endif
00365     }
00366 
00367     /*
00368      * If this is set to one the user has asked it not to fork 
00369      */
00370     if (!setToForeground)
00371     {
00372         if (SYS_Daemon(0, 0))
00373             Log2(PCSC_LOG_CRITICAL, "SYS_Daemon() failed: %s",
00374                 strerror(errno));
00375     }
00376 
00377     /*
00378      * cleanly remove /tmp/pcsc when exiting 
00379      */
00380     signal(SIGQUIT, signal_trap);
00381     signal(SIGTERM, signal_trap);
00382     signal(SIGINT, signal_trap);
00383     signal(SIGHUP, signal_trap);
00384 
00385 #ifdef USE_RUN_PID
00386     /*
00387      * Record our pid to make it easier
00388      * to kill the correct pcscd
00389      */
00390     {
00391         FILE *f;
00392 
00393         if ((f = fopen(USE_RUN_PID, "wb")) != NULL)
00394         {
00395             fprintf(f, "%u\n", (unsigned) getpid());
00396             fclose(f);
00397         }
00398     }
00399 #endif
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, S_ISVTX | S_IRWXO | S_IRWXG | S_IRWXU);
00408         if (rv != 0)
00409         {
00410             Log2(PCSC_LOG_CRITICAL,
00411                 "cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno));
00412             return EXIT_FAILURE;
00413         }
00414     }
00415 
00416     /* cleanly remove /var/run/pcsc.* files when exiting */
00417     if (atexit(at_exit))
00418         Log2(PCSC_LOG_CRITICAL, "atexit() failed: %s", strerror(errno));
00419 
00420     /*
00421      * Allocate memory for reader structures 
00422      */
00423     RFAllocateReaderSpace();
00424 
00425     /*
00426      * Grab the information from the reader.conf 
00427      */
00428     if (newReaderConfig)
00429     {
00430         rv = RFStartSerialReaders(newReaderConfig);
00431         if (rv != 0)
00432         {
00433             Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig,
00434                 strerror(errno));
00435             at_exit();
00436         }
00437     }
00438     else
00439     {
00440         rv = RFStartSerialReaders(PCSCLITE_READER_CONFIG);
00441 
00442 #if 0
00443         if (rv == 1)
00444         {
00445             Log1(PCSC_LOG_INFO,
00446                 "warning: no " PCSCLITE_READER_CONFIG " found");
00447             /*
00448              * Token error in file 
00449              */
00450         }
00451         else
00452 #endif
00453             if (rv == -1)
00454                 at_exit();
00455     }
00456 
00457     /*
00458      * Set the default globals 
00459      */
00460     g_rgSCardT0Pci.dwProtocol = SCARD_PROTOCOL_T0;
00461     g_rgSCardT1Pci.dwProtocol = SCARD_PROTOCOL_T1;
00462     g_rgSCardRawPci.dwProtocol = SCARD_PROTOCOL_RAW;
00463 
00464     Log1(PCSC_LOG_INFO, "pcsc-lite " VERSION " daemon ready.");
00465 
00466     /*
00467      * post initialistion 
00468      */
00469     Init = FALSE;
00470 
00471     /*
00472      * signal_trap() does just set a global variable used by the main loop 
00473      */
00474     signal(SIGQUIT, signal_trap);
00475     signal(SIGTERM, signal_trap);
00476     signal(SIGINT, signal_trap);
00477     signal(SIGHUP, signal_trap);
00478 
00479     signal(SIGUSR1, signal_reload);
00480 
00481     SVCServiceRunLoop();
00482 
00483     Log1(PCSC_LOG_ERROR, "SVCServiceRunLoop returned");
00484     return EXIT_FAILURE;
00485 }
00486 
00487 void at_exit(void)
00488 {
00489     Log1(PCSC_LOG_INFO, "cleaning " PCSCLITE_IPC_DIR);
00490 
00491     clean_temp_files();
00492 
00493     SYS_Exit(EXIT_SUCCESS);
00494 }
00495 
00496 void clean_temp_files(void)
00497 {
00498     int rv;
00499 
00500     rv = SYS_Unlink(PCSCLITE_PUBSHM_FILE);
00501     if (rv != 0)
00502         Log2(PCSC_LOG_ERROR, "Cannot unlink " PCSCLITE_PUBSHM_FILE ": %s",
00503             strerror(errno));
00504 
00505     rv = SYS_Unlink(PCSCLITE_CSOCK_NAME);
00506     if (rv != 0)
00507         Log2(PCSC_LOG_ERROR, "Cannot unlink " PCSCLITE_CSOCK_NAME ": %s",
00508             strerror(errno));
00509 
00510 #ifdef USE_RUN_PID
00511     rv = SYS_Unlink(USE_RUN_PID);
00512     if (rv != 0)
00513         Log2(PCSC_LOG_ERROR, "Cannot unlink " USE_RUN_PID ": %s",
00514             strerror(errno));
00515 #endif
00516 }
00517 
00518 void signal_reload(int sig)
00519 {
00520     Log1(PCSC_LOG_INFO, "Reload serial configuration");
00521     HPReCheckSerialReaders();
00522 } /* signal_reload */
00523 
00524 void signal_trap(int sig)
00525 {
00526     /* the signal handler is called several times for the same Ctrl-C */
00527     if (AraKiri == FALSE)
00528     {
00529         Log1(PCSC_LOG_INFO, "Preparing for suicide");
00530         AraKiri = TRUE;
00531 
00532         /* if still in the init/loading phase the AraKiri will not be
00533          * seen by the main event loop
00534          */
00535         if (Init)
00536         {
00537             Log1(PCSC_LOG_INFO, "Suicide during init");
00538             at_exit();
00539         }
00540     }
00541 }
00542 
00543 void print_version (void)
00544 {
00545     printf("%s version %s.\n",  PACKAGE, VERSION);
00546     printf("Copyright (C) 1999-2002 by David Corcoran <corcoran@linuxnet.com>.\n");
00547     printf("Copyright (C) 2001-2005 by Ludovic Rousseau <ludovic.rousseau@free.fr>.\n");
00548     printf("Copyright (C) 2003-2004 by Damien Sauveron <sauveron@labri.fr>.\n");
00549     printf("Report bugs to <sclinux@linuxnet.com>.\n");
00550 }
00551 
00552 void print_usage (char const * const progname)
00553 {
00554     printf("Usage: %s options\n", progname);
00555     printf("Options:\n");
00556 #ifdef HAVE_GETOPT_LONG
00557     printf("  -a, --apdu        log APDU commands and results\n");
00558     printf("  -c, --config      path to reader.conf\n");
00559     printf("  -f, --foreground  run in foreground (no daemon),\n");
00560     printf("            send logs to stderr instead of syslog\n");
00561     printf("  -h, --help        display usage information\n");
00562     printf("  -v, --version     display the program version number\n");
00563     printf("  -d, --debug       display lower level debug messages\n"); 
00564     printf("      --info        display info level debug messages (default level)\n"); 
00565     printf("  -e  --error       display error level debug messages\n"); 
00566     printf("  -C  --critical    display critical only level debug messages\n"); 
00567 #else
00568     printf("  -a    log APDU commands and results\n");
00569     printf("  -c    path to reader.conf\n");
00570     printf("  -f    run in foreground (no daemon), send logs to stderr instead of syslog\n");
00571     printf("  -d    display debug messages. Output may be:\n"); 
00572     printf("  -h    display usage information\n");
00573     printf("  -v    display the program version number\n");
00574 #endif
00575 }
00576 

Generated on Mon Mar 26 20:50:45 2007 for pcsc-lite by  doxygen 1.4.7