From d5830145bc9af4b8df2eddd5a6ece27c4010b3d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 25 Feb 2013 23:31:16 +0100 Subject: [PATCH 222/241] libcacard: teach vscclient to use GMainLoop for portability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This version handles non-blocking sending and receiving from the socket. Signed-off-by: Marc-André Lureau Reviewed-by: Alon Levy (cherry picked from commit c9495ee9eb57786f5a60d4591bb186b23f6b6bef) --- libcacard/vscclient.c | 391 +++++++++++++++++++++++++++++++------------------- 1 file changed, 246 insertions(+), 145 deletions(-) diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c index 5f47634..ac23647 100644 --- a/libcacard/vscclient.c +++ b/libcacard/vscclient.c @@ -10,7 +10,10 @@ * See the COPYING.LIB file in the top-level directory. */ +#ifndef _WIN32 #include +#endif +#include #include "qemu-common.h" #include "qemu/thread.h" @@ -22,9 +25,7 @@ #include "vcard_emul.h" #include "vevent.h" -int verbose; - -int sock; +static int verbose; static void print_byte_array( @@ -51,7 +52,47 @@ print_usage(void) { vcard_emul_usage(); } -static QemuMutex write_lock; +static GIOChannel *channel_socket; +static GByteArray *socket_to_send; +static QemuMutex socket_to_send_lock; +static guint socket_tag; + +static void +update_socket_watch(gboolean out); + +static gboolean +do_socket_send(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + gsize bw; + GError *err = NULL; + + g_return_val_if_fail(socket_to_send->len != 0, FALSE); + g_return_val_if_fail(condition & G_IO_OUT, FALSE); + + g_io_channel_write_chars(channel_socket, + (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err); + if (err != NULL) { + g_error("Error while sending socket %s", err->message); + return FALSE; + } + g_byte_array_remove_range(socket_to_send, 0, bw); + + if (socket_to_send->len == 0) { + update_socket_watch(FALSE); + return FALSE; + } + return TRUE; +} + +static gboolean +socket_prepare_sending(gpointer user_data) +{ + update_socket_watch(TRUE); + + return FALSE; +} static int send_msg( @@ -60,10 +101,9 @@ send_msg( const void *msg, unsigned int length ) { - int rv; VSCMsgHeader mhHeader; - qemu_mutex_lock(&write_lock); + qemu_mutex_lock(&socket_to_send_lock); if (verbose > 10) { printf("sending type=%d id=%u, len =%u (0x%x)\n", @@ -73,23 +113,11 @@ send_msg( mhHeader.type = htonl(type); mhHeader.reader_id = 0; mhHeader.length = htonl(length); - rv = write(sock, &mhHeader, sizeof(mhHeader)); - if (rv < 0) { - /* Error */ - fprintf(stderr, "write header error\n"); - close(sock); - qemu_mutex_unlock(&write_lock); - return 16; - } - rv = write(sock, msg, length); - if (rv < 0) { - /* Error */ - fprintf(stderr, "write error\n"); - close(sock); - qemu_mutex_unlock(&write_lock); - return 16; - } - qemu_mutex_unlock(&write_lock); + g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader)); + g_byte_array_append(socket_to_send, (guint8 *)msg, length); + g_idle_add(socket_prepare_sending, NULL); + + qemu_mutex_unlock(&socket_to_send_lock); return 0; } @@ -245,139 +273,203 @@ on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) return 0; } + +enum { + STATE_HEADER, + STATE_MESSAGE, +}; + #define APDUBufSize 270 -static int -do_socket_read(void) +static gboolean +do_socket_read(GIOChannel *source, + GIOCondition condition, + gpointer data) { int rv; int dwSendLength; int dwRecvLength; uint8_t pbRecvBuffer[APDUBufSize]; - uint8_t pbSendBuffer[APDUBufSize]; + static uint8_t pbSendBuffer[APDUBufSize]; VReaderStatus reader_status; VReader *reader = NULL; - VSCMsgHeader mhHeader; + static VSCMsgHeader mhHeader; VSCMsgError *error_msg; + GError *err = NULL; - rv = read(sock, &mhHeader, sizeof(mhHeader)); - if (rv < sizeof(mhHeader)) { - /* Error */ - if (rv < 0) { - perror("header read error\n"); - } else { - fprintf(stderr, "header short read %d\n", rv); - } - return -1; - } - mhHeader.type = ntohl(mhHeader.type); - mhHeader.reader_id = ntohl(mhHeader.reader_id); - mhHeader.length = ntohl(mhHeader.length); - if (verbose) { - printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", - mhHeader.type, mhHeader.reader_id, mhHeader.length, - mhHeader.length); - } - switch (mhHeader.type) { - case VSC_APDU: - case VSC_Flush: - case VSC_Error: - case VSC_Init: - rv = read(sock, pbSendBuffer, mhHeader.length); - break; - default: - fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); - return -1; + static gchar *buf; + static gsize br, to_read; + static int state = STATE_HEADER; + + if (state == STATE_HEADER && to_read == 0) { + buf = (gchar *)&mhHeader; + to_read = sizeof(mhHeader); } - switch (mhHeader.type) { - case VSC_APDU: - if (rv < 0) { - /* Error */ - fprintf(stderr, "read error\n"); - close(sock); - return -1; + + if (to_read > 0) { + g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err); + if (err != NULL) { + g_error("error while reading: %s", err->message); } + buf += br; + to_read -= br; + if (to_read != 0) { + return TRUE; + } + } + + if (state == STATE_HEADER) { + mhHeader.type = ntohl(mhHeader.type); + mhHeader.reader_id = ntohl(mhHeader.reader_id); + mhHeader.length = ntohl(mhHeader.length); if (verbose) { - printf(" recv APDU: "); - print_byte_array(pbSendBuffer, mhHeader.length); + printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", + mhHeader.type, mhHeader.reader_id, mhHeader.length, + mhHeader.length); } - /* Transmit received APDU */ - dwSendLength = mhHeader.length; - dwRecvLength = sizeof(pbRecvBuffer); - reader = vreader_get_reader_by_id(mhHeader.reader_id); - reader_status = vreader_xfr_bytes(reader, - pbSendBuffer, dwSendLength, - pbRecvBuffer, &dwRecvLength); - if (reader_status == VREADER_OK) { - mhHeader.length = dwRecvLength; + switch (mhHeader.type) { + case VSC_APDU: + case VSC_Flush: + case VSC_Error: + case VSC_Init: + buf = (gchar *)pbSendBuffer; + to_read = mhHeader.length; + state = STATE_MESSAGE; + return TRUE; + default: + fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); + return FALSE; + } + } + + if (state == STATE_MESSAGE) { + switch (mhHeader.type) { + case VSC_APDU: if (verbose) { - printf(" send response: "); - print_byte_array(pbRecvBuffer, mhHeader.length); + printf(" recv APDU: "); + print_byte_array(pbSendBuffer, mhHeader.length); } - send_msg(VSC_APDU, mhHeader.reader_id, - pbRecvBuffer, dwRecvLength); - } else { - rv = reader_status; /* warning: not meaningful */ - send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); - } - vreader_free(reader); - reader = NULL; /* we've freed it, don't use it by accident - again */ - break; - case VSC_Flush: - /* TODO: actually flush */ - send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); - break; - case VSC_Error: - error_msg = (VSCMsgError *) pbSendBuffer; - if (error_msg->code == VSC_SUCCESS) { - qemu_mutex_lock(&pending_reader_lock); - if (pending_reader) { - vreader_set_id(pending_reader, mhHeader.reader_id); - vreader_free(pending_reader); - pending_reader = NULL; - qemu_cond_signal(&pending_reader_condition); + /* Transmit received APDU */ + dwSendLength = mhHeader.length; + dwRecvLength = sizeof(pbRecvBuffer); + reader = vreader_get_reader_by_id(mhHeader.reader_id); + reader_status = vreader_xfr_bytes(reader, + pbSendBuffer, dwSendLength, + pbRecvBuffer, &dwRecvLength); + if (reader_status == VREADER_OK) { + mhHeader.length = dwRecvLength; + if (verbose) { + printf(" send response: "); + print_byte_array(pbRecvBuffer, mhHeader.length); + } + send_msg(VSC_APDU, mhHeader.reader_id, + pbRecvBuffer, dwRecvLength); + } else { + rv = reader_status; /* warning: not meaningful */ + send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); } - qemu_mutex_unlock(&pending_reader_lock); + vreader_free(reader); + reader = NULL; /* we've freed it, don't use it by accident + again */ break; - } - printf("warning: qemu refused to add reader\n"); - if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { - /* clear pending reader, qemu can't handle any more */ - qemu_mutex_lock(&pending_reader_lock); - if (pending_reader) { - pending_reader = NULL; - /* make sure the event loop doesn't hang */ - qemu_cond_signal(&pending_reader_condition); + case VSC_Flush: + /* TODO: actually flush */ + send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); + break; + case VSC_Error: + error_msg = (VSCMsgError *) pbSendBuffer; + if (error_msg->code == VSC_SUCCESS) { + qemu_mutex_lock(&pending_reader_lock); + if (pending_reader) { + vreader_set_id(pending_reader, mhHeader.reader_id); + vreader_free(pending_reader); + pending_reader = NULL; + qemu_cond_signal(&pending_reader_condition); + } + qemu_mutex_unlock(&pending_reader_lock); + break; } - qemu_mutex_unlock(&pending_reader_lock); + printf("warning: qemu refused to add reader\n"); + if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { + /* clear pending reader, qemu can't handle any more */ + qemu_mutex_lock(&pending_reader_lock); + if (pending_reader) { + pending_reader = NULL; + /* make sure the event loop doesn't hang */ + qemu_cond_signal(&pending_reader_condition); + } + qemu_mutex_unlock(&pending_reader_lock); + } + break; + case VSC_Init: + if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { + return FALSE; + } + break; + default: + g_warn_if_reached(); + return FALSE; } - break; - case VSC_Init: - if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { - return -1; + + state = STATE_HEADER; + } + + + return TRUE; +} + +static gboolean +do_socket(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + /* not sure if two watches work well with a single win32 sources */ + if (condition & G_IO_OUT) { + if (!do_socket_send(source, condition, data)) { + return FALSE; } - break; - default: - printf("Default\n"); - return -1; } - return 0; + if (condition & G_IO_IN) { + if (!do_socket_read(source, condition, data)) { + return FALSE; + } + } + + return TRUE; } static void -do_command(void) +update_socket_watch(gboolean out) +{ + if (socket_tag != 0) { + g_source_remove(socket_tag); + } + + socket_tag = g_io_add_watch(channel_socket, + G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL); +} + +static gboolean +do_command(GIOChannel *source, + GIOCondition condition, + gpointer data) { - char inbuf[255]; char *string; VCardEmulError error; static unsigned int default_reader_id; unsigned int reader_id; VReader *reader = NULL; + GError *err = NULL; + + g_assert(condition & G_IO_IN); reader_id = default_reader_id; - string = fgets(inbuf, sizeof(inbuf), stdin); + g_io_channel_read_line(source, &string, NULL, NULL, &err); + if (err != NULL) { + g_error("Error while reading command: %s", err->message); + } + if (string != NULL) { if (strncmp(string, "exit", 4) == 0) { /* remove all the readers */ @@ -491,6 +583,8 @@ do_command(void) vreader_free(reader); printf("> "); fflush(stdout); + + return TRUE; } @@ -504,7 +598,7 @@ connect_to_qemu( ) { struct addrinfo hints; struct addrinfo *server; - int ret; + int ret, sock; sock = qemu_socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { @@ -543,6 +637,8 @@ main( int argc, char *argv[] ) { + GMainLoop *loop; + GIOChannel *channel_stdin; char *qemu_host; char *qemu_port; VSCMsgHeader mhHeader; @@ -552,7 +648,10 @@ main( char *cert_names[MAX_CERTS]; char *emul_args = NULL; int cert_count = 0; - int c, rv; + int c, sock; + + if (socket_init() != 0) + return 1; while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { switch (c) { @@ -618,15 +717,33 @@ main( exit(5); } - qemu_mutex_init(&write_lock); + socket_to_send = g_byte_array_new(); + qemu_mutex_init(&socket_to_send_lock); qemu_mutex_init(&pending_reader_lock); qemu_cond_init(&pending_reader_condition); vcard_emul_init(command_line_options); + loop = g_main_loop_new(NULL, true); + printf("> "); fflush(stdout); +#ifdef _WIN32 + channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO); +#else + channel_stdin = g_io_channel_unix_new(STDIN_FILENO); +#endif + g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL); +#ifdef _WIN32 + channel_socket = g_io_channel_win32_new_socket(sock); +#else + channel_socket = g_io_channel_unix_new(sock); +#endif + g_io_channel_set_encoding(channel_socket, NULL, NULL); + /* we buffer ourself for thread safety reasons */ + g_io_channel_set_buffered(channel_socket, FALSE); + /* Send init message, Host responds (and then we send reader attachments) */ VSCMsgInit init = { .version = htonl(VSCARD_VERSION), @@ -635,28 +752,12 @@ main( }; send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init)); - do { - fd_set fds; - - FD_ZERO(&fds); - FD_SET(1, &fds); - FD_SET(sock, &fds); + g_main_loop_run(loop); + g_main_loop_unref(loop); - /* waiting on input from the socket */ - rv = select(sock+1, &fds, NULL, NULL, NULL); - if (rv < 0) { - /* handle error */ - perror("select"); - return 7; - } - if (FD_ISSET(1, &fds)) { - do_command(); - } - if (!FD_ISSET(sock, &fds)) { - continue; - } - rv = do_socket_read(); - } while (rv >= 0); + g_io_channel_unref(channel_stdin); + g_io_channel_unref(channel_socket); + g_byte_array_unref(socket_to_send); return 0; } -- 1.8.3.1