Statistics
| Branch: | Revision:

root / qga / channel-win32.c @ ef5b2344

History | View | Annotate | Download (9.5 kB)

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
    if (ov.hEvent) {
263
        CloseHandle(ov.hEvent);
264
        ov.hEvent = NULL;
265
    }
266
    return status;
267
}
268

    
269
GIOStatus 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

    
287
static 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

    
306
GAChannel *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

    
334
void 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
}