qemu/qga/channel-win32.c
<<
>>
Prefs
   1#include <stdlib.h>
   2#include <stdio.h>
   3#include <stdbool.h>
   4#include <glib.h>
   5#include <windows.h>
   6#include <errno.h>
   7#include <io.h>
   8#include "qga/guest-agent-core.h"
   9#include "qga/channel.h"
  10
  11typedef struct GAChannelReadState {
  12    guint thread_id;
  13    uint8_t *buf;
  14    size_t buf_size;
  15    size_t cur; /* current buffer start */
  16    size_t pending; /* pending buffered bytes to read */
  17    OVERLAPPED ov;
  18    bool ov_pending; /* whether on async read is outstanding */
  19} GAChannelReadState;
  20
  21struct GAChannel {
  22    HANDLE handle;
  23    GAChannelCallback cb;
  24    gpointer user_data;
  25    GAChannelReadState rstate;
  26    GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */
  27    GSource *source;
  28};
  29
  30typedef struct GAWatch {
  31    GSource source;
  32    GPollFD pollfd;
  33    GAChannel *channel;
  34    GIOCondition events_mask;
  35} GAWatch;
  36
  37/*
  38 * Called by glib prior to polling to set up poll events if polling is needed.
  39 *
  40 */
  41static gboolean ga_channel_prepare(GSource *source, gint *timeout_ms)
  42{
  43    GAWatch *watch = (GAWatch *)source;
  44    GAChannel *c = (GAChannel *)watch->channel;
  45    GAChannelReadState *rs = &c->rstate;
  46    DWORD count_read, count_to_read = 0;
  47    bool success;
  48    GIOCondition new_events = 0;
  49
  50    g_debug("prepare");
  51    /* go ahead and submit another read if there's room in the buffer
  52     * and no previous reads are outstanding
  53     */
  54    if (!rs->ov_pending) {
  55        if (rs->cur + rs->pending >= rs->buf_size) {
  56            if (rs->cur) {
  57                memmove(rs->buf, rs->buf + rs->cur, rs->pending);
  58                rs->cur = 0;
  59            }
  60        }
  61        count_to_read = rs->buf_size - rs->cur - rs->pending;
  62    }
  63
  64    if (rs->ov_pending || count_to_read <= 0) {
  65            goto out;
  66    }
  67
  68    /* submit the read */
  69    success = ReadFile(c->handle, rs->buf + rs->cur + rs->pending,
  70                       count_to_read, &count_read, &rs->ov);
  71    if (success) {
  72        rs->pending += count_read;
  73        rs->ov_pending = false;
  74    } else {
  75        if (GetLastError() == ERROR_IO_PENDING) {
  76            rs->ov_pending = true;
  77        } else {
  78            new_events |= G_IO_ERR;
  79        }
  80    }
  81
  82out:
  83    /* dont block forever, iterate the main loop every once and a while */
  84    *timeout_ms = 500;
  85    /* if there's data in the read buffer, or another event is pending,
  86     * skip polling and issue user cb.
  87     */
  88    if (rs->pending) {
  89        new_events |= G_IO_IN;
  90    }
  91    c->pending_events |= new_events;
  92    return !!c->pending_events;
  93}
  94
  95/*
  96 * Called by glib after an outstanding read request is completed.
  97 */
  98static gboolean ga_channel_check(GSource *source)
  99{
 100    GAWatch *watch = (GAWatch *)source;
 101    GAChannel *c = (GAChannel *)watch->channel;
 102    GAChannelReadState *rs = &c->rstate;
 103    DWORD count_read, error;
 104    BOOL success;
 105
 106    GIOCondition new_events = 0;
 107
 108    g_debug("check");
 109
 110    /* failing this implies we issued a read that completed immediately,
 111     * yet no data was placed into the buffer (and thus we did not skip
 112     * polling). but since EOF is not obtainable until we retrieve an
 113     * overlapped result, it must be the case that there was data placed
 114     * into the buffer, or an error was generated by Readfile(). in either
 115     * case, we should've skipped the polling for this round.
 116     */
 117    g_assert(rs->ov_pending);
 118
 119    success = GetOverlappedResult(c->handle, &rs->ov, &count_read, FALSE);
 120    if (success) {
 121        g_debug("thread: overlapped result, count_read: %d", (int)count_read);
 122        rs->pending += count_read;
 123        new_events |= G_IO_IN;
 124    } else {
 125        error = GetLastError();
 126        if (error == 0 || error == ERROR_HANDLE_EOF ||
 127            error == ERROR_NO_SYSTEM_RESOURCES ||
 128            error == ERROR_OPERATION_ABORTED) {
 129            /* note: On WinXP SP3 with rhel6ga virtio-win-1.1.16 vioser drivers,
 130             * ENSR seems to be synonymous with when we'd normally expect
 131             * ERROR_HANDLE_EOF. So treat it as such. Microsoft's
 132             * recommendation for ERROR_NO_SYSTEM_RESOURCES is to
 133             * retry the read, so this happens to work out anyway. On newer
 134             * virtio-win driver, this seems to be replaced with EOA, so
 135             * handle that in the same fashion.
 136             */
 137            new_events |= G_IO_HUP;
 138        } else if (error != ERROR_IO_INCOMPLETE) {
 139            g_critical("error retrieving overlapped result: %d", (int)error);
 140            new_events |= G_IO_ERR;
 141        }
 142    }
 143
 144    if (new_events) {
 145        rs->ov_pending = 0;
 146    }
 147    c->pending_events |= new_events;
 148
 149    return !!c->pending_events;
 150}
 151
 152/*
 153 * Called by glib after either prepare or check routines signal readiness
 154 */
 155static gboolean ga_channel_dispatch(GSource *source, GSourceFunc unused,
 156                                    gpointer user_data)
 157{
 158    GAWatch *watch = (GAWatch *)source;
 159    GAChannel *c = (GAChannel *)watch->channel;
 160    GAChannelReadState *rs = &c->rstate;
 161    gboolean success;
 162
 163    g_debug("dispatch");
 164    success = c->cb(watch->pollfd.revents, c->user_data);
 165
 166    if (c->pending_events & G_IO_ERR) {
 167        g_critical("channel error, removing source");
 168        return false;
 169    }
 170
 171    /* TODO: replace rs->pending with watch->revents */
 172    c->pending_events &= ~G_IO_HUP;
 173    if (!rs->pending) {
 174        c->pending_events &= ~G_IO_IN;
 175    } else {
 176        c->pending_events = 0;
 177    }
 178    return success;
 179}
 180
 181static void ga_channel_finalize(GSource *source)
 182{
 183    g_debug("finalize");
 184}
 185
 186GSourceFuncs ga_channel_watch_funcs = {
 187    ga_channel_prepare,
 188    ga_channel_check,
 189    ga_channel_dispatch,
 190    ga_channel_finalize
 191};
 192
 193static GSource *ga_channel_create_watch(GAChannel *c)
 194{
 195    GSource *source = g_source_new(&ga_channel_watch_funcs, sizeof(GAWatch));
 196    GAWatch *watch = (GAWatch *)source;
 197
 198    watch->channel = c;
 199    watch->pollfd.fd = (gintptr) c->rstate.ov.hEvent;
 200    g_source_add_poll(source, &watch->pollfd);
 201
 202    return source;
 203}
 204
 205GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *count)
 206{
 207    GAChannelReadState *rs = &c->rstate;
 208    GIOStatus status;
 209    size_t to_read = 0;
 210
 211    if (c->pending_events & G_IO_ERR) {
 212        return G_IO_STATUS_ERROR;
 213    }
 214
 215    *count = to_read = MIN(size, rs->pending);
 216    if (to_read) {
 217        memcpy(buf, rs->buf + rs->cur, to_read);
 218        rs->cur += to_read;
 219        rs->pending -= to_read;
 220        status = G_IO_STATUS_NORMAL;
 221    } else {
 222        status = G_IO_STATUS_AGAIN;
 223    }
 224
 225    return status;
 226}
 227
 228static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size,
 229                                  size_t *count)
 230{
 231    GIOStatus status;
 232    OVERLAPPED ov = {0};
 233    BOOL ret;
 234    DWORD written;
 235
 236    ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 237    ret = WriteFile(c->handle, buf, size, &written, &ov);
 238    if (!ret) {
 239        if (GetLastError() == ERROR_IO_PENDING) {
 240            /* write is pending */
 241            ret = GetOverlappedResult(c->handle, &ov, &written, TRUE);
 242            if (!ret) {
 243                if (!GetLastError()) {
 244                    status = G_IO_STATUS_AGAIN;
 245                } else {
 246                    status = G_IO_STATUS_ERROR;
 247                }
 248            } else {
 249                /* write is complete */
 250                status = G_IO_STATUS_NORMAL;
 251                *count = written;
 252            }
 253        } else {
 254            status = G_IO_STATUS_ERROR;
 255        }
 256    } else {
 257        /* write returned immediately */
 258        status = G_IO_STATUS_NORMAL;
 259        *count = written;
 260    }
 261
 262    if (ov.hEvent) {
 263        CloseHandle(ov.hEvent);
 264        ov.hEvent = NULL;
 265    }
 266    return status;
 267}
 268
 269GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
 270{
 271    GIOStatus status = G_IO_STATUS_NORMAL;;
 272    size_t count;
 273
 274    while (size) {
 275        status = ga_channel_write(c, buf, size, &count);
 276        if (status == G_IO_STATUS_NORMAL) {
 277            size -= count;
 278            buf += count;
 279        } else if (status != G_IO_STATUS_AGAIN) {
 280            break;
 281        }
 282    }
 283
 284    return status;
 285}
 286
 287static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
 288                                const gchar *path)
 289{
 290    if (!method == GA_CHANNEL_VIRTIO_SERIAL) {
 291        g_critical("unsupported communication method");
 292        return false;
 293    }
 294
 295    c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
 296                           OPEN_EXISTING,
 297                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
 298    if (c->handle == INVALID_HANDLE_VALUE) {
 299        g_critical("error opening path");
 300        return false;
 301    }
 302
 303    return true;
 304}
 305
 306GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
 307                          GAChannelCallback cb, gpointer opaque)
 308{
 309    GAChannel *c = g_malloc0(sizeof(GAChannel));
 310    SECURITY_ATTRIBUTES sec_attrs;
 311
 312    if (!ga_channel_open(c, method, path)) {
 313        g_critical("error opening channel");
 314        g_free(c);
 315        return NULL;
 316    }
 317
 318    c->cb = cb;
 319    c->user_data = opaque;
 320
 321    sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
 322    sec_attrs.lpSecurityDescriptor = NULL;
 323    sec_attrs.bInheritHandle = false;
 324
 325    c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
 326    c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
 327    c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);
 328
 329    c->source = ga_channel_create_watch(c);
 330    g_source_attach(c->source, NULL);
 331    return c;
 332}
 333
 334void ga_channel_free(GAChannel *c)
 335{
 336    if (c->source) {
 337        g_source_destroy(c->source);
 338    }
 339    if (c->rstate.ov.hEvent) {
 340        CloseHandle(c->rstate.ov.hEvent);
 341    }
 342    g_free(c->rstate.buf);
 343    g_free(c);
 344}
 345