/****************************************************************** Title : pcscdaemon.c Package: PC/SC Lite Author : David Corcoran Date : 10/24/99 License: Copyright (C) 1999 David Corcoran Purpose: This is the main pcscd daemon. $Id: pcscdaemon.c,v 1.2 2002/04/25 22:44:10 stuartha Exp $ ********************************************************************/ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #endif #include "wintypes.h" #include "pcsclite.h" #include "winscard_msg.h" #include "winscard_svc.h" #include "sys_generic.h" #include "thread_generic.h" #include "hotplug.h" #include "debuglog.h" #include "readerfactory.h" #include "configfile.h" #ifdef PCSC_TARGET_OSX #include "powermgt_generic.h" #endif static char AraKiri = 0; static char Init = 1; extern int errno; /* * Some internal functions */ void SVCServiceRunLoop(); void SVCClientCleanup(psharedSegmentMsg); void at_exit(void); void signal_trap(int); void print_version (void); void print_usage (char const * const); PCSCLITE_MUTEX usbNotifierMutex; /* * Cleans up messages still on the queue when a client dies */ void SVCClientCleanup(psharedSegmentMsg msgStruct) { /* * May be implemented in future releases */ } /* * The Message Queue Listener function */ void SVCServiceRunLoop() { char errMessage[200]; sharedSegmentMsg msgStruct; int currHandle, rsp; currHandle = 0, rsp = 0; /* * Initialize the comm structure */ rsp = SHMInitializeCommonSegment(); if (rsp == -1) { DebugLogA("SVCServiceRunLoop: Error initializing pcscd."); exit(-1); } /* * Solaris sends a SIGALRM and it is annoying */ signal(SIGALRM, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGHUP, SIG_IGN); /* needed for Solaris. The signal is sent * when the shell is existed */ /* * This function always returns zero */ rsp = SYS_MutexInit(&usbNotifierMutex); /* * Set up the search for USB/PCMCIA devices */ HPSearchHotPluggables(); /* * Set up the power management callback routine for OS X */ #ifdef PCSC_TARGET_OSX PMRegisterForPowerEvents(); #endif while (1) { switch (rsp = SHMProcessEvents(&msgStruct, 0)) { case 0: if (msgStruct.mtype == CMD_CLIENT_DIED) { /* * Clean up the dead client */ SYS_MutexLock(&usbNotifierMutex); MSGCleanupClient(&msgStruct); SYS_MutexUnLock(&usbNotifierMutex); snprintf(errMessage, sizeof(errMessage), "%s%d%s", "SVCServiceRun: Client ", msgStruct.request_id, " has disappeared."); DebugLogB("%s", errMessage); } else { continue; } break; case 1: if (msgStruct.mtype == CMD_FUNCTION) { /* * Command must be found */ SYS_MutexLock(&usbNotifierMutex); MSGFunctionDemarshall(&msgStruct); rsp = SHMMessageSend(&msgStruct, msgStruct.request_id, PCSCLITE_SERVER_ATTEMPTS); SYS_MutexUnLock(&usbNotifierMutex); } else { continue; } break; case 2: // timeout in SHMProcessEvents(): do nothing // this is used to catch the Ctrl-C signal at some time when // nothing else happens break; case -1: DebugLogA("SVCServiceRun: Error in SHMProcessEvents"); break; default: DebugLogB("SVCServiceRun: SHMProcessEvents unknown retval: %d", rsp); break; } if (AraKiri) RFCleanupReaders(1); } } int main(int argc, char **argv) { int rv; char setToForeground; char *newReaderConfig; struct stat fStatBuf; int opt; #ifdef HAVE_GETOPT_LONG int option_index; static struct option long_options[] = { {"config", 1, 0, 'c'}, {"foreground", 0, 0, 'f'}, {"debug", 1, 0, 'd'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {0, 0, 0, 0} }; #endif rv = 0; newReaderConfig = 0; setToForeground = 0; /* * 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 1; } /* * Handle any command line arguments */ #ifdef HAVE_GETOPT_LONG while ((opt = getopt_long (argc, argv, "c:fd:hv", long_options, &option_index)) != -1) { #else while ((opt = getopt (argc, argv, "c:fd:hv")) != -1) { #endif switch (opt) { case 'c': DebugLogB("main: using new config file: %s", optarg); newReaderConfig = optarg; break; case 'f': DebugLogA("main: pcscd set to foreground"); setToForeground = 1; break; case 'd': if (strncmp(optarg, "syslog", PCSCLITE_MAX_COMSIZE) == 0) DebugLogSetLogType(DEBUGLOG_SYSLOG_DEBUG); else if (strncmp(optarg, "stderr", PCSCLITE_MAX_COMSIZE) == 0) { DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); DebugLogA("main: debug messages to stderr"); setToForeground = 1; } else if (strncmp(optarg, "stdout", PCSCLITE_MAX_COMSIZE) == 0) { DebugLogSetLogType(DEBUGLOG_STDOUT_DEBUG); DebugLogA("main: debug messages to stdout"); setToForeground = 1; } break; case 'h': print_usage (argv[0]); return 0; case 'v': print_version (); return 0; default: print_usage (argv[0]); return 1; } } /* * test the presence of /tmp/pcsc */ rv = SYS_Stat(PCSCLITE_IPC_DIR, &fStatBuf); if (rv == 0) { DebugLogA("main: Directory " PCSCLITE_IPC_DIR " already exists."); DebugLogA("Maybe another pcscd is running?"); DebugLogA("Remove " PCSCLITE_IPC_DIR " if pcscd is not running"); DebugLogA("to clear this message"); return 1; } /* * If this is set to one the user has asked it not to fork */ if (setToForeground == 0) { #ifndef HAVE_DAEMON switch (SYS_Fork()) { case -1: return (-1); case 0: break; default: return (0); } if (SYS_CloseFile(0)) DebugLogB("main: SYS_CloseFile(0) failed: %s", strerror(errno)); if (SYS_CloseFile(1)) DebugLogB("main: SYS_CloseFile(1) failed: %s", strerror(errno)); if (SYS_CloseFile(2)) DebugLogB("main: SYS_CloseFile(2) failed: %s", strerror(errno)); if (SYS_Chdir("/")) DebugLogB("main: SYS_Chdir() failed: %s", strerror(errno)); #else if (SYS_Daemon(0, 0)) DebugLogB("main: SYS_Daemon() failed: %s", strerror(errno)); #endif } /* * 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 /* * Create the /tmp/pcsc directory and chmod it */ rv = SYS_Mkdir(PCSCLITE_IPC_DIR, S_ISVTX | S_IRWXO | S_IRWXG | S_IRWXU); if (rv != 0) { DebugLogB("main: cannot create " PCSCLITE_IPC_DIR ": %s", strerror(errno)); return 1; } rv = SYS_Chmod(PCSCLITE_IPC_DIR, S_ISVTX | S_IRWXO | S_IRWXG | S_IRWXU); if (rv != 0) { DebugLogB("main: cannot chmod " PCSCLITE_IPC_DIR ": %s", strerror(errno)); return 1; } /* cleanly remove /tmp/pcsc when exiting */ if (atexit(at_exit)) DebugLogB("main: atexit() failed: %s", strerror(errno)); /* * Allocate memory for reader structures */ RFAllocateReaderSpace(PCSCLITE_MAX_CONTEXTS); /* * Grab the information from the reader.conf */ if (newReaderConfig) { rv = DBUpdateReaders(newReaderConfig); if (rv != 0) { DebugLogB("main: invalid file %s\n", newReaderConfig); at_exit(); return 1; } } else { rv = DBUpdateReaders(PCSCLITE_READER_CONFIG); if (rv == 1) { DebugLogA("main: warning: no reader.conf found\n"); /* * Token error in file */ } else if (rv == -1) { at_exit(); return 1; } } /* * Set the default globals */ g_rgSCardT0Pci.dwProtocol = SCARD_PROTOCOL_T0; g_rgSCardT1Pci.dwProtocol = SCARD_PROTOCOL_T1; g_rgSCardRawPci.dwProtocol = SCARD_PROTOCOL_RAW; DebugLogA("main: pcsc-lite 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); SVCServiceRunLoop(); DebugLogA("pcscdaemon.c: main: SVCServiceRunLoop returned"); return 1; } void at_exit(void) { int rv; DebugLogA("at_exit: cleaning " PCSCLITE_IPC_DIR); rv = SYS_Unlink(PCSCLITE_PUBSHM_FILE); if (rv != 0) DebugLogB("main: Cannot unlink " PCSCLITE_PUBSHM_FILE ": %s", strerror(errno)); rv = SYS_Unlink(PCSCLITE_CSOCK_NAME); if (rv != 0) DebugLogB("main: Cannot unlink " PCSCLITE_CSOCK_NAME ": %s", strerror(errno)); rv = SYS_Rmdir(PCSCLITE_IPC_DIR); if (rv != 0) DebugLogB("main: Cannot rmdir " PCSCLITE_IPC_DIR ": %s", strerror(errno)); #ifdef USE_RUN_PID rv = SYS_Unlink(USE_RUN_PID); if (rv != 0) DebugLogB("main: Cannot unlink " USE_RUN_PID ": %s", strerror(errno)); #endif SYS_Exit(1); } void signal_trap(int sig) { // the signal handler is called several times for the same Ctrl-C if (AraKiri == 0) { DebugLogA("Preparing for suicide"); AraKiri = 1; // if still in the init/loading phase the AraKiri will not be // seen by the main event loop if (Init) { DebugLogA("Suicide during init"); at_exit(); } } } void print_version (void) { printf("%s version %s.\n", PACKAGE, VERSION); printf("Copyright (C) 1999-2002 by David Corcoran .\n"); printf("Report bugs to .\n"); } void print_usage (char const * const progname) { printf("Usage: %s [-c file] [-f] [-d output] [-v] [-h]\n", progname); printf("Options:\n"); #ifdef HAVE_GETOPT_LONG printf(" -c, --config path to reader.conf\n"); printf(" -f, --foreground run in foreground (no daemon)\n"); printf(" -d, --debug display debug messages. Output may be:\n"); printf(" stdout (imply -f), stderr (imply -f),\n"); printf(" or syslog\n"); printf(" -h, --help display usage information\n"); printf(" -v, --version display the program version number\n"); #else printf(" -c path to reader.conf\n"); printf(" -f run in foreground (no daemon)\n"); printf(" -d display debug messages. Output may be:\n"); printf(" stdout (imply -f), stderr (imply -f),\n"); printf(" or syslog\n"); printf(" -h display usage information\n"); printf(" -v display the program version number\n"); #endif }