]> git.proxmox.com Git - mirror_qemu.git/blame - qga/channel-win32.c
Merge remote-tracking branch 'remotes/stefanha/tags/net-pull-request' into staging
[mirror_qemu.git] / qga / channel-win32.c
CommitLineData
7868e26e
MR
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
b71706d1
JC
262 if (ov.hEvent) {
263 CloseHandle(ov.hEvent);
264 ov.hEvent = NULL;
265 }
7868e26e
MR
266 return status;
267}
268
269GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
270{
c7e775e4 271 GIOStatus status = G_IO_STATUS_NORMAL;
7868e26e
MR
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{
a749f42d
MM
290 COMMTIMEOUTS comTimeOut = {0};
291 gchar newpath[MAXPATHLEN] = {0};
292 comTimeOut.ReadIntervalTimeout = 1;
293
294 if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
7868e26e
MR
295 g_critical("unsupported communication method");
296 return false;
297 }
298
a749f42d
MM
299 if (method == GA_CHANNEL_ISA_SERIAL){
300 snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
301 }else {
302 g_strlcpy(newpath, path, sizeof(newpath));
303 }
304
305 c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
7868e26e
MR
306 OPEN_EXISTING,
307 FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
308 if (c->handle == INVALID_HANDLE_VALUE) {
309 g_critical("error opening path");
310 return false;
311 }
312
a749f42d
MM
313 if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) {
314 g_critical("error setting timeout for com port: %lu",GetLastError());
315 CloseHandle(c->handle);
316 return false;
317 }
318
7868e26e
MR
319 return true;
320}
321
322GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
323 GAChannelCallback cb, gpointer opaque)
324{
325 GAChannel *c = g_malloc0(sizeof(GAChannel));
326 SECURITY_ATTRIBUTES sec_attrs;
327
328 if (!ga_channel_open(c, method, path)) {
329 g_critical("error opening channel");
330 g_free(c);
331 return NULL;
332 }
333
334 c->cb = cb;
335 c->user_data = opaque;
336
337 sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
338 sec_attrs.lpSecurityDescriptor = NULL;
339 sec_attrs.bInheritHandle = false;
340
341 c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
342 c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
343 c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);
344
345 c->source = ga_channel_create_watch(c);
346 g_source_attach(c->source, NULL);
347 return c;
348}
349
350void ga_channel_free(GAChannel *c)
351{
352 if (c->source) {
353 g_source_destroy(c->source);
354 }
355 if (c->rstate.ov.hEvent) {
356 CloseHandle(c->rstate.ov.hEvent);
357 }
358 g_free(c->rstate.buf);
359 g_free(c);
360}