~dannf/qemu-linaro/qemu-highbank-ppa

« back to all changes in this revision

Viewing changes to qga/channel-win32.c

  • Committer: Steve Langasek
  • Date: 2012-03-15 21:13:19 UTC
  • mfrom: (0.1.15)
  • Revision ID: steve.langasek@canonical.com-20120315211319-f1j3ot1ihx30b2s9
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
11
typedef 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
 
 
21
struct 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
 
 
30
typedef 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
 */
 
41
static 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
 
 
82
out:
 
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
 */
 
98
static 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
 */
 
155
static 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
 
 
181
static void ga_channel_finalize(GSource *source)
 
182
{
 
183
    g_debug("finalize");
 
184
}
 
185
 
 
186
GSourceFuncs ga_channel_watch_funcs = {
 
187
    ga_channel_prepare,
 
188
    ga_channel_check,
 
189
    ga_channel_dispatch,
 
190
    ga_channel_finalize
 
191
};
 
192
 
 
193
static 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
 
 
205
GIOStatus 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
 
 
228
static 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
    return status;
 
263
}
 
264
 
 
265
GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
 
266
{
 
267
    GIOStatus status = G_IO_STATUS_NORMAL;;
 
268
    size_t count;
 
269
 
 
270
    while (size) {
 
271
        status = ga_channel_write(c, buf, size, &count);
 
272
        if (status == G_IO_STATUS_NORMAL) {
 
273
            size -= count;
 
274
            buf += count;
 
275
        } else if (status != G_IO_STATUS_AGAIN) {
 
276
            break;
 
277
        }
 
278
    }
 
279
 
 
280
    return status;
 
281
}
 
282
 
 
283
static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
 
284
                                const gchar *path)
 
285
{
 
286
    if (!method == GA_CHANNEL_VIRTIO_SERIAL) {
 
287
        g_critical("unsupported communication method");
 
288
        return false;
 
289
    }
 
290
 
 
291
    c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
 
292
                           OPEN_EXISTING,
 
293
                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
 
294
    if (c->handle == INVALID_HANDLE_VALUE) {
 
295
        g_critical("error opening path");
 
296
        return false;
 
297
    }
 
298
 
 
299
    return true;
 
300
}
 
301
 
 
302
GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
 
303
                          GAChannelCallback cb, gpointer opaque)
 
304
{
 
305
    GAChannel *c = g_malloc0(sizeof(GAChannel));
 
306
    SECURITY_ATTRIBUTES sec_attrs;
 
307
 
 
308
    if (!ga_channel_open(c, method, path)) {
 
309
        g_critical("error opening channel");
 
310
        g_free(c);
 
311
        return NULL;
 
312
    }
 
313
 
 
314
    c->cb = cb;
 
315
    c->user_data = opaque;
 
316
 
 
317
    sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
 
318
    sec_attrs.lpSecurityDescriptor = NULL;
 
319
    sec_attrs.bInheritHandle = false;
 
320
 
 
321
    c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
 
322
    c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
 
323
    c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);
 
324
 
 
325
    c->source = ga_channel_create_watch(c);
 
326
    g_source_attach(c->source, NULL);
 
327
    return c;
 
328
}
 
329
 
 
330
void ga_channel_free(GAChannel *c)
 
331
{
 
332
    if (c->source) {
 
333
        g_source_destroy(c->source);
 
334
    }
 
335
    if (c->rstate.ov.hEvent) {
 
336
        CloseHandle(c->rstate.ov.hEvent);
 
337
    }
 
338
    g_free(c->rstate.buf);
 
339
    g_free(c);
 
340
}