|
|
1.1 ! root 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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.