Annotation of qemu/qga/channel-win32.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.