pcsc-lite 1.7.2
|
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