|
|
1.1 ! root 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" ! 16: #include "qemu-thread.h" ! 17: #include "qemu_socket.h" ! 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) { ! 69: printf("sending type=%d id=%d, len =%d (0x%x)\n", ! 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: } ! 132: /* this reader hasn't been told it's status from qemu yet, wait for ! 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) { ! 170: printf(" READER REMOVE: %d\n", reader_id); ! 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) { ! 181: printf(" CARD INSERT %d: ", reader_id); ! 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) { ! 189: printf(" CARD REMOVE %d:\n", reader_id); ! 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: ! 214: static void ! 215: do_command(void) ! 216: { ! 217: char inbuf[255]; ! 218: char *string; ! 219: VCardEmulError error; ! 220: static unsigned int default_reader_id; ! 221: unsigned int reader_id; ! 222: VReader *reader = NULL; ! 223: ! 224: reader_id = default_reader_id; ! 225: string = fgets(inbuf, sizeof(inbuf), stdin); ! 226: if (string != NULL) { ! 227: if (strncmp(string, "exit", 4) == 0) { ! 228: /* remove all the readers */ ! 229: VReaderList *list = vreader_get_reader_list(); ! 230: VReaderListEntry *reader_entry; ! 231: printf("Active Readers:\n"); ! 232: for (reader_entry = vreader_list_get_first(list); reader_entry; ! 233: reader_entry = vreader_list_get_next(reader_entry)) { ! 234: VReader *reader = vreader_list_get_reader(reader_entry); ! 235: vreader_id_t reader_id; ! 236: reader_id = vreader_get_id(reader); ! 237: if (reader_id == -1) { ! 238: continue; ! 239: } ! 240: /* be nice and signal card removal first (qemu probably should ! 241: * do this itself) */ ! 242: if (vreader_card_is_present(reader) == VREADER_OK) { ! 243: send_msg(VSC_CardRemove, reader_id, NULL, 0); ! 244: } ! 245: send_msg(VSC_ReaderRemove, reader_id, NULL, 0); ! 246: } ! 247: exit(0); ! 248: } else if (strncmp(string, "insert", 6) == 0) { ! 249: if (string[6] == ' ') { ! 250: reader_id = get_id_from_string(&string[7], reader_id); ! 251: } ! 252: reader = vreader_get_reader_by_id(reader_id); ! 253: if (reader != NULL) { ! 254: error = vcard_emul_force_card_insert(reader); ! 255: printf("insert %s, returned %d\n", ! 256: reader ? vreader_get_name(reader) ! 257: : "invalid reader", error); ! 258: } else { ! 259: printf("no reader by id %d found\n", reader_id); ! 260: } ! 261: } else if (strncmp(string, "remove", 6) == 0) { ! 262: if (string[6] == ' ') { ! 263: reader_id = get_id_from_string(&string[7], reader_id); ! 264: } ! 265: reader = vreader_get_reader_by_id(reader_id); ! 266: if (reader != NULL) { ! 267: error = vcard_emul_force_card_remove(reader); ! 268: printf("remove %s, returned %d\n", ! 269: reader ? vreader_get_name(reader) ! 270: : "invalid reader", error); ! 271: } else { ! 272: printf("no reader by id %d found\n", reader_id); ! 273: } ! 274: } else if (strncmp(string, "select", 6) == 0) { ! 275: if (string[6] == ' ') { ! 276: reader_id = get_id_from_string(&string[7], ! 277: VSCARD_UNDEFINED_READER_ID); ! 278: } ! 279: if (reader_id != VSCARD_UNDEFINED_READER_ID) { ! 280: reader = vreader_get_reader_by_id(reader_id); ! 281: } ! 282: if (reader) { ! 283: printf("Selecting reader %d, %s\n", reader_id, ! 284: vreader_get_name(reader)); ! 285: default_reader_id = reader_id; ! 286: } else { ! 287: printf("Reader with id %d not found\n", reader_id); ! 288: } ! 289: } else if (strncmp(string, "debug", 5) == 0) { ! 290: if (string[5] == ' ') { ! 291: verbose = get_id_from_string(&string[6], 0); ! 292: } ! 293: printf("debug level = %d\n", verbose); ! 294: } else if (strncmp(string, "list", 4) == 0) { ! 295: VReaderList *list = vreader_get_reader_list(); ! 296: VReaderListEntry *reader_entry; ! 297: printf("Active Readers:\n"); ! 298: for (reader_entry = vreader_list_get_first(list); reader_entry; ! 299: reader_entry = vreader_list_get_next(reader_entry)) { ! 300: VReader *reader = vreader_list_get_reader(reader_entry); ! 301: vreader_id_t reader_id; ! 302: reader_id = vreader_get_id(reader); ! 303: if (reader_id == -1) { ! 304: continue; ! 305: } ! 306: printf("%3d %s %s\n", reader_id, ! 307: vreader_card_is_present(reader) == VREADER_OK ? ! 308: "CARD_PRESENT" : " ", ! 309: vreader_get_name(reader)); ! 310: } ! 311: printf("Inactive Readers:\n"); ! 312: for (reader_entry = vreader_list_get_first(list); reader_entry; ! 313: reader_entry = vreader_list_get_next(reader_entry)) { ! 314: VReader *reader = vreader_list_get_reader(reader_entry); ! 315: vreader_id_t reader_id; ! 316: reader_id = vreader_get_id(reader); ! 317: if (reader_id != -1) { ! 318: continue; ! 319: } ! 320: ! 321: printf("INA %s %s\n", ! 322: vreader_card_is_present(reader) == VREADER_OK ? ! 323: "CARD_PRESENT" : " ", ! 324: vreader_get_name(reader)); ! 325: } ! 326: } else if (*string != 0) { ! 327: printf("valid commands:\n"); ! 328: printf("insert [reader_id]\n"); ! 329: printf("remove [reader_id]\n"); ! 330: printf("select reader_id\n"); ! 331: printf("list\n"); ! 332: printf("debug [level]\n"); ! 333: printf("exit\n"); ! 334: } ! 335: } ! 336: vreader_free(reader); ! 337: printf("> "); ! 338: fflush(stdout); ! 339: } ! 340: ! 341: ! 342: #define APDUBufSize 270 ! 343: ! 344: /* just for ease of parsing command line arguments. */ ! 345: #define MAX_CERTS 100 ! 346: ! 347: static int ! 348: connect_to_qemu( ! 349: const char *host, ! 350: const char *port ! 351: ) { ! 352: struct addrinfo hints; ! 353: struct addrinfo *server; ! 354: int ret; ! 355: ! 356: sock = qemu_socket(AF_INET, SOCK_STREAM, 0); ! 357: if (sock < 0) { ! 358: /* Error */ ! 359: fprintf(stderr, "Error opening socket!\n"); ! 360: } ! 361: ! 362: memset(&hints, 0, sizeof(struct addrinfo)); ! 363: hints.ai_family = AF_UNSPEC; ! 364: hints.ai_socktype = SOCK_STREAM; ! 365: hints.ai_flags = 0; ! 366: hints.ai_protocol = 0; /* Any protocol */ ! 367: ! 368: ret = getaddrinfo(host, port, &hints, &server); ! 369: ! 370: if (ret != 0) { ! 371: /* Error */ ! 372: fprintf(stderr, "getaddrinfo failed\n"); ! 373: return 5; ! 374: } ! 375: ! 376: if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { ! 377: /* Error */ ! 378: fprintf(stderr, "Could not connect\n"); ! 379: return 5; ! 380: } ! 381: if (verbose) { ! 382: printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); ! 383: } ! 384: return sock; ! 385: } ! 386: ! 387: static int on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) ! 388: { ! 389: uint32_t *capabilities = (incoming->capabilities); ! 390: int num_capabilities = ! 391: 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); ! 392: int i; ! 393: int rv; ! 394: pthread_t thread_id; ! 395: ! 396: incoming->version = ntohl(incoming->version); ! 397: if (incoming->version != VSCARD_VERSION) { ! 398: if (verbose > 0) { ! 399: printf("warning: host has version %d, we have %d\n", ! 400: verbose, VSCARD_VERSION); ! 401: } ! 402: } ! 403: if (incoming->magic != VSCARD_MAGIC) { ! 404: printf("unexpected magic: got %d, expected %d\n", ! 405: incoming->magic, VSCARD_MAGIC); ! 406: return -1; ! 407: } ! 408: for (i = 0 ; i < num_capabilities; ++i) { ! 409: capabilities[i] = ntohl(capabilities[i]); ! 410: } ! 411: /* Future: check capabilities */ ! 412: /* remove whatever reader might be left in qemu, ! 413: * in case of an unclean previous exit. */ ! 414: send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); ! 415: /* launch the event_thread. This will trigger reader adds for all the ! 416: * existing readers */ ! 417: rv = pthread_create(&thread_id, NULL, event_thread, NULL); ! 418: if (rv < 0) { ! 419: perror("pthread_create"); ! 420: return rv; ! 421: } ! 422: return 0; ! 423: } ! 424: ! 425: int ! 426: main( ! 427: int argc, ! 428: char *argv[] ! 429: ) { ! 430: char *qemu_host; ! 431: char *qemu_port; ! 432: VSCMsgHeader mhHeader; ! 433: VSCMsgError *error_msg; ! 434: ! 435: int rv; ! 436: int dwSendLength; ! 437: int dwRecvLength; ! 438: uint8_t pbRecvBuffer[APDUBufSize]; ! 439: uint8_t pbSendBuffer[APDUBufSize]; ! 440: VReaderStatus reader_status; ! 441: VReader *reader = NULL; ! 442: VCardEmulOptions *command_line_options = NULL; ! 443: ! 444: char *cert_names[MAX_CERTS]; ! 445: char *emul_args = NULL; ! 446: int cert_count = 0; ! 447: int c; ! 448: ! 449: while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { ! 450: switch (c) { ! 451: case 'c': ! 452: if (cert_count >= MAX_CERTS) { ! 453: printf("too many certificates (max = %d)\n", MAX_CERTS); ! 454: exit(5); ! 455: } ! 456: cert_names[cert_count++] = optarg; ! 457: break; ! 458: case 'e': ! 459: emul_args = optarg; ! 460: break; ! 461: case 'p': ! 462: print_usage(); ! 463: exit(4); ! 464: break; ! 465: case 'd': ! 466: verbose = get_id_from_string(optarg, 1); ! 467: break; ! 468: } ! 469: } ! 470: ! 471: if (argc - optind != 2) { ! 472: print_usage(); ! 473: exit(4); ! 474: } ! 475: ! 476: if (cert_count > 0) { ! 477: char *new_args; ! 478: int len, i; ! 479: /* if we've given some -c options, we clearly we want do so some ! 480: * software emulation. add that emulation now. this is NSS Emulator ! 481: * specific */ ! 482: if (emul_args == NULL) { ! 483: emul_args = (char *)"db=\"/etc/pki/nssdb\""; ! 484: } ! 485: #define SOFT_STRING ",soft=(,Virtual Reader,CAC,," ! 486: /* 2 == close paren & null */ ! 487: len = strlen(emul_args) + strlen(SOFT_STRING) + 2; ! 488: for (i = 0; i < cert_count; i++) { ! 489: len += strlen(cert_names[i])+1; /* 1 == comma */ ! 490: } ! 491: new_args = qemu_malloc(len); ! 492: strcpy(new_args, emul_args); ! 493: strcat(new_args, SOFT_STRING); ! 494: for (i = 0; i < cert_count; i++) { ! 495: strcat(new_args, cert_names[i]); ! 496: strcat(new_args, ","); ! 497: } ! 498: strcat(new_args, ")"); ! 499: emul_args = new_args; ! 500: } ! 501: if (emul_args) { ! 502: command_line_options = vcard_emul_options(emul_args); ! 503: } ! 504: ! 505: qemu_host = strdup(argv[argc - 2]); ! 506: qemu_port = strdup(argv[argc - 1]); ! 507: sock = connect_to_qemu(qemu_host, qemu_port); ! 508: ! 509: qemu_mutex_init(&write_lock); ! 510: qemu_mutex_init(&pending_reader_lock); ! 511: qemu_cond_init(&pending_reader_condition); ! 512: ! 513: vcard_emul_init(command_line_options); ! 514: ! 515: printf("> "); ! 516: fflush(stdout); ! 517: ! 518: /* Send init message, Host responds (and then we send reader attachments) */ ! 519: VSCMsgInit init = { ! 520: .version = htonl(VSCARD_VERSION), ! 521: .magic = VSCARD_MAGIC, ! 522: .capabilities = {0} ! 523: }; ! 524: send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); ! 525: ! 526: do { ! 527: fd_set fds; ! 528: ! 529: FD_ZERO(&fds); ! 530: FD_SET(1, &fds); ! 531: FD_SET(sock, &fds); ! 532: ! 533: /* waiting on input from the socket */ ! 534: rv = select(sock+1, &fds, NULL, NULL, NULL); ! 535: if (rv < 0) { ! 536: /* handle error */ ! 537: perror("select"); ! 538: return 7; ! 539: } ! 540: if (FD_ISSET(1, &fds)) { ! 541: do_command(); ! 542: } ! 543: if (!FD_ISSET(sock, &fds)) { ! 544: continue; ! 545: } ! 546: ! 547: rv = read(sock, &mhHeader, sizeof(mhHeader)); ! 548: if (rv < sizeof(mhHeader)) { ! 549: /* Error */ ! 550: if (rv < 0) { ! 551: perror("header read error\n"); ! 552: } else { ! 553: fprintf(stderr, "header short read %d\n", rv); ! 554: } ! 555: return 8; ! 556: } ! 557: mhHeader.type = ntohl(mhHeader.type); ! 558: mhHeader.reader_id = ntohl(mhHeader.reader_id); ! 559: mhHeader.length = ntohl(mhHeader.length); ! 560: if (verbose) { ! 561: printf("Header: type=%d, reader_id=%d length=%d (0x%x)\n", ! 562: mhHeader.type, mhHeader.reader_id, mhHeader.length, ! 563: mhHeader.length); ! 564: } ! 565: switch (mhHeader.type) { ! 566: case VSC_APDU: ! 567: case VSC_Flush: ! 568: case VSC_Error: ! 569: case VSC_Init: ! 570: rv = read(sock, pbSendBuffer, mhHeader.length); ! 571: break; ! 572: default: ! 573: fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); ! 574: return 0; ! 575: } ! 576: switch (mhHeader.type) { ! 577: case VSC_APDU: ! 578: if (rv < 0) { ! 579: /* Error */ ! 580: fprintf(stderr, "read error\n"); ! 581: close(sock); ! 582: return 8; ! 583: } ! 584: if (verbose) { ! 585: printf(" recv APDU: "); ! 586: print_byte_array(pbSendBuffer, mhHeader.length); ! 587: } ! 588: /* Transmit recieved APDU */ ! 589: dwSendLength = mhHeader.length; ! 590: dwRecvLength = sizeof(pbRecvBuffer); ! 591: reader = vreader_get_reader_by_id(mhHeader.reader_id); ! 592: reader_status = vreader_xfr_bytes(reader, ! 593: pbSendBuffer, dwSendLength, ! 594: pbRecvBuffer, &dwRecvLength); ! 595: if (reader_status == VREADER_OK) { ! 596: mhHeader.length = dwRecvLength; ! 597: if (verbose) { ! 598: printf(" send response: "); ! 599: print_byte_array(pbRecvBuffer, mhHeader.length); ! 600: } ! 601: send_msg(VSC_APDU, mhHeader.reader_id, ! 602: pbRecvBuffer, dwRecvLength); ! 603: } else { ! 604: rv = reader_status; /* warning: not meaningful */ ! 605: send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); ! 606: } ! 607: vreader_free(reader); ! 608: reader = NULL; /* we've freed it, don't use it by accident ! 609: again */ ! 610: break; ! 611: case VSC_Flush: ! 612: /* TODO: actually flush */ ! 613: send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); ! 614: break; ! 615: case VSC_Error: ! 616: error_msg = (VSCMsgError *) pbSendBuffer; ! 617: if (error_msg->code == VSC_SUCCESS) { ! 618: qemu_mutex_lock(&pending_reader_lock); ! 619: if (pending_reader) { ! 620: vreader_set_id(pending_reader, mhHeader.reader_id); ! 621: vreader_free(pending_reader); ! 622: pending_reader = NULL; ! 623: qemu_cond_signal(&pending_reader_condition); ! 624: } ! 625: qemu_mutex_unlock(&pending_reader_lock); ! 626: break; ! 627: } ! 628: printf("warning: qemu refused to add reader\n"); ! 629: if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { ! 630: /* clear pending reader, qemu can't handle any more */ ! 631: qemu_mutex_lock(&pending_reader_lock); ! 632: if (pending_reader) { ! 633: pending_reader = NULL; ! 634: /* make sure the event loop doesn't hang */ ! 635: qemu_cond_signal(&pending_reader_condition); ! 636: } ! 637: qemu_mutex_unlock(&pending_reader_lock); ! 638: } ! 639: break; ! 640: case VSC_Init: ! 641: if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { ! 642: return -1; ! 643: } ! 644: break; ! 645: default: ! 646: printf("Default\n"); ! 647: return 0; ! 648: } ! 649: } while (rv >= 0); ! 650: ! 651: return 0; ! 652: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.