Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 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 | |
Jeff Cody | b71706d | 2012-03-15 14:26:18 -0400 | [diff] [blame] | 262 | if (ov.hEvent) { |
| 263 | CloseHandle(ov.hEvent); |
| 264 | ov.hEvent = NULL; |
| 265 | } |
Michael Roth | 7868e26 | 2012-01-20 19:01:30 -0600 | [diff] [blame] | 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 | } |