int main(int argc, char **argv) { int rv; char setToForeground; char HotPlug; char *newReaderConfig; struct stat fStatBuf; int customMaxThreadCounter = 0; int customMaxReaderHandles = 0; int customMaxThreadCardHandles = 0; int opt; int limited_rights = FALSE; int r; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option long_options[] = { {"config", 1, NULL, 'c'}, {"foreground", 0, NULL, 'f'}, {"color", 0, NULL, 'T'}, {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'v'}, {"apdu", 0, NULL, 'a'}, {"debug", 0, NULL, 'd'}, {"info", 0, NULL, 0}, {"error", 0, NULL, 'e'}, {"critical", 0, NULL, 'C'}, {"hotplug", 0, NULL, 'H'}, {"force-reader-polling", optional_argument, NULL, 0}, {"max-thread", 1, NULL, 't'}, {"max-card-handle-per-thread", 1, NULL, 's'}, {"max-card-handle-per-reader", 1, NULL, 'r'}, {"auto-exit", 0, NULL, 'x'}, {"reader-name-no-serial", 0, NULL, 'S'}, {"reader-name-no-interface", 0, NULL, 'I'}, {NULL, 0, NULL, 0} }; #endif #define OPT_STRING "c:fTdhvaeCHt:r:s:xSI" newReaderConfig = NULL; setToForeground = FALSE; HotPlug = FALSE; /* * test the version */ if (strcmp(PCSCLITE_VERSION_NUMBER, VERSION) != 0) { printf("BUILD ERROR: The release version number PCSCLITE_VERSION_NUMBER\n"); printf(" in pcsclite.h (%s) does not match the release version number\n", PCSCLITE_VERSION_NUMBER); printf(" generated in config.h (%s) (see configure.in).\n", VERSION); return EXIT_FAILURE; } /* * By default we create a daemon (not connected to any output) * so log to syslog to have error messages. */ DebugLogSetLogType(DEBUGLOG_SYSLOG_DEBUG); /* if the process is setuid or setgid it may have some restrictions */ limited_rights = (getgid() != getegid()) && (getuid() != 0); /* * Handle any command line arguments */ #ifdef HAVE_GETOPT_LONG while ((opt = getopt_long (argc, argv, OPT_STRING, long_options, &option_index)) != -1) { #else while ((opt = getopt (argc, argv, OPT_STRING)) != -1) { #endif switch (opt) { #ifdef HAVE_GETOPT_LONG case 0: if (strcmp(long_options[option_index].name, "force-reader-polling") == 0) HPForceReaderPolling = optarg ? abs(atoi(optarg)) : 1; break; #endif case 'c': if (limited_rights) { Log1(PCSC_LOG_CRITICAL, "Can't use a user specified config file"); return EXIT_FAILURE; } Log2(PCSC_LOG_INFO, "using new config file: %s", optarg); newReaderConfig = optarg; break; case 'f': setToForeground = TRUE; /* debug to stdout instead of default syslog */ DebugLogSetLogType(DEBUGLOG_STDOUT_DEBUG); Log1(PCSC_LOG_INFO, "pcscd set to foreground with debug send to stdout"); break; case 'T': DebugLogSetLogType(DEBUGLOG_STDOUT_COLOR_DEBUG); Log1(PCSC_LOG_INFO, "Force colored logs"); break; case 'd': DebugLogSetLevel(PCSC_LOG_DEBUG); break; case 'e': DebugLogSetLevel(PCSC_LOG_ERROR); break; case 'C': DebugLogSetLevel(PCSC_LOG_CRITICAL); break; case 'h': print_usage (argv[0]); return EXIT_SUCCESS; case 'v': print_version (); return EXIT_SUCCESS; case 'a': if (limited_rights) { Log1(PCSC_LOG_CRITICAL, "Can't log APDU (restricted)"); return EXIT_FAILURE; } (void)DebugLogSetCategory(DEBUG_CATEGORY_APDU); break; case 'H': /* debug to stdout instead of default syslog */ DebugLogSetLogType(DEBUGLOG_STDOUT_DEBUG); HotPlug = TRUE; break; case 't': customMaxThreadCounter = optarg ? atoi(optarg) : 0; if (limited_rights && (customMaxThreadCounter < PCSC_MAX_CONTEXT_THREADS)) customMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS; Log2(PCSC_LOG_INFO, "setting customMaxThreadCounter to: %d", customMaxThreadCounter); break; case 'r': customMaxReaderHandles = optarg ? atoi(optarg) : 0; if (limited_rights && (customMaxReaderHandles < PCSC_MAX_READER_HANDLES)) customMaxReaderHandles = PCSC_MAX_READER_HANDLES; Log2(PCSC_LOG_INFO, "setting customMaxReaderHandles to: %d", customMaxReaderHandles); break; case 's': customMaxThreadCardHandles = optarg ? atoi(optarg) : 0; if (limited_rights && (customMaxThreadCardHandles < PCSC_MAX_CONTEXT_CARD_HANDLES)) customMaxThreadCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES; Log2(PCSC_LOG_INFO, "setting customMaxThreadCardHandles to: %d", customMaxThreadCardHandles); break; case 'x': AutoExit = TRUE; Log2(PCSC_LOG_INFO, "Auto exit after %d seconds of inactivity", TIME_BEFORE_SUICIDE); break; case 'S': Add_Serial_In_Name = FALSE; break; case 'I': Add_Interface_In_Name = FALSE; break; default: print_usage (argv[0]); return EXIT_FAILURE; } } if (argv[optind]) { printf("Unknown option: %s\n", argv[optind]); print_usage(argv[0]); return EXIT_FAILURE; } /* * Check if systemd passed us any file descriptors */ rv = sd_listen_fds(0); if (rv > 1) { Log1(PCSC_LOG_CRITICAL, "Too many file descriptors received"); return EXIT_FAILURE; } else { if (rv == 1) { SocketActivated = TRUE; Log1(PCSC_LOG_INFO, "Started by systemd"); } else SocketActivated = FALSE; } /* * test the presence of /var/run/pcscd/pcscd.comm */ rv = stat(PCSCLITE_CSOCK_NAME, &fStatBuf); if (rv == 0) { pid_t pid; /* read the pid file to get the old pid and test if the old pcscd is * still running */ pid = GetDaemonPid(); if (pid != -1) { if (HotPlug) return SendHotplugSignal(); rv = kill(pid, 0); if (0 == rv) { Log1(PCSC_LOG_CRITICAL, "file " PCSCLITE_CSOCK_NAME " already exists."); Log2(PCSC_LOG_CRITICAL, "Another pcscd (pid: %d) seems to be running.", pid); return EXIT_FAILURE; } else if (ESRCH == errno) { /* the old pcscd is dead. make some cleanup */ clean_temp_files(); } else { /* permission denied or other error */ Log2(PCSC_LOG_CRITICAL, "kill failed: %s", strerror(errno)); return EXIT_FAILURE; } } else { if (HotPlug) { Log1(PCSC_LOG_CRITICAL, "file " PCSCLITE_RUN_PID " do not exist"); Log1(PCSC_LOG_CRITICAL, "Hotplug failed"); return EXIT_FAILURE; } } } else if (HotPlug) { Log1(PCSC_LOG_CRITICAL, "Hotplug failed: pcscd is not running"); return EXIT_FAILURE; } /* like in daemon(3): changes the current working directory to the * root ("/") */ r = chdir("/"); if (r < 0) { Log2(PCSC_LOG_CRITICAL, "chdir() failed: %s", strerror(errno)); return EXIT_FAILURE; } /* * If this is set to one the user has asked it not to fork */ if (!setToForeground) { int pid; int fd; if (pipe(pipefd) == -1) { Log2(PCSC_LOG_CRITICAL, "pipe() failed: %s", strerror(errno)); return EXIT_FAILURE; } pid = fork(); if (-1 == pid) { Log2(PCSC_LOG_CRITICAL, "fork() failed: %s", strerror(errno)); return EXIT_FAILURE; } /* like in daemon(3): redirect standard input, standard output * and standard error to /dev/null */ fd = open("/dev/null", O_RDWR); if (fd != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); /* do not close stdin, stdout or stderr */ if (fd > 2) close(fd); } if (pid) /* in the father */ { char buf; int ret; /* close write side */ close(pipefd[1]); /* wait for the son to write the return code */ ret = read(pipefd[0], &buf, 1); if (ret <= 0) return 2; close(pipefd[0]); /* exit code */ return buf; } else /* in the son */ { /* close read side */ close(pipefd[0]); } } /* * cleanly remove /var/run/pcscd/files when exiting * signal_trap() does just set a global variable used by the main loop */ (void)signal(SIGQUIT, signal_trap); (void)signal(SIGTERM, signal_trap); /* default kill signal & init round 1 */ (void)signal(SIGINT, signal_trap); /* sent by Ctrl-C */ /* exits on SIGALARM to allow pcscd to suicide if not used */ (void)signal(SIGALRM, signal_trap); /* * If PCSCLITE_IPC_DIR does not exist then create it */ { int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU; rv = mkdir(PCSCLITE_IPC_DIR, mode); if ((rv != 0) && (errno != EEXIST)) { Log2(PCSC_LOG_CRITICAL, "cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno)); return EXIT_FAILURE; } /* set mode so that the directory is world readable and * executable even is umask is restrictive * The directory containes files used by libpcsclite */ (void)chmod(PCSCLITE_IPC_DIR, mode); } /* * Allocate memory for reader structures */ rv = RFAllocateReaderSpace(customMaxReaderHandles); if (SCARD_S_SUCCESS != rv) at_exit(); #ifdef USE_SERIAL /* * Grab the information from the reader.conf */ if (newReaderConfig) { rv = RFStartSerialReaders(newReaderConfig); if (rv != 0) { Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig, strerror(errno)); at_exit(); } } else { rv = RFStartSerialReaders(PCSCLITE_CONFIG_DIR); if (rv == -1) at_exit(); } #endif Log1(PCSC_LOG_INFO, "pcsc-lite " VERSION " daemon ready."); /* * Record our pid to make it easier * to kill the correct pcscd * * Do not fork after this point or the stored pid will be wrong */ { int f; int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; f = open(PCSCLITE_RUN_PID, O_RDWR | O_CREAT, mode); if (f != -1) { char pid[PID_ASCII_SIZE]; ssize_t rr; (void)snprintf(pid, sizeof(pid), "%u\n", (unsigned) getpid()); rr = write(f, pid, strlen(pid) + 1); if (rr < 0) { Log2(PCSC_LOG_CRITICAL, "writing " PCSCLITE_RUN_PID " failed: %s", strerror(errno)); } (void)close(f); /* set mode so that the file is world readable even is umask is * restrictive * The file is used by libpcsclite */ (void)chmod(PCSCLITE_RUN_PID, mode); } else Log2(PCSC_LOG_CRITICAL, "cannot create " PCSCLITE_RUN_PID ": %s", strerror(errno)); } /* * post initialistion */ Init = FALSE; /* * Hotplug rescan */ (void)signal(SIGUSR1, signal_reload); /* * Initialize the comm structure */ if (SocketActivated) rv = ListenExistingSocket(SD_LISTEN_FDS_START + 0); else rv = InitializeSocket(); if (rv) { Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd."); at_exit(); } /* * Initialize the contexts structure */ rv = ContextsInitialize(customMaxThreadCounter, customMaxThreadCardHandles); if (rv == -1) { Log1(PCSC_LOG_CRITICAL, "Error initializing pcscd."); at_exit(); } (void)signal(SIGPIPE, SIG_IGN); (void)signal(SIGHUP, SIG_IGN); /* needed for Solaris. The signal is sent * when the shell is existed */ #if !defined(PCSCLITE_STATIC_DRIVER) && defined(USE_USB) /* * Set up the search for USB/PCMCIA devices */ rv = HPSearchHotPluggables(); #ifndef USE_SERIAL if (rv) at_exit(); #endif rv = HPRegisterForHotplugEvents(); if (rv) { Log1(PCSC_LOG_ERROR, "HPRegisterForHotplugEvents failed"); at_exit(); } RFWaitForReaderInit(); #endif /* * Set up the power management callback routine */ (void)PMRegisterForPowerEvents(); /* initialisation succeeded */ if (pipefd[1] >= 0) { char buf = 0; ssize_t rr; /* write a 0 (success) to father process */ rr = write(pipefd[1], &buf, 1); if (rr < 0) { Log2(PCSC_LOG_ERROR, "write() failed: %s", strerror(errno)); } close(pipefd[1]); } SVCServiceRunLoop(); Log1(PCSC_LOG_ERROR, "SVCServiceRunLoop returned"); return EXIT_FAILURE; } static void at_exit(void) { Log1(PCSC_LOG_INFO, "cleaning " PCSCLITE_IPC_DIR); clean_temp_files(); if (pipefd[1] >= 0) { char buf; ssize_t r; /* write the error code to father process */ buf = ExitValue; r = write(pipefd[1], &buf, 1); if (r < 0) { Log2(PCSC_LOG_ERROR, "write() failed: %s", strerror(errno)); } close(pipefd[1]); } exit(ExitValue); }
int main(int argc, char **argv) { int rv; char setToForeground; char HotPlug; char *newReaderConfig; struct stat fStatBuf; int opt; #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option long_options[] = { {"config", 1, 0, 'c'}, {"foreground", 0, 0, 'f'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"apdu", 0, 0, 'a'}, {"debug", 0, 0, 'd'}, {"info", 0, 0, 0}, {"error", 0, 0, 'e'}, {"critical", 0, 0, 'C'}, {"hotplug", 0, 0, 'H'}, {"force-reader-polling", optional_argument, 0, 0}, {0, 0, 0, 0} }; #endif #define OPT_STRING "c:fdhvaeCH" rv = 0; newReaderConfig = NULL; setToForeground = 0; HotPlug = 0; globalArgv = argv; /* * test the version */ if (strcmp(PCSCLITE_VERSION_NUMBER, VERSION) != 0) { printf("BUILD ERROR: The release version number PCSCLITE_VERSION_NUMBER\n"); printf(" in pcsclite.h (%s) does not match the release version number\n", PCSCLITE_VERSION_NUMBER); printf(" generated in config.h (%s) (see configure.in).\n", VERSION); return EXIT_FAILURE; } /* * By default we create a daemon (not connected to any output) * The log will go to wherever securityd log output goes. */ DebugLogSetLogType(DEBUGLOG_NO_DEBUG); /* * Handle any command line arguments */ #ifdef HAVE_GETOPT_LONG while ((opt = getopt_long (argc, argv, OPT_STRING, long_options, &option_index)) != -1) { #else while ((opt = getopt (argc, argv, OPT_STRING)) != -1) { #endif switch (opt) { #ifdef HAVE_GETOPT_LONG case 0: if (strcmp(long_options[option_index].name, "force-reader-polling") == 0) HPForceReaderPolling = optarg ? abs(atoi(optarg)) : 1; break; #endif case 'c': Log2(PCSC_LOG_INFO, "using new config file: %s", optarg); newReaderConfig = optarg; break; case 'f': setToForeground = 1; /* debug to stderr instead of default syslog */ Log1(PCSC_LOG_INFO, "pcscd set to foreground with debug send to stderr"); break; case 'd': DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); DebugLogSetLevel(PCSC_LOG_DEBUG); break; case 'e': DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); DebugLogSetLevel(PCSC_LOG_ERROR); break; case 'C': DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); DebugLogSetLevel(PCSC_LOG_CRITICAL); break; case 'h': print_usage (argv[0]); return EXIT_SUCCESS; case 'v': print_version (); return EXIT_SUCCESS; case 'a': DebugLogSetCategory(DEBUG_CATEGORY_APDU); break; case 'H': /* debug to stderr instead of default syslog */ DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); HotPlug = 1; break; default: print_usage (argv[0]); return EXIT_FAILURE; } } if (argv[optind]) { printf("Unknown option: %s\n\n", argv[optind]); print_usage(argv[0]); return EXIT_SUCCESS; } /* If this run of pcscd has the hotplug option, just send a signal to the running one and exit */ if (HotPlug) return ProcessHotplugRequest(); /* * test the presence of /var/run/pcsc.comm */ rv = SYS_Stat(PCSCLITE_CSOCK_NAME, &fStatBuf); if (rv == 0) { #ifdef USE_RUN_PID pid_t pid; /* read the pid file to get the old pid and test if the old pcscd is * still running */ pid = GetDaemonPid(); if (pid != -1) { if (kill(pid, 0) == 0) { Log2(PCSC_LOG_CRITICAL, "Another pcscd (pid: %d) seems to be running.", pid); Log1(PCSC_LOG_CRITICAL, "Remove " USE_RUN_PID " if pcscd is not running to clear this message."); return EXIT_FAILURE; } else /* the old pcscd is dead. Do some cleanup */ clean_temp_files(); } #else { Log1(PCSC_LOG_CRITICAL, "file " PCSCLITE_CSOCK_NAME " already exists."); Log1(PCSC_LOG_CRITICAL, "Maybe another pcscd is running?"); Log1(PCSC_LOG_CRITICAL, "Remove " PCSCLITE_CSOCK_NAME "if pcscd is not running to clear this message."); return EXIT_FAILURE; } #endif } /* * If this is set to one the user has asked it not to fork */ if (!setToForeground) { if (SYS_Daemon(0, 0)) Log2(PCSC_LOG_CRITICAL, "SYS_Daemon() failed: %s", strerror(errno)); } /* * cleanly remove /tmp/pcsc when exiting */ signal(SIGQUIT, signal_trap); signal(SIGTERM, signal_trap); signal(SIGINT, signal_trap); signal(SIGHUP, signal_trap); #ifdef USE_RUN_PID /* * Record our pid to make it easier * to kill the correct pcscd */ { FILE *f; if ((f = fopen(USE_RUN_PID, "wb")) != NULL) { fprintf(f, "%u\n", (unsigned) getpid()); fclose(f); } } #endif /* * If PCSCLITE_IPC_DIR does not exist then create it */ rv = SYS_Stat(PCSCLITE_IPC_DIR, &fStatBuf); if (rv < 0) { rv = SYS_Mkdir(PCSCLITE_IPC_DIR, S_ISVTX | S_IRWXO | S_IRWXG | S_IRWXU); if (rv != 0) { Log2(PCSC_LOG_CRITICAL, "cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno)); return EXIT_FAILURE; } } /* cleanly remove /var/run/pcsc.* files when exiting */ if (atexit(at_exit)) Log2(PCSC_LOG_CRITICAL, "atexit() failed: %s", strerror(errno)); /* * Allocate memory for reader structures */ RFAllocateReaderSpace(); /* Grab the information from the reader.conf. If a file has been specified and there is any error, consider it fatal. If no file was explicitly specified, ignore if file not present. DBUpdateReaders returns: 1 if config file can't be opened -1 if config file is broken 0 if all good We skip this step if running in 64 bit mode, as serial readers are considered legacy code. */ rv = RFStartSerialReaders(newReaderConfig?newReaderConfig:PCSCLITE_READER_CONFIG); if (rv == -1) { Log3(PCSC_LOG_CRITICAL, "invalid file %s: %s", newReaderConfig, strerror(errno)); at_exit(); } else if ((rv == 1) && newReaderConfig) { Log3(PCSC_LOG_CRITICAL, "file %s can't be opened: %s", newReaderConfig, strerror(errno)); at_exit(); } /* * Set the default globals */ g_rgSCardT0Pci.dwProtocol = SCARD_PROTOCOL_T0; g_rgSCardT1Pci.dwProtocol = SCARD_PROTOCOL_T1; g_rgSCardRawPci.dwProtocol = SCARD_PROTOCOL_RAW; Log1(PCSC_LOG_INFO, "pcsc-lite " VERSION " daemon ready."); /* * post initialistion */ Init = 0; /* * signal_trap() does just set a global variable used by the main loop */ signal(SIGQUIT, signal_trap); signal(SIGTERM, signal_trap); signal(SIGINT, signal_trap); signal(SIGHUP, signal_trap); signal(SIGUSR1, signal_reload); SVCServiceRunLoop(); Log1(PCSC_LOG_ERROR, "SVCServiceRunLoop returned"); return EXIT_FAILURE; } void at_exit(void) { Log1(PCSC_LOG_INFO, "cleaning " PCSCLITE_IPC_DIR); clean_temp_files(); SYS_Exit(EXIT_SUCCESS); }