]>
Commit | Line | Data |
---|---|---|
2ac85b93 RR |
1 | /* |
2 | * Tester for VSCARD protocol, client side. | |
3 | * | |
4 | * Can be used with ccid-card-passthru. | |
5 | * | |
6 | * Copyright (c) 2011 Red Hat. | |
7 | * Written by Alon Levy. | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include <netdb.h> | |
14 | ||
15 | #include "qemu-common.h" | |
1de7afc9 PB |
16 | #include "qemu/thread.h" |
17 | #include "qemu/sockets.h" | |
2ac85b93 RR |
18 | |
19 | #include "vscard_common.h" | |
20 | ||
21 | #include "vreader.h" | |
22 | #include "vcard_emul.h" | |
23 | #include "vevent.h" | |
24 | ||
25 | int verbose; | |
26 | ||
27 | int sock; | |
28 | ||
29 | static void | |
30 | print_byte_array( | |
31 | uint8_t *arrBytes, | |
32 | unsigned int nSize | |
33 | ) { | |
34 | int i; | |
35 | for (i = 0; i < nSize; i++) { | |
36 | printf("%02X ", arrBytes[i]); | |
37 | } | |
38 | printf("\n"); | |
39 | } | |
40 | ||
41 | static void | |
42 | print_usage(void) { | |
43 | printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] " | |
44 | "<host> <port>\n", | |
45 | #ifdef USE_PASSTHRU | |
46 | " -p"); | |
47 | printf(" -p use passthrough mode\n"); | |
48 | #else | |
49 | ""); | |
50 | #endif | |
51 | vcard_emul_usage(); | |
52 | } | |
53 | ||
54 | static QemuMutex write_lock; | |
55 | ||
56 | static int | |
57 | send_msg( | |
58 | VSCMsgType type, | |
59 | uint32_t reader_id, | |
60 | const void *msg, | |
61 | unsigned int length | |
62 | ) { | |
63 | int rv; | |
64 | VSCMsgHeader mhHeader; | |
65 | ||
66 | qemu_mutex_lock(&write_lock); | |
67 | ||
68 | if (verbose > 10) { | |
ba79c886 | 69 | printf("sending type=%d id=%u, len =%u (0x%x)\n", |
2ac85b93 RR |
70 | type, reader_id, length, length); |
71 | } | |
72 | ||
73 | mhHeader.type = htonl(type); | |
74 | mhHeader.reader_id = 0; | |
75 | mhHeader.length = htonl(length); | |
76 | rv = write(sock, &mhHeader, sizeof(mhHeader)); | |
77 | if (rv < 0) { | |
78 | /* Error */ | |
79 | fprintf(stderr, "write header error\n"); | |
80 | close(sock); | |
81 | qemu_mutex_unlock(&write_lock); | |
82 | return 16; | |
83 | } | |
84 | rv = write(sock, msg, length); | |
85 | if (rv < 0) { | |
86 | /* Error */ | |
87 | fprintf(stderr, "write error\n"); | |
88 | close(sock); | |
89 | qemu_mutex_unlock(&write_lock); | |
90 | return 16; | |
91 | } | |
92 | qemu_mutex_unlock(&write_lock); | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | static VReader *pending_reader; | |
98 | static QemuMutex pending_reader_lock; | |
99 | static QemuCond pending_reader_condition; | |
100 | ||
101 | #define MAX_ATR_LEN 40 | |
102 | static void * | |
103 | event_thread(void *arg) | |
104 | { | |
105 | unsigned char atr[MAX_ATR_LEN]; | |
106 | int atr_len = MAX_ATR_LEN; | |
107 | VEvent *event = NULL; | |
108 | unsigned int reader_id; | |
109 | ||
110 | ||
111 | while (1) { | |
112 | const char *reader_name; | |
113 | ||
114 | event = vevent_wait_next_vevent(); | |
115 | if (event == NULL) { | |
116 | break; | |
117 | } | |
118 | reader_id = vreader_get_id(event->reader); | |
119 | if (reader_id == VSCARD_UNDEFINED_READER_ID && | |
120 | event->type != VEVENT_READER_INSERT) { | |
121 | /* ignore events from readers qemu has rejected */ | |
122 | /* if qemu is still deciding on this reader, wait to see if need to | |
123 | * forward this event */ | |
124 | qemu_mutex_lock(&pending_reader_lock); | |
125 | if (!pending_reader || (pending_reader != event->reader)) { | |
126 | /* wasn't for a pending reader, this reader has already been | |
127 | * rejected by qemu */ | |
128 | qemu_mutex_unlock(&pending_reader_lock); | |
129 | vevent_delete(event); | |
130 | continue; | |
131 | } | |
93148aa5 | 132 | /* this reader hasn't been told its status from qemu yet, wait for |
2ac85b93 RR |
133 | * that status */ |
134 | while (pending_reader != NULL) { | |
135 | qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); | |
136 | } | |
137 | qemu_mutex_unlock(&pending_reader_lock); | |
138 | /* now recheck the id */ | |
139 | reader_id = vreader_get_id(event->reader); | |
140 | if (reader_id == VSCARD_UNDEFINED_READER_ID) { | |
141 | /* this reader was rejected */ | |
142 | vevent_delete(event); | |
143 | continue; | |
144 | } | |
145 | /* reader was accepted, now forward the event */ | |
146 | } | |
147 | switch (event->type) { | |
148 | case VEVENT_READER_INSERT: | |
149 | /* tell qemu to insert a new CCID reader */ | |
150 | /* wait until qemu has responded to our first reader insert | |
151 | * before we send a second. That way we won't confuse the responses | |
152 | * */ | |
153 | qemu_mutex_lock(&pending_reader_lock); | |
154 | while (pending_reader != NULL) { | |
155 | qemu_cond_wait(&pending_reader_condition, &pending_reader_lock); | |
156 | } | |
157 | pending_reader = vreader_reference(event->reader); | |
158 | qemu_mutex_unlock(&pending_reader_lock); | |
159 | reader_name = vreader_get_name(event->reader); | |
160 | if (verbose > 10) { | |
161 | printf(" READER INSERT: %s\n", reader_name); | |
162 | } | |
163 | send_msg(VSC_ReaderAdd, | |
164 | reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ | |
165 | NULL, 0 /* TODO reader_name, strlen(reader_name) */); | |
166 | break; | |
167 | case VEVENT_READER_REMOVE: | |
168 | /* future, tell qemu that an old CCID reader has been removed */ | |
169 | if (verbose > 10) { | |
ba79c886 | 170 | printf(" READER REMOVE: %u\n", reader_id); |
2ac85b93 RR |
171 | } |
172 | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | |
173 | break; | |
174 | case VEVENT_CARD_INSERT: | |
175 | /* get the ATR (intended as a response to a power on from the | |
176 | * reader */ | |
177 | atr_len = MAX_ATR_LEN; | |
178 | vreader_power_on(event->reader, atr, &atr_len); | |
179 | /* ATR call functions as a Card Insert event */ | |
180 | if (verbose > 10) { | |
ba79c886 | 181 | printf(" CARD INSERT %u: ", reader_id); |
2ac85b93 RR |
182 | print_byte_array(atr, atr_len); |
183 | } | |
184 | send_msg(VSC_ATR, reader_id, atr, atr_len); | |
185 | break; | |
186 | case VEVENT_CARD_REMOVE: | |
187 | /* Card removed */ | |
188 | if (verbose > 10) { | |
ba79c886 | 189 | printf(" CARD REMOVE %u:\n", reader_id); |
2ac85b93 RR |
190 | } |
191 | send_msg(VSC_CardRemove, reader_id, NULL, 0); | |
192 | break; | |
193 | default: | |
194 | break; | |
195 | } | |
196 | vevent_delete(event); | |
197 | } | |
198 | return NULL; | |
199 | } | |
200 | ||
201 | ||
202 | static unsigned int | |
203 | get_id_from_string(char *string, unsigned int default_id) | |
204 | { | |
205 | unsigned int id = atoi(string); | |
206 | ||
207 | /* don't accidentally swith to zero because no numbers have been supplied */ | |
208 | if ((id == 0) && *string != '0') { | |
209 | return default_id; | |
210 | } | |
211 | return id; | |
212 | } | |
213 | ||
a50b831a MAL |
214 | static int |
215 | on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) | |
216 | { | |
217 | uint32_t *capabilities = (incoming->capabilities); | |
218 | int num_capabilities = | |
219 | 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); | |
220 | int i; | |
221 | int rv; | |
222 | pthread_t thread_id; | |
223 | ||
224 | incoming->version = ntohl(incoming->version); | |
225 | if (incoming->version != VSCARD_VERSION) { | |
226 | if (verbose > 0) { | |
227 | printf("warning: host has version %d, we have %d\n", | |
228 | verbose, VSCARD_VERSION); | |
229 | } | |
230 | } | |
231 | if (incoming->magic != VSCARD_MAGIC) { | |
232 | printf("unexpected magic: got %d, expected %d\n", | |
233 | incoming->magic, VSCARD_MAGIC); | |
234 | return -1; | |
235 | } | |
236 | for (i = 0 ; i < num_capabilities; ++i) { | |
237 | capabilities[i] = ntohl(capabilities[i]); | |
238 | } | |
239 | /* Future: check capabilities */ | |
240 | /* remove whatever reader might be left in qemu, | |
241 | * in case of an unclean previous exit. */ | |
242 | send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); | |
243 | /* launch the event_thread. This will trigger reader adds for all the | |
244 | * existing readers */ | |
245 | rv = pthread_create(&thread_id, NULL, event_thread, NULL); | |
246 | if (rv < 0) { | |
247 | perror("pthread_create"); | |
248 | return rv; | |
249 | } | |
250 | return 0; | |
251 | } | |
252 | ||
253 | #define APDUBufSize 270 | |
254 | ||
255 | static int | |
256 | do_socket_read(void) | |
257 | { | |
258 | int rv; | |
259 | int dwSendLength; | |
260 | int dwRecvLength; | |
261 | uint8_t pbRecvBuffer[APDUBufSize]; | |
262 | uint8_t pbSendBuffer[APDUBufSize]; | |
263 | VReaderStatus reader_status; | |
264 | VReader *reader = NULL; | |
265 | VSCMsgHeader mhHeader; | |
266 | VSCMsgError *error_msg; | |
267 | ||
268 | rv = read(sock, &mhHeader, sizeof(mhHeader)); | |
269 | if (rv < sizeof(mhHeader)) { | |
270 | /* Error */ | |
271 | if (rv < 0) { | |
272 | perror("header read error\n"); | |
273 | } else { | |
274 | fprintf(stderr, "header short read %d\n", rv); | |
275 | } | |
276 | return -1; | |
277 | } | |
278 | mhHeader.type = ntohl(mhHeader.type); | |
279 | mhHeader.reader_id = ntohl(mhHeader.reader_id); | |
280 | mhHeader.length = ntohl(mhHeader.length); | |
281 | if (verbose) { | |
282 | printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", | |
283 | mhHeader.type, mhHeader.reader_id, mhHeader.length, | |
284 | mhHeader.length); | |
285 | } | |
286 | switch (mhHeader.type) { | |
287 | case VSC_APDU: | |
288 | case VSC_Flush: | |
289 | case VSC_Error: | |
290 | case VSC_Init: | |
291 | rv = read(sock, pbSendBuffer, mhHeader.length); | |
292 | break; | |
293 | default: | |
294 | fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); | |
295 | return -1; | |
296 | } | |
297 | switch (mhHeader.type) { | |
298 | case VSC_APDU: | |
299 | if (rv < 0) { | |
300 | /* Error */ | |
301 | fprintf(stderr, "read error\n"); | |
302 | close(sock); | |
303 | return -1; | |
304 | } | |
305 | if (verbose) { | |
306 | printf(" recv APDU: "); | |
307 | print_byte_array(pbSendBuffer, mhHeader.length); | |
308 | } | |
309 | /* Transmit received APDU */ | |
310 | dwSendLength = mhHeader.length; | |
311 | dwRecvLength = sizeof(pbRecvBuffer); | |
312 | reader = vreader_get_reader_by_id(mhHeader.reader_id); | |
313 | reader_status = vreader_xfr_bytes(reader, | |
314 | pbSendBuffer, dwSendLength, | |
315 | pbRecvBuffer, &dwRecvLength); | |
316 | if (reader_status == VREADER_OK) { | |
317 | mhHeader.length = dwRecvLength; | |
318 | if (verbose) { | |
319 | printf(" send response: "); | |
320 | print_byte_array(pbRecvBuffer, mhHeader.length); | |
321 | } | |
322 | send_msg(VSC_APDU, mhHeader.reader_id, | |
323 | pbRecvBuffer, dwRecvLength); | |
324 | } else { | |
325 | rv = reader_status; /* warning: not meaningful */ | |
326 | send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); | |
327 | } | |
328 | vreader_free(reader); | |
329 | reader = NULL; /* we've freed it, don't use it by accident | |
330 | again */ | |
331 | break; | |
332 | case VSC_Flush: | |
333 | /* TODO: actually flush */ | |
334 | send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); | |
335 | break; | |
336 | case VSC_Error: | |
337 | error_msg = (VSCMsgError *) pbSendBuffer; | |
338 | if (error_msg->code == VSC_SUCCESS) { | |
339 | qemu_mutex_lock(&pending_reader_lock); | |
340 | if (pending_reader) { | |
341 | vreader_set_id(pending_reader, mhHeader.reader_id); | |
342 | vreader_free(pending_reader); | |
343 | pending_reader = NULL; | |
344 | qemu_cond_signal(&pending_reader_condition); | |
345 | } | |
346 | qemu_mutex_unlock(&pending_reader_lock); | |
347 | break; | |
348 | } | |
349 | printf("warning: qemu refused to add reader\n"); | |
350 | if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { | |
351 | /* clear pending reader, qemu can't handle any more */ | |
352 | qemu_mutex_lock(&pending_reader_lock); | |
353 | if (pending_reader) { | |
354 | pending_reader = NULL; | |
355 | /* make sure the event loop doesn't hang */ | |
356 | qemu_cond_signal(&pending_reader_condition); | |
357 | } | |
358 | qemu_mutex_unlock(&pending_reader_lock); | |
359 | } | |
360 | break; | |
361 | case VSC_Init: | |
362 | if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { | |
363 | return -1; | |
364 | } | |
365 | break; | |
366 | default: | |
367 | printf("Default\n"); | |
368 | return -1; | |
369 | } | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
2ac85b93 RR |
374 | static void |
375 | do_command(void) | |
376 | { | |
377 | char inbuf[255]; | |
378 | char *string; | |
379 | VCardEmulError error; | |
380 | static unsigned int default_reader_id; | |
381 | unsigned int reader_id; | |
382 | VReader *reader = NULL; | |
383 | ||
384 | reader_id = default_reader_id; | |
385 | string = fgets(inbuf, sizeof(inbuf), stdin); | |
386 | if (string != NULL) { | |
387 | if (strncmp(string, "exit", 4) == 0) { | |
388 | /* remove all the readers */ | |
389 | VReaderList *list = vreader_get_reader_list(); | |
390 | VReaderListEntry *reader_entry; | |
391 | printf("Active Readers:\n"); | |
392 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
393 | reader_entry = vreader_list_get_next(reader_entry)) { | |
394 | VReader *reader = vreader_list_get_reader(reader_entry); | |
395 | vreader_id_t reader_id; | |
396 | reader_id = vreader_get_id(reader); | |
397 | if (reader_id == -1) { | |
398 | continue; | |
399 | } | |
400 | /* be nice and signal card removal first (qemu probably should | |
401 | * do this itself) */ | |
402 | if (vreader_card_is_present(reader) == VREADER_OK) { | |
403 | send_msg(VSC_CardRemove, reader_id, NULL, 0); | |
404 | } | |
405 | send_msg(VSC_ReaderRemove, reader_id, NULL, 0); | |
406 | } | |
407 | exit(0); | |
408 | } else if (strncmp(string, "insert", 6) == 0) { | |
409 | if (string[6] == ' ') { | |
410 | reader_id = get_id_from_string(&string[7], reader_id); | |
411 | } | |
412 | reader = vreader_get_reader_by_id(reader_id); | |
413 | if (reader != NULL) { | |
414 | error = vcard_emul_force_card_insert(reader); | |
415 | printf("insert %s, returned %d\n", | |
416 | reader ? vreader_get_name(reader) | |
417 | : "invalid reader", error); | |
418 | } else { | |
ba79c886 | 419 | printf("no reader by id %u found\n", reader_id); |
2ac85b93 RR |
420 | } |
421 | } else if (strncmp(string, "remove", 6) == 0) { | |
422 | if (string[6] == ' ') { | |
423 | reader_id = get_id_from_string(&string[7], reader_id); | |
424 | } | |
425 | reader = vreader_get_reader_by_id(reader_id); | |
426 | if (reader != NULL) { | |
427 | error = vcard_emul_force_card_remove(reader); | |
428 | printf("remove %s, returned %d\n", | |
429 | reader ? vreader_get_name(reader) | |
430 | : "invalid reader", error); | |
431 | } else { | |
ba79c886 | 432 | printf("no reader by id %u found\n", reader_id); |
2ac85b93 RR |
433 | } |
434 | } else if (strncmp(string, "select", 6) == 0) { | |
435 | if (string[6] == ' ') { | |
436 | reader_id = get_id_from_string(&string[7], | |
437 | VSCARD_UNDEFINED_READER_ID); | |
438 | } | |
439 | if (reader_id != VSCARD_UNDEFINED_READER_ID) { | |
440 | reader = vreader_get_reader_by_id(reader_id); | |
441 | } | |
442 | if (reader) { | |
ba79c886 | 443 | printf("Selecting reader %u, %s\n", reader_id, |
2ac85b93 RR |
444 | vreader_get_name(reader)); |
445 | default_reader_id = reader_id; | |
446 | } else { | |
ba79c886 | 447 | printf("Reader with id %u not found\n", reader_id); |
2ac85b93 RR |
448 | } |
449 | } else if (strncmp(string, "debug", 5) == 0) { | |
450 | if (string[5] == ' ') { | |
451 | verbose = get_id_from_string(&string[6], 0); | |
452 | } | |
453 | printf("debug level = %d\n", verbose); | |
454 | } else if (strncmp(string, "list", 4) == 0) { | |
455 | VReaderList *list = vreader_get_reader_list(); | |
456 | VReaderListEntry *reader_entry; | |
457 | printf("Active Readers:\n"); | |
458 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
459 | reader_entry = vreader_list_get_next(reader_entry)) { | |
460 | VReader *reader = vreader_list_get_reader(reader_entry); | |
461 | vreader_id_t reader_id; | |
462 | reader_id = vreader_get_id(reader); | |
463 | if (reader_id == -1) { | |
464 | continue; | |
465 | } | |
ba79c886 | 466 | printf("%3u %s %s\n", reader_id, |
2ac85b93 RR |
467 | vreader_card_is_present(reader) == VREADER_OK ? |
468 | "CARD_PRESENT" : " ", | |
469 | vreader_get_name(reader)); | |
470 | } | |
471 | printf("Inactive Readers:\n"); | |
472 | for (reader_entry = vreader_list_get_first(list); reader_entry; | |
473 | reader_entry = vreader_list_get_next(reader_entry)) { | |
474 | VReader *reader = vreader_list_get_reader(reader_entry); | |
475 | vreader_id_t reader_id; | |
476 | reader_id = vreader_get_id(reader); | |
477 | if (reader_id != -1) { | |
478 | continue; | |
479 | } | |
480 | ||
481 | printf("INA %s %s\n", | |
482 | vreader_card_is_present(reader) == VREADER_OK ? | |
483 | "CARD_PRESENT" : " ", | |
484 | vreader_get_name(reader)); | |
485 | } | |
486 | } else if (*string != 0) { | |
487 | printf("valid commands:\n"); | |
488 | printf("insert [reader_id]\n"); | |
489 | printf("remove [reader_id]\n"); | |
490 | printf("select reader_id\n"); | |
491 | printf("list\n"); | |
492 | printf("debug [level]\n"); | |
493 | printf("exit\n"); | |
494 | } | |
495 | } | |
496 | vreader_free(reader); | |
497 | printf("> "); | |
498 | fflush(stdout); | |
499 | } | |
500 | ||
501 | ||
2ac85b93 RR |
502 | /* just for ease of parsing command line arguments. */ |
503 | #define MAX_CERTS 100 | |
504 | ||
505 | static int | |
506 | connect_to_qemu( | |
507 | const char *host, | |
508 | const char *port | |
509 | ) { | |
510 | struct addrinfo hints; | |
511 | struct addrinfo *server; | |
512 | int ret; | |
513 | ||
514 | sock = qemu_socket(AF_INET, SOCK_STREAM, 0); | |
515 | if (sock < 0) { | |
516 | /* Error */ | |
517 | fprintf(stderr, "Error opening socket!\n"); | |
e7c5e893 | 518 | return -1; |
2ac85b93 RR |
519 | } |
520 | ||
521 | memset(&hints, 0, sizeof(struct addrinfo)); | |
522 | hints.ai_family = AF_UNSPEC; | |
523 | hints.ai_socktype = SOCK_STREAM; | |
524 | hints.ai_flags = 0; | |
525 | hints.ai_protocol = 0; /* Any protocol */ | |
526 | ||
527 | ret = getaddrinfo(host, port, &hints, &server); | |
528 | ||
529 | if (ret != 0) { | |
530 | /* Error */ | |
531 | fprintf(stderr, "getaddrinfo failed\n"); | |
e7c5e893 | 532 | return -1; |
2ac85b93 RR |
533 | } |
534 | ||
535 | if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { | |
536 | /* Error */ | |
537 | fprintf(stderr, "Could not connect\n"); | |
e7c5e893 | 538 | return -1; |
2ac85b93 RR |
539 | } |
540 | if (verbose) { | |
541 | printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); | |
542 | } | |
543 | return sock; | |
544 | } | |
545 | ||
2ac85b93 RR |
546 | int |
547 | main( | |
548 | int argc, | |
549 | char *argv[] | |
550 | ) { | |
551 | char *qemu_host; | |
552 | char *qemu_port; | |
553 | VSCMsgHeader mhHeader; | |
2ac85b93 | 554 | |
2ac85b93 RR |
555 | VCardEmulOptions *command_line_options = NULL; |
556 | ||
557 | char *cert_names[MAX_CERTS]; | |
558 | char *emul_args = NULL; | |
559 | int cert_count = 0; | |
a50b831a | 560 | int c, rv; |
2ac85b93 RR |
561 | |
562 | while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { | |
563 | switch (c) { | |
564 | case 'c': | |
565 | if (cert_count >= MAX_CERTS) { | |
566 | printf("too many certificates (max = %d)\n", MAX_CERTS); | |
567 | exit(5); | |
568 | } | |
569 | cert_names[cert_count++] = optarg; | |
570 | break; | |
571 | case 'e': | |
572 | emul_args = optarg; | |
573 | break; | |
574 | case 'p': | |
575 | print_usage(); | |
576 | exit(4); | |
577 | break; | |
578 | case 'd': | |
579 | verbose = get_id_from_string(optarg, 1); | |
580 | break; | |
581 | } | |
582 | } | |
583 | ||
584 | if (argc - optind != 2) { | |
585 | print_usage(); | |
586 | exit(4); | |
587 | } | |
588 | ||
589 | if (cert_count > 0) { | |
590 | char *new_args; | |
591 | int len, i; | |
592 | /* if we've given some -c options, we clearly we want do so some | |
593 | * software emulation. add that emulation now. this is NSS Emulator | |
594 | * specific */ | |
595 | if (emul_args == NULL) { | |
596 | emul_args = (char *)"db=\"/etc/pki/nssdb\""; | |
597 | } | |
598 | #define SOFT_STRING ",soft=(,Virtual Reader,CAC,," | |
599 | /* 2 == close paren & null */ | |
600 | len = strlen(emul_args) + strlen(SOFT_STRING) + 2; | |
601 | for (i = 0; i < cert_count; i++) { | |
602 | len += strlen(cert_names[i])+1; /* 1 == comma */ | |
603 | } | |
7267c094 | 604 | new_args = g_malloc(len); |
2ac85b93 RR |
605 | strcpy(new_args, emul_args); |
606 | strcat(new_args, SOFT_STRING); | |
607 | for (i = 0; i < cert_count; i++) { | |
608 | strcat(new_args, cert_names[i]); | |
609 | strcat(new_args, ","); | |
610 | } | |
611 | strcat(new_args, ")"); | |
612 | emul_args = new_args; | |
613 | } | |
614 | if (emul_args) { | |
615 | command_line_options = vcard_emul_options(emul_args); | |
616 | } | |
617 | ||
be168af8 MA |
618 | qemu_host = g_strdup(argv[argc - 2]); |
619 | qemu_port = g_strdup(argv[argc - 1]); | |
2ac85b93 | 620 | sock = connect_to_qemu(qemu_host, qemu_port); |
e7c5e893 AL |
621 | if (sock == -1) { |
622 | fprintf(stderr, "error opening socket, exiting.\n"); | |
623 | exit(5); | |
624 | } | |
2ac85b93 RR |
625 | |
626 | qemu_mutex_init(&write_lock); | |
627 | qemu_mutex_init(&pending_reader_lock); | |
628 | qemu_cond_init(&pending_reader_condition); | |
629 | ||
630 | vcard_emul_init(command_line_options); | |
631 | ||
632 | printf("> "); | |
633 | fflush(stdout); | |
634 | ||
635 | /* Send init message, Host responds (and then we send reader attachments) */ | |
636 | VSCMsgInit init = { | |
637 | .version = htonl(VSCARD_VERSION), | |
638 | .magic = VSCARD_MAGIC, | |
639 | .capabilities = {0} | |
640 | }; | |
641 | send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); | |
642 | ||
643 | do { | |
644 | fd_set fds; | |
645 | ||
646 | FD_ZERO(&fds); | |
647 | FD_SET(1, &fds); | |
648 | FD_SET(sock, &fds); | |
649 | ||
650 | /* waiting on input from the socket */ | |
651 | rv = select(sock+1, &fds, NULL, NULL, NULL); | |
652 | if (rv < 0) { | |
653 | /* handle error */ | |
654 | perror("select"); | |
655 | return 7; | |
656 | } | |
657 | if (FD_ISSET(1, &fds)) { | |
658 | do_command(); | |
659 | } | |
660 | if (!FD_ISSET(sock, &fds)) { | |
661 | continue; | |
662 | } | |
a50b831a | 663 | rv = do_socket_read(); |
2ac85b93 RR |
664 | } while (rv >= 0); |
665 | ||
666 | return 0; | |
667 | } |