1 /*
2 Generate events on card status change
3 Copyrigt (C) 2005 Juan Antonio Martinez <jonsito@teleline.es>
4 Based on pcsc_scan tool by Ludovic Rousseau <ludovic.rousseau@free.fr>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22 /* $Id$ */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33
34 #include <pcsclite.h>
35 #include <wintypes.h>
36 #include <winscard.h>
37
38 #include "config.h"
39 #include "../scconf/scconf.h"
40 #include "../common/debug.h"
41 #include "../common/error.h"
42
43 #ifndef HAVE_DAEMON
44 int daemon(int nochdir, int noclose);
45 #endif
46
47 #ifndef TRUE
48 #define TRUE 1
49 #define FALSE 0
50 #endif
51
52 #define DEF_TIMEOUT 1000 /* 1 second timeout */
53 #define DEF_CONFIG_FILE CONFDIR "/card_eventmgr.conf"
54
55 #define ONERROR_IGNORE 0
56 #define ONERROR_RETURN 1
57 #define ONERROR_QUIT 2
58
59 int timeout;
60 int timeout_count;
61 int timeout_limit;
62 int daemonize;
63 int debug;
64 const char *cfgfile;
65 scconf_context *ctx = NULL;
66 const scconf_block *root;
67 SCARDCONTEXT hContext;
68 char *pidfile = NULL;
69 char AraKiri = FALSE;
70
thats_all_folks(void)71 static void thats_all_folks(void) {
72 int rv;
73 DBG("Exitting");
74 /* We try to leave things as clean as possible */
75 rv = SCardReleaseContext(hContext);
76 if (rv != SCARD_S_SUCCESS) {
77 DBG1("SCardReleaseContext: %X", rv);
78 }
79
80 /* free configuration context */
81 if (ctx)
82 scconf_free(ctx);
83 }
84
85 extern char **environ;
my_system(char * command)86 static int my_system(char *command) {
87 int pid, status;
88 if (!command) return 1;
89 pid = fork();
90 if (pid == -1) return -1;
91 if (pid == 0) {
92 char *argv[4];
93 argv[0] = "/bin/sh";
94 argv[1] = "-c";
95 argv[2] = command;
96 argv[3] = 0;
97 execve("/bin/sh", argv, environ);
98 exit(127);
99 }
100 do {
101 if (waitpid(pid, &status, 0) == -1) {
102 if (errno != EINTR) return -1;
103 } else return status;
104 } while(1);
105 }
106
execute_event(const char * action)107 static int execute_event (const char *action) {
108 int onerr;
109 const char *onerrorstr;
110 const scconf_list *actionlist;
111 scconf_block **blocklist, *myblock;
112 blocklist = scconf_find_blocks(ctx,root,"event",action);
113 if (!blocklist) {
114 DBG("Event block list not found");
115 return -1;
116 }
117 myblock=blocklist[0];
118 free(blocklist);
119 blocklist = NULL;
120 if (!myblock) {
121 DBG1("Event item not found: '%s'",action);
122 return -1;
123 }
124 onerrorstr = scconf_get_str(myblock,"on_error","ignore");
125 if(!strcmp(onerrorstr,"ignore")) onerr = ONERROR_IGNORE;
126 else if(!strcmp(onerrorstr,"return")) onerr = ONERROR_RETURN;
127 else if(!strcmp(onerrorstr,"quit")) onerr = ONERROR_QUIT;
128 else {
129 onerr = ONERROR_IGNORE;
130 DBG1("Invalid onerror value: '%s'. Assumed 'ignore'",onerrorstr);
131 }
132 /* search actions */
133 actionlist = scconf_find_list(myblock,"action");
134 if (!actionlist) {
135 DBG1("No action list for event '%s'",action);
136 return 0;
137 }
138 DBG1("Onerror is set to: '%s'",onerrorstr);
139 while (actionlist) {
140 int res;
141 char *action_cmd= actionlist->data;
142 DBG1("Executiong action: '%s'",action_cmd);
143 /*
144 there are some security issues on using system() in
145 setuid/setgid programs. so we will use an alternate function
146 */
147 /* res=system(action_cmd); */
148 res = my_system(action_cmd);
149 actionlist=actionlist->next;
150 /* evaluate return and take care on "onerror" value */
151 DBG2("Action '%s' returns %d",action_cmd, res);
152 if (!res) continue;
153 switch(onerr) {
154 case ONERROR_IGNORE: continue;
155 case ONERROR_RETURN: return 0;
156 case ONERROR_QUIT: thats_all_folks();
157 exit(0);
158 default: DBG("Invalid onerror value");
159 return -1;
160 }
161 }
162 return 0;
163 }
164
parse_config_file(void)165 static int parse_config_file(void) {
166 ctx = scconf_new(cfgfile);
167 if (!ctx) {
168 DBG("Error creating conf context");
169 return -1;
170 }
171 if ( scconf_parse(ctx) <= 0 ) {
172 DBG2("Error parsing file '%s': %s",cfgfile, ctx->errmsg);
173 return -1;
174 }
175 /* now parse options */
176 root = scconf_find_block(ctx, NULL, "card_eventmgr");
177 if (!root) {
178 DBG1("card_eventmgr block not found in config: '%s'",cfgfile);
179 return -1;
180 }
181 debug = scconf_get_bool(root,"debug",debug);
182 daemonize = scconf_get_bool(root,"daemon",daemonize);
183 timeout = scconf_get_int(root,"timeout",timeout);
184 timeout_limit = scconf_get_int(root,"timeout_limit",0);
185 if (debug) set_debug_level(1);
186 return 0;
187 }
188
parse_args(int argc,char * argv[])189 static int parse_args(int argc, char *argv[]) {
190 int i;
191 timeout = DEF_TIMEOUT;
192 timeout_limit = 0;
193 debug = 0;
194 daemonize = 0;
195 cfgfile = DEF_CONFIG_FILE;
196 /* first of all check whether debugging should be enabled */
197 for (i = 0; i < argc; i++) {
198 if (! strcmp("debug", argv[i])) set_debug_level(1);
199 }
200 /* try to find a configuration file entry */
201 for (i = 0; i < argc; i++) {
202 if (strstr(argv[i],"config_file=") ) {
203 cfgfile=1+strchr(argv[i],'=');
204 break;
205 }
206 }
207 /* parse configuration file */
208 if ( parse_config_file()<0) {
209 fprintf(stderr,"Error parsing configuration file %s\n",cfgfile);
210 exit(-1);
211 }
212
213 /* and now re-parse command line to take precedence over cfgfile */
214 for (i = 1; i < argc; i++) {
215 if (strcmp("daemon", argv[i]) == 0) {
216 daemonize=1;
217 continue;
218 }
219 if (strcmp("nodaemon", argv[i]) == 0) {
220 daemonize=0;
221 continue;
222 }
223 if (strcmp("kill", argv[i]) == 0) {
224 AraKiri=TRUE;
225 continue;
226 }
227 if (strstr(argv[i],"timeout=") ) {
228 sscanf(argv[i],"timeout=%d",&timeout);
229 continue;
230 }
231 if (strstr(argv[i],"timeout_limit=") ) {
232 sscanf(argv[i],"timeout_limit=%d",&timeout_limit);
233 continue;
234 }
235 if (strstr(argv[i],"pidfile=") ) {
236 pidfile = strchr(argv[i],'=') +1;
237 continue;
238 }
239 if (strstr(argv[i],"debug") ) {
240 continue; /* already parsed: skip */
241 }
242 if (strstr(argv[i],"nodebug") ) {
243 continue; /* already parsed: skip */
244 }
245 if (strstr(argv[i],"config_file=") ) {
246 continue; /* already parsed: skip */
247 }
248 fprintf(stderr,"unknown option %s\n",argv[i]);
249 /* arriving here means syntax error */
250 fprintf(stderr,"Usage %s [[no]debug] [[no]daemon] [timeout=<timeout>] [timeout_limit=<limit>] [config_file=<file>] [kill] [pidfile=<file>]\n",argv[0]);
251 fprintf(stderr,"Defaults: debug=0 daemon=0 timeout=%d (ms) timeout_limit=0 (none) config_file=%s\n",DEF_TIMEOUT,DEF_CONFIG_FILE );
252 exit(1);
253 } /* for */
254 /* end of config: return */
255 return 0;
256 }
257
read_pidfile(char * filename)258 static pid_t read_pidfile(char *filename)
259 {
260 FILE *fd;
261 pid_t pid;
262 long temp = 0;
263
264 fd = fopen(filename, "r");
265 if (NULL == fd)
266 {
267 DBG2("Can't read pidfile %s: %s", filename, strerror(errno));
268 return 0;
269 }
270
271 if (fscanf(fd, "%ld", &temp) != 1)
272 DBG2("Can't parse pidfile %s: %s", filename, strerror(errno));
273
274 pid = temp;
275
276 fclose(fd);
277
278 return pid;
279 }
280
remove_pidfile(char * filename)281 static void remove_pidfile(char *filename)
282 {
283 if (unlink(filename))
284 DBG2("Can't unlink pidfile %s: %s", filename, strerror(errno));
285 }
286
create_pidfile(char * filename)287 static void create_pidfile(char *filename)
288 {
289 int fd;
290 char tmp[20];
291
292 fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0644);
293 if (fd < 0)
294 {
295 DBG2("Can't create pidfile %s: %s", filename, strerror(errno));
296 return;
297 }
298
299 snprintf(tmp, sizeof(tmp)-1, "%d\n", getpid());
300 tmp[sizeof(tmp)-1] = '\0';
301
302 if (write(fd, tmp, strlen(tmp)) != strlen(tmp))
303 DBG2("Can't write pidfile %s: %s", filename, strerror(errno));
304
305 close(fd);
306 }
307
signal_trap(int sig)308 static void signal_trap(int sig)
309 {
310 (void)sig;
311 if (FALSE == AraKiri)
312 {
313 DBG("Preparing to suicide");
314 AraKiri = TRUE;
315 }
316 }
317
main(int argc,char * argv[])318 int main(int argc, char *argv[]) {
319 int current_reader;
320 LONG rv;
321 SCARD_READERSTATE *rgReaderStates_t = NULL;
322 DWORD dwReaders, dwReadersOld;
323 LPSTR mszReaders = NULL;
324 char *ptr, **readers = NULL;
325 int nbReaders, i;
326 int first_loop = TRUE;
327
328 parse_args(argc,argv);
329
330 /* AraKiri is set if kill argument is passed */
331 if (AraKiri)
332 {
333 /* we are asked to kill the previous pkcs11_eventmgr */
334 if (pidfile)
335 {
336 pid_t pid = read_pidfile(pidfile);
337 if (pid > 0)
338 {
339 DBG1("Killing process: %ld", (long)pid);
340 kill(pid, SIGQUIT);
341 }
342 else
343 DBG("Invalid pid");
344 }
345 else
346 DBG("You need to specify a pidfile");
347 return 0;
348 }
349
350 /* put my self into background if flag is set */
351 if (daemonize) {
352 DBG("Going to be daemon...");
353 if ( daemon(0,debug)<0 ) {
354 DBG1("Error in daemon() call: %s", strerror(errno));
355 goto end;
356 }
357 }
358
359 /* establish pc/sc handle _after_ possible fork */
360 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
361 if (rv != SCARD_S_SUCCESS) {
362 DBG1("SCardEstablishContext: Cannot Connect to Resource Manager %lX", rv);
363 if (ctx)
364 scconf_free(ctx);
365 return 1;
366 }
367
368 signal(SIGINT, signal_trap);
369 signal(SIGQUIT, signal_trap);
370 signal(SIGTERM, signal_trap);
371
372 if (pidfile)
373 create_pidfile(pidfile);
374
375 get_readers:
376 /* free memory possibly allocated in a previous loop */
377 /* free() already check if pt is null, so no check needed */
378 free(readers);
379 readers = NULL;
380 free(rgReaderStates_t);
381 rgReaderStates_t = NULL;
382
383 /* Retrieve the available readers list.
384 *
385 * 1. Call with a null buffer to get the number of bytes to allocate
386 * 2. malloc the necessary storage
387 * 3. call with the real allocated buffer
388 */
389 DBG("Scanning present readers");
390 rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
391 if (rv != SCARD_S_SUCCESS) {
392 DBG1("SCardListReader: %lX", rv);
393 }
394 dwReadersOld = dwReaders;
395
396 /* if non NULL we came back so free first */
397 free(mszReaders);
398 mszReaders = NULL;
399
400 mszReaders = malloc(sizeof(char)*dwReaders);
401 if (mszReaders == NULL) {
402 DBG("malloc: not enough memory");
403 goto end;
404 }
405
406 rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
407 if (rv != SCARD_S_SUCCESS) {
408 DBG1("SCardListReader: %lX", rv);
409 }
410
411 /* Extract readers from the null separated string and get the total
412 * number of readers */
413 nbReaders = 0;
414 ptr = mszReaders;
415 while (*ptr != '\0') {
416 ptr += strlen(ptr)+1;
417 nbReaders++;
418 }
419
420 if (nbReaders == 0) {
421 /* exit if no reader is present at startup */
422 if (first_loop) {
423 printf("%s: No reader present, exiting\n", argv[0]);
424 goto end;
425 }
426
427 DBG("Waiting for the first reader...");
428 while ((SCardListReaders(hContext, NULL, NULL, &dwReaders)
429 == SCARD_S_SUCCESS) && (dwReaders == dwReadersOld))
430 sleep(1);
431 DBG("found one");
432 goto get_readers;
433 }
434
435 /* allocate the readers table */
436 readers = calloc(nbReaders, sizeof(char *));
437 if (! readers) {
438 DBG("Not enough memory for readers table");
439 goto end;
440 }
441
442 /* fill the readers table */
443 nbReaders = 0;
444 ptr = mszReaders;
445 while (*ptr != '\0') {
446 DBG2("%d: %s", nbReaders, ptr);
447 readers[nbReaders] = ptr;
448 ptr += strlen(ptr)+1;
449 nbReaders++;
450 }
451
452 /* allocate the ReaderStates table */
453 rgReaderStates_t = calloc(nbReaders, sizeof(* rgReaderStates_t));
454 if (! rgReaderStates_t) {
455 DBG("Not enough memory for readers states");
456 goto end;
457 }
458
459 /* Set the initial states to something we do not know
460 * The loop below will include this state to the dwCurrentState
461 */
462 for (i=0; i<nbReaders; i++) {
463 rgReaderStates_t[i].szReader = readers[i];
464 rgReaderStates_t[i].dwCurrentState = SCARD_STATE_UNAWARE;
465 }
466
467 /* Wait endlessly for all events in the list of readers
468 * We only stop in case of an error
469 */
470 rv = SCardGetStatusChange(hContext, timeout, rgReaderStates_t, nbReaders);
471 while ((rv == SCARD_S_SUCCESS) || (rv == SCARD_E_TIMEOUT)) {
472 /* A new reader appeared? */
473 if ((SCardListReaders(hContext, NULL, NULL, &dwReaders)
474 == SCARD_S_SUCCESS) && (dwReaders != dwReadersOld))
475 goto get_readers;
476
477 /* we were asked to suicide */
478 if (AraKiri)
479 break;
480
481 /* Now we have an event, check all the readers to see what happened */
482 for (current_reader=0; current_reader < nbReaders; current_reader++) {
483 unsigned long new_state;
484
485 if (rgReaderStates_t[current_reader].dwEventState &
486 SCARD_STATE_CHANGED) {
487 /* If something has changed the new state is now the current
488 * state */
489 rgReaderStates_t[current_reader].dwCurrentState =
490 rgReaderStates_t[current_reader].dwEventState;
491 }
492 /* If nothing changed then skip to the next reader */
493 else continue;
494
495 /* From here we know that the state for the current reader has
496 * changed because we did not pass through the continue statement
497 * above.
498 */
499
500 if (first_loop)
501 continue; /* skip first pass */
502
503 /* Specify the current reader's number and name */
504 DBG2("Reader %d (%s)", current_reader,
505 rgReaderStates_t[current_reader].szReader);
506
507 /* Dump the full current state */
508 new_state = rgReaderStates_t[current_reader].dwEventState;
509 DBG1("Card state: 0x%08ld", new_state);
510
511 if (new_state & SCARD_STATE_UNKNOWN) {
512 DBG("Reader unknown");
513 goto get_readers;
514 }
515
516 if (new_state & SCARD_STATE_EMPTY) {
517 DBG("Card removed");
518 execute_event("card_remove");
519 }
520
521 if (new_state & SCARD_STATE_PRESENT) {
522 DBG("Card inserted");
523 execute_event("card_insert");
524 }
525 } /* for */
526
527 first_loop = FALSE;
528 rv = SCardGetStatusChange(hContext, timeout, rgReaderStates_t, nbReaders);
529 } /* while */
530
531 /* If we get out the loop, GetStatusChange() was unsuccessful */
532 DBG1("SCardGetStatusChange: %lX", rv);
533
534 end:
535 /* free memory possibly allocated */
536 free(readers);
537 readers = NULL;
538 free(rgReaderStates_t);
539 rgReaderStates_t = NULL;
540
541 if (pidfile)
542 remove_pidfile(pidfile);
543
544 thats_all_folks();
545 exit(0);
546 } /* main */
547
548