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) 2002-2009
00007  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
00008  *
00009  * $Id: pcscdaemon.c 4969 2010-06-01 07:33:36Z 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 "hotplug.h"
00044 #include "readerfactory.h"
00045 #include "configfile.h"
00046 #include "powermgt_generic.h"
00047 #include "utils.h"
00048 
00049 #ifndef TRUE
00050 #define TRUE 1
00051 #define FALSE 0
00052 #endif
00053 
00054 char AraKiri = FALSE;
00055 static char Init = TRUE;
00056 char AutoExit = FALSE;
00057 static int ExitValue = EXIT_FAILURE;
00058 int HPForceReaderPolling = 0;
00059 static int pipefd[] = {-1, -1};
00060 
00061 /*
00062  * Some internal functions
00063  */
00064 static void at_exit(void);
00065 static void clean_temp_files(void);
00066 static void signal_reload(int sig);
00067 static void signal_trap(int);
00068 static void print_version (void);
00069 static void print_usage (char const * const);
00070 
00079 static void SVCServiceRunLoop(void)
00080 {
00081     int rsp;
00082     LONG rv;
00083     uint32_t dwClientID;    /* Connection ID used to reference the Client */
00084 
00085     rv = 0;
00086 
00087     while (TRUE)
00088     {
00089         switch (rsp = ProcessEventsServer(&dwClientID))
00090         {
00091 
00092         case 0:
00093             Log2(PCSC_LOG_DEBUG, "A new context thread creation is requested: %d", dwClientID);
00094             rv = CreateContextThread(&dwClientID);
00095 
00096             if (rv != SCARD_S_SUCCESS)
00097                 Log1(PCSC_LOG_ERROR, "Problem during the context thread creation");
00098             break;
00099 
00100         case 2:
00101             /*
00102              * timeout in ProcessEventsServer(): do nothing
00103              * this is used to catch the Ctrl-C signal at some time when
00104              * nothing else happens
00105              */
00106             break;
00107 
00108         case -1:
00109             Log1(PCSC_LOG_ERROR, "Error in ProcessEventsServer");
00110             break;
00111 
00112         case -2:
00113             /* Nothing to do in case of a syscall interrupted
00114              * It happens when SIGUSR1 (reload) or SIGINT (Ctrl-C) is received
00115              * We just try again */
00116             break;
00117 
00118         default:
00119             Log2(PCSC_LOG_ERROR, "ProcessEventsServer unknown retval: %d",
00120                 rsp);
00121             break;
00122         }
00123 
00124         if (AraKiri)
00125         {
00126             /* stop the hotpug thread and waits its exit */
00127 #ifdef USE_USB
00128             (void)HPStopHotPluggables();
00129 #endif
00130             (void)SYS_Sleep(1);
00131 
00132             /* now stop all the drivers */
00133             RFCleanupReaders();
00134             ContextsDeinitialize();
00135             at_exit();
00136         }
00137     }
00138 }
00139 
00140 int main(int argc, char **argv)
00141 {
00142     int rv;
00143     char setToForeground;
00144     char HotPlug;
00145     char *newReaderConfig;
00146     struct stat fStatBuf;
00147     int customMaxThreadCounter = 0;
00148     int customMaxReaderHandles = 0;
00149     int customMaxThreadCardHandles = 0;
00150     int opt;
00151 #ifdef HAVE_GETOPT_LONG
00152     int option_index = 0;
00153     static struct option long_options[] = {
00154         {"config", 1, NULL, 'c'},
00155         {"foreground", 0, NULL, 'f'},
00156         {"help", 0, NULL, 'h'},
00157         {"version", 0, NULL, 'v'},
00158         {"apdu", 0, NULL, 'a'},
00159         {"debug", 0, NULL, 'd'},
00160         {"info", 0, NULL, 0},
00161         {"error", 0, NULL, 'e'},
00162         {"critical", 0, NULL, 'C'},
00163         {"hotplug", 0, NULL, 'H'},
00164         {"force-reader-polling", optional_argument, NULL, 0},
00165         {"max-thread", 1, NULL, 't'},
00166         {"max-card-handle-per-thread", 1, NULL, 's'},
00167         {"max-card-handle-per-reader", 1, NULL, 'r'},
00168         {"auto-exit", 0, NULL, 'x'},
00169         {NULL, 0, NULL, 0}
00170     };
00171 #endif
00172 #define OPT_STRING "c:fdhvaeCHt:r:s:x"
00173 
00174     rv = 0;
00175     newReaderConfig = NULL;
00176     setToForeground = FALSE;
00177     HotPlug = FALSE;
00178 
00179     /*
00180      * test the version
00181      */
00182     if (strcmp(PCSCLITE_VERSION_NUMBER, VERSION) != 0)
00183     {
00184         printf("BUILD ERROR: The release version number PCSCLITE_VERSION_NUMBER\n");
00185         printf("  in pcsclite.h (%s) does not match the release version number\n",
00186             PCSCLITE_VERSION_NUMBER);
00187         printf("  generated in config.h (%s) (see configure.in).\n", VERSION);
00188 
00189         return EXIT_FAILURE;
00190     }
00191 
00192     /*
00193      * By default we create a daemon (not connected to any output)
00194      * so log to syslog to have error messages.
00195      */
00196     DebugLogSetLogType(DEBUGLOG_SYSLOG_DEBUG);
00197 
00198     /*
00199      * Handle any command line arguments
00200      */
00201 #ifdef  HAVE_GETOPT_LONG
00202     while ((opt = getopt_long (argc, argv, OPT_STRING, long_options, &option_index)) != -1) {
00203 #else
00204     while ((opt = getopt (argc, argv, OPT_STRING)) != -1) {
00205 #endif
00206         switch (opt) {
00207 #ifdef  HAVE_GETOPT_LONG
00208             case 0:
00209                 if (strcmp(long_options[option_index].name,
00210                     "force-reader-polling") == 0)
00211                     HPForceReaderPolling = optarg ? abs(atoi(optarg)) : 1;
00212                 break;
00213 #endif
00214             case 'c':
00215                 Log2(PCSC_LOG_INFO, "using new config file: %s", optarg);
00216                 newReaderConfig = optarg;
00217                 break;
00218 
00219             case 'f':
00220                 setToForeground = TRUE;
00221                 /* debug to stderr instead of default syslog */
00222                 DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG);
00223                 Log1(PCSC_LOG_INFO,
00224                     "pcscd set to foreground with debug send to stderr");
00225                 break;
00226 
00227             case 'd':
00228                 DebugLogSetLevel(PCSC_LOG_DEBUG);
00229                 break;
00230 
00231             case 'e':
00232                 DebugLogSetLevel(PCSC_LOG_ERROR);
00233                 break;
00234 
00235             case 'C':
00236                 DebugLogSetLevel(PCSC_LOG_CRITICAL);
00237                 break;
00238 
00239             case 'h':
00240                 print_usage (argv[0]);
00241                 return EXIT_SUCCESS;
00242 
00243             case 'v':
00244                 print_version ();
00245                 return EXIT_SUCCESS;
00246 
00247             case 'a':
00248                 (void)DebugLogSetCategory(DEBUG_CATEGORY_APDU);
00249                 break;
00250 
00251             case 'H':
00252                 /* debug to stderr instead of default syslog */
00253                 DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG);
00254                 HotPlug = TRUE;
00255                 break;
00256 
00257             case 't':
00258                 customMaxThreadCounter = optarg ? atoi(optarg) : 0; 
00259                 Log2(PCSC_LOG_INFO, "setting customMaxThreadCounter to: %d",
00260                     customMaxThreadCounter);
00261                 break;
00262 
00263             case 'r':
00264                 customMaxReaderHandles = optarg ? atoi(optarg) : 0; 
00265                 Log2(PCSC_LOG_INFO, "setting customMaxReaderHandles to: %d",
00266                     customMaxReaderHandles);
00267                 break;
00268 
00269             case 's':
00270                 customMaxThreadCardHandles = optarg ? atoi(optarg) : 0; 
00271                 Log2(PCSC_LOG_INFO, "setting customMaxThreadCardHandles to: %d",
00272                     customMaxThreadCardHandles);
00273                 break;
00274 
00275             case 'x':
00276                 AutoExit = TRUE;
00277                 Log2(PCSC_LOG_INFO, "Auto exit after %d seconds of inactivity",
00278                     TIME_BEFORE_SUICIDE);
00279                 break;
00280 
00281             default:
00282                 print_usage (argv[0]);
00283                 return EXIT_FAILURE;
00284         }
00285 
00286     }
00287 
00288     if (argv[optind])
00289     {
00290         printf("Unknown option: %s\n", argv[optind]);
00291         print_usage(argv[0]);
00292         return EXIT_FAILURE;
00293     }
00294 
00295     /*
00296      * test the presence of /var/run/pcscd/pcscd.comm
00297      */
00298 
00299     rv = stat(PCSCLITE_CSOCK_NAME, &fStatBuf);
00300 
00301     if (rv == 0)
00302     {
00303         pid_t pid;
00304 
00305         /* read the pid file to get the old pid and test if the old pcscd is
00306          * still running
00307          */
00308         pid = GetDaemonPid();
00309 
00310         if (pid != -1)
00311         {
00312             if (HotPlug)
00313                 return SendHotplugSignal();
00314 
00315             rv = kill(pid, 0);
00316             if (0 == rv)
00317             {
00318                 Log1(PCSC_LOG_CRITICAL,
00319                     "file " PCSCLITE_CSOCK_NAME " already exists.");
00320                 Log2(PCSC_LOG_CRITICAL,
00321                     "Another pcscd (pid: %d) seems to be running.", pid);
00322                 return EXIT_FAILURE;
00323             }
00324             else
00325                 if (ESRCH == errno)
00326                 {
00327                     /* the old pcscd is dead. make some cleanup */
00328                     clean_temp_files();
00329                 }
00330                 else
00331                 {
00332                     /* permission denied or other error */
00333                     Log2(PCSC_LOG_CRITICAL, "kill failed: %s", strerror(errno));
00334                     return EXIT_FAILURE;
00335                 }
00336         }
00337         else
00338         {
00339             if (HotPlug)
00340             {
00341                 Log1(PCSC_LOG_CRITICAL, "file " PCSCLITE_RUN_PID " do not exist");
00342                 Log1(PCSC_LOG_CRITICAL, "Hotplug failed");
00343                 return EXIT_FAILURE;
00344             }
00345 
00346             Log1(PCSC_LOG_CRITICAL,
00347                 "file " PCSCLITE_CSOCK_NAME " already exists.");
00348             Log1(PCSC_LOG_CRITICAL,
00349                 "Maybe another pcscd is running?");
00350             Log1(PCSC_LOG_CRITICAL,
00351                 "I can't read process pid from " PCSCLITE_RUN_PID);
00352             Log1(PCSC_LOG_CRITICAL, "Remove " PCSCLITE_CSOCK_NAME);
00353             Log1(PCSC_LOG_CRITICAL,
00354                 "if pcscd is not running to clear this message.");
00355             return EXIT_FAILURE;
00356         }
00357     }
00358     else
00359         if (HotPlug)
00360         {
00361             Log1(PCSC_LOG_CRITICAL, "Hotplug failed: pcscd is not running");
00362             return EXIT_FAILURE;
00363         }
00364 
00365     /* like in daemon(3): changes the current working directory to the
00366      * root ("/") */
00367     (void)chdir("/");
00368 
00369     if (AutoExit)
00370     {
00371         int pid;
00372 
00373         /* fork() so that pcscd always return in --auto-exit mode */
00374         pid = fork();
00375         if (-1 == pid )
00376             Log2(PCSC_LOG_CRITICAL, "fork() failed: %s", strerror(errno));
00377 
00378         if (pid)
00379             /* father */
00380             return EXIT_SUCCESS;
00381     }
00382 
00383     /*
00384      * If this is set to one the user has asked it not to fork
00385      */
00386     if (!setToForeground)
00387     {
00388         int pid;
00389 
00390         if (pipe(pipefd) == -1)
00391         {
00392             Log2(PCSC_LOG_CRITICAL, "pipe() failed: %s", strerror(errno));
00393             return EXIT_FAILURE;
00394         }
00395 
00396         pid = fork();
00397         if (-1 == pid)
00398         {
00399             Log2(PCSC_LOG_CRITICAL, "fork() failed: %s", strerror(errno));
00400             return EXIT_FAILURE;
00401         }
00402 
00403         /* like in daemon(3): redirect standard input, standard output
00404          * and standard error to /dev/null */
00405         (void)close(0);
00406         (void)close(1);
00407         (void)close(2);
00408 
00409         if (pid)
00410         /* in the father */
00411         {
00412             char buf;
00413             int ret;
00414 
00415             /* close write side */
00416             close(pipefd[1]);
00417 
00418             /* wait for the son to write the return code */
00419             ret = read(pipefd[0], &buf, 1);
00420             if (ret <= 0)
00421                 return 2;
00422 
00423             close(pipefd[0]);
00424 
00425             /* exit code */
00426             return buf;
00427         }
00428         else
00429         /* in the son */
00430         {
00431             /* close read side */
00432             close(pipefd[0]);
00433         }
00434     }
00435 
00436     /*
00437      * cleanly remove /var/run/pcscd/files when exiting
00438      * signal_trap() does just set a global variable used by the main loop
00439      */
00440     (void)signal(SIGQUIT, signal_trap);
00441     (void)signal(SIGTERM, signal_trap);
00442     (void)signal(SIGINT, signal_trap);
00443 
00444     /* exits on SIGALARM to allow pcscd to suicide if not used */
00445     (void)signal(SIGALRM, signal_trap);
00446 
00447     /*
00448      * If PCSCLITE_IPC_DIR does not exist then create it
00449      */
00450     rv = stat(PCSCLITE_IPC_DIR, &fStatBuf);
00451     if (rv < 0)
00452     {
00453         int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU;
00454 
00455         rv = mkdir(PCSCLITE_IPC_DIR, mode);
00456         if (rv != 0)
00457         {
00458             Log2(PCSC_LOG_CRITICAL,
00459                 "cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno));
00460             return EXIT_FAILURE;
00461         }
00462 
00463         /* set mode so that the directory is world readable and
00464          * executable even is umask is restrictive
00465          * The directory containes files used by libpcsclite */
00466         (void)chmod(PCSCLITE_IPC_DIR, mode);
00467     }
00468 
00469     /*
00470      * Record our pid to make it easier
00471      * to kill the correct pcscd
00472      */
00473     {
00474         int f;
00475         int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
00476 
00477         if ((f = open(PCSCLITE_RUN_PID, O_RDWR | O_CREAT, mode)) != -1)
00478         {
00479             char pid[PID_ASCII_SIZE];
00480 
00481             (void)snprintf(pid, sizeof(pid), "%u\n", (unsigned) getpid());
00482             (void)write(f, pid, strlen(pid));
00483             (void)close(f);
00484 
00485             /* set mode so that the file is world readable even is umask is
00486              * restrictive
00487              * The file is used by libpcsclite */
00488             (void)chmod(PCSCLITE_RUN_PID, mode);
00489         }
00490         else
00491             Log2(PCSC_LOG_CRITICAL, "cannot create " PCSCLITE_RUN_PID ": %s",
00492                 strerror(errno));
00493     }
00494 
00495     /* cleanly remove /var/run/pcscd/pcsc.* files when exiting */
00496     if (atexit(at_exit))
00497         Log2(PCSC_LOG_CRITICAL, "atexit() failed: %s", strerror(errno));
00498 
00499     /*
00500      * Allocate memory for reader structures
00501      */
00502     rv = RFAllocateReaderSpace(customMaxReaderHandles);
00503     if (SCARD_S_SUCCESS != rv)
00504         at_exit();
00505 
00506 #ifdef USE_SERIAL
00507     /*
00508      * Grab the information from the reader.conf
00509      */
00510     if (newReaderConfig)
00511     {
00512         rv = RFStartSerialReaders(newReaderConfig);
00513         if (rv != 0)
00514         {
00515             Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig,
00516                 strerror(errno));
00517             at_exit();
00518         }
00519     }
00520     else
00521     {
00522         rv = RFStartSerialReaders(PCSCLITE_CONFIG_DIR);
00523         if (rv == -1)
00524             at_exit();
00525     }
00526 #endif
00527 
00528     Log1(PCSC_LOG_INFO, "pcsc-lite " VERSION " daemon ready.");
00529 
00530     /*
00531      * post initialistion
00532      */
00533     Init = FALSE;
00534 
00535     /*
00536      * Hotplug rescan
00537      */
00538     (void)signal(SIGUSR1, signal_reload);
00539 
00540     /*
00541      * Initialize the comm structure
00542      */
00543     rv = InitializeSocket();
00544     if (rv)
00545     {
00546         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00547         at_exit();
00548     }
00549 
00550     /*
00551      * Initialize the contexts structure
00552      */
00553     rv = ContextsInitialize(customMaxThreadCounter, customMaxThreadCardHandles);
00554 
00555     if (rv == -1)
00556     {
00557         Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd.");
00558         at_exit();
00559     }
00560 
00561     (void)signal(SIGPIPE, SIG_IGN);
00562     (void)signal(SIGHUP, SIG_IGN);  /* needed for Solaris. The signal is sent
00563                  * when the shell is existed */
00564 
00565 #if !defined(PCSCLITE_STATIC_DRIVER) && defined(USE_USB)
00566     /*
00567      * Set up the search for USB/PCMCIA devices
00568      */
00569     rv = HPSearchHotPluggables();
00570     if (rv)
00571         at_exit();
00572 
00573     rv = HPRegisterForHotplugEvents();
00574     if (rv)
00575         at_exit();
00576 #endif
00577 
00578     /*
00579      * Set up the power management callback routine
00580      */
00581     (void)PMRegisterForPowerEvents();
00582 
00583     /* initialisation succeeded */
00584     if (pipefd[1] >= 0)
00585     {
00586         char buf = 0;
00587 
00588         /* write a 0 (success) to father process */
00589         write(pipefd[1], &buf, 1);
00590         close(pipefd[1]);
00591     }
00592 
00593     SVCServiceRunLoop();
00594 
00595     Log1(PCSC_LOG_ERROR, "SVCServiceRunLoop returned");
00596     return EXIT_FAILURE;
00597 }
00598 
00599 static void at_exit(void)
00600 {
00601     Log1(PCSC_LOG_INFO, "cleaning " PCSCLITE_IPC_DIR);
00602 
00603     clean_temp_files();
00604 
00605     if (pipefd[1] >= 0)
00606     {
00607         char buf;
00608 
00609         /* write the error code to father process */
00610         buf = ExitValue;
00611         write(pipefd[1], &buf, 1);
00612         close(pipefd[1]);
00613     }
00614 
00615     _exit(ExitValue);
00616 }
00617 
00618 static void clean_temp_files(void)
00619 {
00620     int rv;
00621 
00622     rv = remove(PCSCLITE_CSOCK_NAME);
00623     if (rv != 0)
00624         Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_CSOCK_NAME ": %s",
00625             strerror(errno));
00626 
00627     rv = remove(PCSCLITE_RUN_PID);
00628     if (rv != 0)
00629         Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_RUN_PID ": %s",
00630             strerror(errno));
00631 }
00632 
00633 static void signal_reload(/*@unused@*/ int sig)
00634 {
00635     (void)signal(SIGUSR1, signal_reload);
00636 
00637     (void)sig;
00638 
00639     if (AraKiri)
00640         return;
00641 
00642 #ifdef USE_USB
00643     HPReCheckSerialReaders();
00644 #endif
00645 } /* signal_reload */
00646 
00647 static void signal_trap(int sig)
00648 {
00649     Log2(PCSC_LOG_INFO, "Received signal: %d", sig);
00650 
00651     /* the signal handler is called several times for the same Ctrl-C */
00652     if (AraKiri == FALSE)
00653     {
00654         Log1(PCSC_LOG_INFO, "Preparing for suicide");
00655         AraKiri = TRUE;
00656 
00657         /* if still in the init/loading phase the AraKiri will not be
00658          * seen by the main event loop
00659          */
00660         if (Init)
00661         {
00662             Log1(PCSC_LOG_INFO, "Suicide during init");
00663             at_exit();
00664         }
00665     }
00666 }
00667 
00668 static void print_version (void)
00669 {
00670     printf("%s version %s.\n",  PACKAGE, VERSION);
00671     printf("Copyright (C) 1999-2002 by David Corcoran <corcoran@linuxnet.com>.\n");
00672     printf("Copyright (C) 2001-2010 by Ludovic Rousseau <ludovic.rousseau@free.fr>.\n");
00673     printf("Copyright (C) 2003-2004 by Damien Sauveron <sauveron@labri.fr>.\n");
00674     printf("Report bugs to <muscle@lists.musclecard.com>.\n");
00675 
00676     printf ("Enabled features:%s\n", PCSCLITE_FEATURES);
00677 }
00678 
00679 static void print_usage (char const * const progname)
00680 {
00681     printf("Usage: %s options\n", progname);
00682     printf("Options:\n");
00683 #ifdef HAVE_GETOPT_LONG
00684     printf("  -a, --apdu        log APDU commands and results\n");
00685     printf("  -c, --config      path to reader.conf\n");
00686     printf("  -f, --foreground  run in foreground (no daemon),\n");
00687     printf("            send logs to stderr instead of syslog\n");
00688     printf("  -h, --help        display usage information\n");
00689     printf("  -H, --hotplug     ask the daemon to rescan the available readers\n");
00690     printf("  -v, --version     display the program version number\n");
00691     printf("  -d, --debug       display lower level debug messages\n");
00692     printf("      --info        display info level debug messages (default level)\n");
00693     printf("  -e  --error       display error level debug messages\n");
00694     printf("  -C  --critical    display critical only level debug messages\n");
00695     printf("  --force-reader-polling ignore the IFD_GENERATE_HOTPLUG reader capability\n");
00696     printf("  -t, --max-thread  maximum number of threads (default %d)\n", PCSC_MAX_CONTEXT_THREADS);
00697     printf("  -s, --max-card-handle-per-thread  maximum number of card handle per thread (default: %d)\n", PCSC_MAX_CONTEXT_CARD_HANDLES);
00698     printf("  -r, --max-card-handle-per-reader  maximum number of card handle per reader (default: %d)\n", PCSC_MAX_READER_HANDLES);
00699 #else
00700     printf("  -a    log APDU commands and results\n");
00701     printf("  -c    path to reader.conf\n");
00702     printf("  -f    run in foreground (no daemon), send logs to stderr instead of syslog\n");
00703     printf("  -d    display debug messages. Output may be:\n");
00704     printf("  -h    display usage information\n");
00705     printf("  -H    ask the daemon to rescan the available readers\n");
00706     printf("  -v    display the program version number\n");
00707     printf("  -t    maximum number of threads\n");
00708     printf("  -s    maximum number of card handle per thread\n");
00709     printf("  -r    maximum number of card handle per reader\n");
00710 #endif
00711 }
00712