Esempio n. 1
0
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);
}
Esempio n. 2
0
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);
}