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