qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Alon Levy <alevy@redhat.com>
To: qemu-devel@nongnu.org
Cc: mlureau@redhat.com
Subject: [Qemu-devel] [PATCH 11/26] libcacard: teach vscclient to use GMainLoop for portability
Date: Mon, 18 Mar 2013 15:10:57 +0200	[thread overview]
Message-ID: <1363612272-13713-12-git-send-email-alevy@redhat.com> (raw)
In-Reply-To: <1363612272-13713-1-git-send-email-alevy@redhat.com>

From: Marc-André Lureau <marcandre.lureau@gmail.com>

This version handles non-blocking sending and receiving from the
socket.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 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 <netdb.h>
+#endif
+#include <glib.h>
 
 #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.1.4

  parent reply	other threads:[~2013-03-18 13:11 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-18 13:10 [Qemu-devel] [PATCH 00/26] ccid and libcacard fixes for windows/mingw Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 01/26] libcacard: correct T0 historical bytes size Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 02/26] ccid-card-emul: do not crash if backend is not provided Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 03/26] ccid: make backend_enum_table "static const" and adjust users Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 04/26] ccid: declare DEFAULT_ATR table to be "static const" Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 05/26] libcacard: use system config directory for nss db on win32 Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 06/26] util: move socket_init() to osdep.c Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 07/26] build-sys: must link with -fstack-protector Alon Levy
2013-03-22 17:18   ` Paolo Bonzini
2013-03-18 13:10 ` [Qemu-devel] [PATCH 08/26] libcacard: fix mingw64 cross-compilation Alon Levy
2013-03-18 14:54   ` Paolo Bonzini
2013-03-18 13:10 ` [Qemu-devel] [PATCH 09/26] libcacard: split vscclient main() from socket reading Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 10/26] libcacard: vscclient to use QemuThread for portability Alon Levy
2013-03-18 13:10 ` Alon Levy [this message]
2013-03-18 13:10 ` [Qemu-devel] [PATCH 12/26] hw/ccid-card-passthru.c: add atr check Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-22 19:31     ` Alon Levy
2013-03-18 13:10 ` [Qemu-devel] [PATCH 13/26] ccid-card-passthru, dev-smartcard-reader: add debug environment variables Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 14/26] hw/usb/dev-smartcard-reader.c: white space fixes Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 15/26] hw/usb/dev-smartcard-reader.c: nicer debug messages Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 16/26] hw/usb/dev-smartcard-reader.c: remove aborts (never triggered, but just in case) Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 17/26] hw/usb/dev-smartcard-reader.c: define structs for CCID_Parameter internals Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 18/26] hw/usb/dev-smartcard-reader.c: copy atr's protocol to ccid's parameters (adds todo's) Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-27 15:06     ` Alon Levy
2013-03-18 13:11 ` [Qemu-devel] [PATCH 19/26] hw/usb/dev-smartcard-reader.c: dwFeadvertise support for T=0 only Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 20/26] hw/usb/dev-smartcard-reader: support windows guest Alon Levy
2013-03-22 14:23   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 21/26] dev-smartcard-reader: reuse usb.h definitions Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 22/26] libcacard/vreader: add debugging messages for apdu Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 23/26] libcacard: change default ATR Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 24/26] libcacard: move atr setting from macro to function Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-22 19:33     ` Alon Levy
2013-03-18 13:11 ` [Qemu-devel] [PATCH 25/26] dev-smartcard-reader: empty implementation for Mechanical (fail correctly) Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-18 13:11 ` [Qemu-devel] [PATCH 26/26] libcacard/cac.c: questionable change to single return of big switch functions Alon Levy
2013-03-22 14:22   ` Marc-André Lureau
2013-03-18 13:15 ` [Qemu-devel] [PATCH 00/26] ccid and libcacard fixes for windows/mingw Alon Levy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1363612272-13713-12-git-send-email-alevy@redhat.com \
    --to=alevy@redhat.com \
    --cc=mlureau@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).