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