/* * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved. * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please * obtain a copy of the License at http://www.apple.com/publicsource and * read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please * see the License for the specific language governing rights and * limitations under the License. */ /****************************************************************** 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.4 2004/12/07 01:15:43 mb 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(request_object *msgStruct); void at_exit(void); void clean_temp_files(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(request_object *msgStruct) { /* * May be implemented in future releases */ } /* * The Message Queue Listener function */ void SVCServiceRunLoop() { char errMessage[200]; request_object request = {}; int currHandle, rsp; currHandle = 0, rsp = 0; /* * Initialize the comm structure */ rsp = MSGServerSetupCommonChannel(); 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(); HPRegisterForHotplugEvents(); #ifdef PCSC_TARGET_OSX /* * Set up the power management callback routine for OS X */ PMRegisterForPowerEvents(); #endif while (1) { switch (rsp = MSGServerProcessEvents(&request, 0)) { case 0: if (request.mtype == CMD_CLIENT_DIED) { /* * Clean up the dead client */ SYS_MutexLock(&usbNotifierMutex); MSGCleanupClient(&request); SYS_MutexUnLock(&usbNotifierMutex); snprintf(errMessage, sizeof(errMessage), "%s%d%s", "SVCServiceRun: Client ", request.socket, " has disappeared."); DebugLogB("%s", errMessage); } else { continue; } break; case 1: if (request.mtype == CMD_FUNCTION) { reply_object reply = {0,}; reply_header *replyh = &reply.message.header; reply.additional_data = NULL; /* * Command must be found */ SYS_MutexLock(&usbNotifierMutex); rsp = MSGFunctionDemarshall(&request, &reply); /* Command is bogus, ignore it, we should probably also get rid of this client. */ if (rsp) continue; /* note that fields in reply are in byte network order for sending */ if (!reply.additional_data) replyh->additional_data_size = 0; rsp = MSGSendData(request.socket, PCSCLITE_SERVER_ATTEMPTS, replyh, ntohl(replyh->size)); if (!rsp && replyh->additional_data_size) { rsp = MSGSendData(request.socket, PCSCLITE_SERVER_ATTEMPTS, reply.additional_data, ntohl(replyh->additional_data_size)); } if (reply.additional_data) { if (replyh->additional_data_size) memset(reply.additional_data, 0, ntohl(replyh->additional_data_size)); free(reply.additional_data); } SYS_MutexUnLock(&usbNotifierMutex); } else { continue; } break; case 2: // timeout in MSGServerProcessEvents(): 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 MSGServerProcessEvents"); break; default: DebugLogB("SVCServiceRun: MSGServerProcessEvents returned: %d", rsp); break; } if (request.additional_data) { if (request.message.header.additional_data_size) memset(request.additional_data, 0, request.message.header.additional_data_size); free(request.additional_data); request.additional_data = NULL; } 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'}, {"apdu", 0, 0, 'a'}, {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; } /* * log to stderr by default */ DebugLogSetLogType(DEBUGLOG_STDERR_DEBUG); /* * Handle any command line arguments */ #ifdef HAVE_GETOPT_LONG while ((opt = getopt_long (argc, argv, "c:fd:hva", long_options, &option_index)) != -1) { #else while ((opt = getopt (argc, argv, "c:fd:hva")) != -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; } else { DebugLogB("unknown debug argument: %s", optarg); print_usage (argv[0]); return 1; } break; case 'h': print_usage (argv[0]); return 0; case 'v': print_version (); return 0; case 'a': DebugLogSetCategory(DEBUG_CATEGORY_APDU); break; default: print_usage (argv[0]); return 1; } } /* * test the presence of /tmp/pcsc */ rv = SYS_Stat(PCSCLITE_IPC_DIR, &fStatBuf); if (rv == 0) { #ifdef USE_RUN_PID // read the pid file to get the old pid and test if the old pcscd is // still running FILE *f; // pids are only 15 bits but 4294967296 (32 bits in case of a new // system use it) is on 10 bytes #define PID_ASCII_SIZE 11 char pid_ascii[PID_ASCII_SIZE]; int pid; if ((f = fopen(USE_RUN_PID, "rb")) != NULL) { fgets(pid_ascii, PID_ASCII_SIZE, f); fclose(f); pid = atoi(pid_ascii); if (kill(pid, 0) == 0) { DebugLogA("main: Directory " PCSCLITE_IPC_DIR " already exists."); DebugLogA("Another pcscd seems to be running"); return 1; } else // the old pcscd is dead. make some cleanup clean_temp_files(); } else { DebugLogA("main: Directory " PCSCLITE_IPC_DIR " already exists."); DebugLogA("Maybe another pcscd is running?"); DebugLogA("Can't read process pid from " USE_RUN_PID); DebugLogA("Remove " PCSCLITE_IPC_DIR " if pcscd is not running"); DebugLogA("to clear this message."); return 1; } #else 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; #endif } /* * 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_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | 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_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | 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) { DebugLogA("at_exit: cleaning " PCSCLITE_IPC_DIR); clean_temp_files(); SYS_Exit(1); } void clean_temp_files(void) { int rv; 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 } 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(" -a, --apdu log APDU commands and results\n"); printf(" -c, --config path to reader.conf\n"); printf(" -f, --foreground run in foreground (no daemon)\n"); printf(" -d, --debug output 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(" -a log APDU commands and results\n"); 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 }