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

Generated on Wed Jul 14 16:38:27 2010 for pcsc-lite by  doxygen 1.4.7