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