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