pcsc-lite 1.7.2

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