summary refs log tree commit diff stats
path: root/qga
diff options
context:
space:
mode:
Diffstat (limited to 'qga')
-rw-r--r--qga/channel-posix.c246
-rw-r--r--qga/channel-win32.c340
-rw-r--r--qga/channel.h33
-rw-r--r--qga/commands-posix.c (renamed from qga/guest-agent-commands.c)59
-rw-r--r--qga/commands-win32.c130
-rw-r--r--qga/commands.c73
-rw-r--r--qga/guest-agent-core.h3
-rw-r--r--qga/service-win32.c114
-rw-r--r--qga/service-win32.h30
9 files changed, 969 insertions, 59 deletions
diff --git a/qga/channel-posix.c b/qga/channel-posix.c
new file mode 100644
index 0000000000..40f7658ccd
--- /dev/null
+++ b/qga/channel-posix.c
@@ -0,0 +1,246 @@
+#include <glib.h>
+#include <termios.h>
+#include "qemu_socket.h"
+#include "qga/channel.h"
+
+#define GA_CHANNEL_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */
+
+struct GAChannel {
+    GIOChannel *listen_channel;
+    GIOChannel *client_channel;
+    GAChannelMethod method;
+    GAChannelCallback event_cb;
+    gpointer user_data;
+};
+
+static int ga_channel_client_add(GAChannel *c, int fd);
+
+static gboolean ga_channel_listen_accept(GIOChannel *channel,
+                                         GIOCondition condition, gpointer data)
+{
+    GAChannel *c = data;
+    int ret, client_fd;
+    bool accepted = false;
+    struct sockaddr_un addr;
+    socklen_t addrlen = sizeof(addr);
+
+    g_assert(channel != NULL);
+
+    client_fd = qemu_accept(g_io_channel_unix_get_fd(channel),
+                            (struct sockaddr *)&addr, &addrlen);
+    if (client_fd == -1) {
+        g_warning("error converting fd to gsocket: %s", strerror(errno));
+        goto out;
+    }
+    fcntl(client_fd, F_SETFL, O_NONBLOCK);
+    ret = ga_channel_client_add(c, client_fd);
+    if (ret) {
+        g_warning("error setting up connection");
+        goto out;
+    }
+    accepted = true;
+
+out:
+    /* only accept 1 connection at a time */
+    return !accepted;
+}
+
+/* start polling for readable events on listen fd, new==true
+ * indicates we should use the existing s->listen_channel
+ */
+static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create)
+{
+    if (create) {
+        c->listen_channel = g_io_channel_unix_new(listen_fd);
+    }
+    g_io_add_watch(c->listen_channel, G_IO_IN, ga_channel_listen_accept, c);
+}
+
+static void ga_channel_listen_close(GAChannel *c)
+{
+    g_assert(c->method == GA_CHANNEL_UNIX_LISTEN);
+    g_assert(c->listen_channel);
+    g_io_channel_shutdown(c->listen_channel, true, NULL);
+    g_io_channel_unref(c->listen_channel);
+    c->listen_channel = NULL;
+}
+
+/* cleanup state for closed connection/session, start accepting new
+ * connections if we're in listening mode
+ */
+static void ga_channel_client_close(GAChannel *c)
+{
+    g_assert(c->client_channel);
+    g_io_channel_shutdown(c->client_channel, true, NULL);
+    g_io_channel_unref(c->client_channel);
+    c->client_channel = NULL;
+    if (c->method == GA_CHANNEL_UNIX_LISTEN && c->listen_channel) {
+        ga_channel_listen_add(c, 0, false);
+    }
+}
+
+static gboolean ga_channel_client_event(GIOChannel *channel,
+                                        GIOCondition condition, gpointer data)
+{
+    GAChannel *c = data;
+    gboolean client_cont;
+
+    g_assert(c);
+    if (c->event_cb) {
+        client_cont = c->event_cb(condition, c->user_data);
+        if (!client_cont) {
+            ga_channel_client_close(c);
+            return false;
+        }
+    }
+    return true;
+}
+
+static int ga_channel_client_add(GAChannel *c, int fd)
+{
+    GIOChannel *client_channel;
+    GError *err = NULL;
+
+    g_assert(c && !c->client_channel);
+    client_channel = g_io_channel_unix_new(fd);
+    g_assert(client_channel);
+    g_io_channel_set_encoding(client_channel, NULL, &err);
+    if (err != NULL) {
+        g_warning("error setting channel encoding to binary");
+        g_error_free(err);
+        return -1;
+    }
+    g_io_add_watch(client_channel, G_IO_IN | G_IO_HUP,
+                   ga_channel_client_event, c);
+    c->client_channel = client_channel;
+    return 0;
+}
+
+static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod method)
+{
+    int ret;
+    c->method = method;
+
+    switch (c->method) {
+    case GA_CHANNEL_VIRTIO_SERIAL: {
+        int fd = qemu_open(path, O_RDWR | O_NONBLOCK | O_ASYNC);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        ret = ga_channel_client_add(c, fd);
+        if (ret) {
+            g_critical("error adding channel to main loop");
+            return false;
+        }
+        break;
+    }
+    case GA_CHANNEL_ISA_SERIAL: {
+        struct termios tio;
+        int fd = qemu_open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+        if (fd == -1) {
+            g_critical("error opening channel: %s", strerror(errno));
+            exit(EXIT_FAILURE);
+        }
+        tcgetattr(fd, &tio);
+        /* set up serial port for non-canonical, dumb byte streaming */
+        tio.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP |
+                         INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY |
+                         IMAXBEL);
+        tio.c_oflag = 0;
+        tio.c_lflag = 0;
+        tio.c_cflag |= GA_CHANNEL_BAUDRATE_DEFAULT;
+        /* 1 available byte min or reads will block (we'll set non-blocking
+         * elsewhere, else we have to deal with read()=0 instead)
+         */
+        tio.c_cc[VMIN] = 1;
+        tio.c_cc[VTIME] = 0;
+        /* flush everything waiting for read/xmit, it's garbage at this point */
+        tcflush(fd, TCIFLUSH);
+        tcsetattr(fd, TCSANOW, &tio);
+        ret = ga_channel_client_add(c, fd);
+        if (ret) {
+            g_error("error adding channel to main loop");
+        }
+        break;
+    }
+    case GA_CHANNEL_UNIX_LISTEN: {
+        int fd = unix_listen(path, NULL, strlen(path));
+        if (fd == -1) {
+            g_critical("error opening path: %s", strerror(errno));
+            return false;
+        }
+        ga_channel_listen_add(c, fd, true);
+        break;
+    }
+    default:
+        g_critical("error binding/listening to specified socket");
+        return false;
+    }
+
+    return true;
+}
+
+GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size)
+{
+    GError *err = NULL;
+    gsize written = 0;
+    GIOStatus status = G_IO_STATUS_NORMAL;
+
+    while (size) {
+        status = g_io_channel_write_chars(c->client_channel, buf, size,
+                                          &written, &err);
+        g_debug("sending data, count: %d", (int)size);
+        if (err != NULL) {
+            g_warning("error writing to channel: %s", err->message);
+            return G_IO_STATUS_ERROR;
+        }
+        if (status != G_IO_STATUS_NORMAL) {
+            break;
+        }
+        size -= written;
+    }
+
+    if (status == G_IO_STATUS_NORMAL) {
+        status = g_io_channel_flush(c->client_channel, &err);
+        if (err != NULL) {
+            g_warning("error flushing channel: %s", err->message);
+            return G_IO_STATUS_ERROR;
+        }
+    }
+
+    return status;
+}
+
+GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count)
+{
+    return g_io_channel_read_chars(c->client_channel, buf, size, count, NULL);
+}
+
+GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
+                          GAChannelCallback cb, gpointer opaque)
+{
+    GAChannel *c = g_malloc0(sizeof(GAChannel));
+    c->event_cb = cb;
+    c->user_data = opaque;
+
+    if (!ga_channel_open(c, path, method)) {
+        g_critical("error opening channel");
+        ga_channel_free(c);
+        return NULL;
+    }
+
+    return c;
+}
+
+void ga_channel_free(GAChannel *c)
+{
+    if (c->method == GA_CHANNEL_UNIX_LISTEN
+        && c->listen_channel) {
+        ga_channel_listen_close(c);
+    }
+    if (c->client_channel) {
+        ga_channel_client_close(c);
+    }
+    g_free(c);
+}
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
new file mode 100644
index 0000000000..190251bb57
--- /dev/null
+++ b/qga/channel-win32.c
@@ -0,0 +1,340 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <windows.h>
+#include <errno.h>
+#include <io.h>
+#include "qga/guest-agent-core.h"
+#include "qga/channel.h"
+
+typedef struct GAChannelReadState {
+    guint thread_id;
+    uint8_t *buf;
+    size_t buf_size;
+    size_t cur; /* current buffer start */
+    size_t pending; /* pending buffered bytes to read */
+    OVERLAPPED ov;
+    bool ov_pending; /* whether on async read is outstanding */
+} GAChannelReadState;
+
+struct GAChannel {
+    HANDLE handle;
+    GAChannelCallback cb;
+    gpointer user_data;
+    GAChannelReadState rstate;
+    GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */
+    GSource *source;
+};
+
+typedef struct GAWatch {
+    GSource source;
+    GPollFD pollfd;
+    GAChannel *channel;
+    GIOCondition events_mask;
+} GAWatch;
+
+/*
+ * Called by glib prior to polling to set up poll events if polling is needed.
+ *
+ */
+static gboolean ga_channel_prepare(GSource *source, gint *timeout_ms)
+{
+    GAWatch *watch = (GAWatch *)source;
+    GAChannel *c = (GAChannel *)watch->channel;
+    GAChannelReadState *rs = &c->rstate;
+    DWORD count_read, count_to_read = 0;
+    bool success;
+    GIOCondition new_events = 0;
+
+    g_debug("prepare");
+    /* go ahead and submit another read if there's room in the buffer
+     * and no previous reads are outstanding
+     */
+    if (!rs->ov_pending) {
+        if (rs->cur + rs->pending >= rs->buf_size) {
+            if (rs->cur) {
+                memmove(rs->buf, rs->buf + rs->cur, rs->pending);
+                rs->cur = 0;
+            }
+        }
+        count_to_read = rs->buf_size - rs->cur - rs->pending;
+    }
+
+    if (rs->ov_pending || count_to_read <= 0) {
+            goto out;
+    }
+
+    /* submit the read */
+    success = ReadFile(c->handle, rs->buf + rs->cur + rs->pending,
+                       count_to_read, &count_read, &rs->ov);
+    if (success) {
+        rs->pending += count_read;
+        rs->ov_pending = false;
+    } else {
+        if (GetLastError() == ERROR_IO_PENDING) {
+            rs->ov_pending = true;
+        } else {
+            new_events |= G_IO_ERR;
+        }
+    }
+
+out:
+    /* dont block forever, iterate the main loop every once and a while */
+    *timeout_ms = 500;
+    /* if there's data in the read buffer, or another event is pending,
+     * skip polling and issue user cb.
+     */
+    if (rs->pending) {
+        new_events |= G_IO_IN;
+    }
+    c->pending_events |= new_events;
+    return !!c->pending_events;
+}
+
+/*
+ * Called by glib after an outstanding read request is completed.
+ */
+static gboolean ga_channel_check(GSource *source)
+{
+    GAWatch *watch = (GAWatch *)source;
+    GAChannel *c = (GAChannel *)watch->channel;
+    GAChannelReadState *rs = &c->rstate;
+    DWORD count_read, error;
+    BOOL success;
+
+    GIOCondition new_events = 0;
+
+    g_debug("check");
+
+    /* failing this implies we issued a read that completed immediately,
+     * yet no data was placed into the buffer (and thus we did not skip
+     * polling). but since EOF is not obtainable until we retrieve an
+     * overlapped result, it must be the case that there was data placed
+     * into the buffer, or an error was generated by Readfile(). in either
+     * case, we should've skipped the polling for this round.
+     */
+    g_assert(rs->ov_pending);
+
+    success = GetOverlappedResult(c->handle, &rs->ov, &count_read, FALSE);
+    if (success) {
+        g_debug("thread: overlapped result, count_read: %d", (int)count_read);
+        rs->pending += count_read;
+        new_events |= G_IO_IN;
+    } else {
+        error = GetLastError();
+        if (error == 0 || error == ERROR_HANDLE_EOF ||
+            error == ERROR_NO_SYSTEM_RESOURCES ||
+            error == ERROR_OPERATION_ABORTED) {
+            /* note: On WinXP SP3 with rhel6ga virtio-win-1.1.16 vioser drivers,
+             * ENSR seems to be synonymous with when we'd normally expect
+             * ERROR_HANDLE_EOF. So treat it as such. Microsoft's
+             * recommendation for ERROR_NO_SYSTEM_RESOURCES is to
+             * retry the read, so this happens to work out anyway. On newer
+             * virtio-win driver, this seems to be replaced with EOA, so
+             * handle that in the same fashion.
+             */
+            new_events |= G_IO_HUP;
+        } else if (error != ERROR_IO_INCOMPLETE) {
+            g_critical("error retrieving overlapped result: %d", (int)error);
+            new_events |= G_IO_ERR;
+        }
+    }
+
+    if (new_events) {
+        rs->ov_pending = 0;
+    }
+    c->pending_events |= new_events;
+
+    return !!c->pending_events;
+}
+
+/*
+ * Called by glib after either prepare or check routines signal readiness
+ */
+static gboolean ga_channel_dispatch(GSource *source, GSourceFunc unused,
+                                    gpointer user_data)
+{
+    GAWatch *watch = (GAWatch *)source;
+    GAChannel *c = (GAChannel *)watch->channel;
+    GAChannelReadState *rs = &c->rstate;
+    gboolean success;
+
+    g_debug("dispatch");
+    success = c->cb(watch->pollfd.revents, c->user_data);
+
+    if (c->pending_events & G_IO_ERR) {
+        g_critical("channel error, removing source");
+        return false;
+    }
+
+    /* TODO: replace rs->pending with watch->revents */
+    c->pending_events &= ~G_IO_HUP;
+    if (!rs->pending) {
+        c->pending_events &= ~G_IO_IN;
+    } else {
+        c->pending_events = 0;
+    }
+    return success;
+}
+
+static void ga_channel_finalize(GSource *source)
+{
+    g_debug("finalize");
+}
+
+GSourceFuncs ga_channel_watch_funcs = {
+    ga_channel_prepare,
+    ga_channel_check,
+    ga_channel_dispatch,
+    ga_channel_finalize
+};
+
+static GSource *ga_channel_create_watch(GAChannel *c)
+{
+    GSource *source = g_source_new(&ga_channel_watch_funcs, sizeof(GAWatch));
+    GAWatch *watch = (GAWatch *)source;
+
+    watch->channel = c;
+    watch->pollfd.fd = (gintptr) c->rstate.ov.hEvent;
+    g_source_add_poll(source, &watch->pollfd);
+
+    return source;
+}
+
+GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *count)
+{
+    GAChannelReadState *rs = &c->rstate;
+    GIOStatus status;
+    size_t to_read = 0;
+
+    if (c->pending_events & G_IO_ERR) {
+        return G_IO_STATUS_ERROR;
+    }
+
+    *count = to_read = MIN(size, rs->pending);
+    if (to_read) {
+        memcpy(buf, rs->buf + rs->cur, to_read);
+        rs->cur += to_read;
+        rs->pending -= to_read;
+        status = G_IO_STATUS_NORMAL;
+    } else {
+        status = G_IO_STATUS_AGAIN;
+    }
+
+    return status;
+}
+
+static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size,
+                                  size_t *count)
+{
+    GIOStatus status;
+    OVERLAPPED ov = {0};
+    BOOL ret;
+    DWORD written;
+
+    ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    ret = WriteFile(c->handle, buf, size, &written, &ov);
+    if (!ret) {
+        if (GetLastError() == ERROR_IO_PENDING) {
+            /* write is pending */
+            ret = GetOverlappedResult(c->handle, &ov, &written, TRUE);
+            if (!ret) {
+                if (!GetLastError()) {
+                    status = G_IO_STATUS_AGAIN;
+                } else {
+                    status = G_IO_STATUS_ERROR;
+                }
+            } else {
+                /* write is complete */
+                status = G_IO_STATUS_NORMAL;
+                *count = written;
+            }
+        } else {
+            status = G_IO_STATUS_ERROR;
+        }
+    } else {
+        /* write returned immediately */
+        status = G_IO_STATUS_NORMAL;
+        *count = written;
+    }
+
+    return status;
+}
+
+GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
+{
+    GIOStatus status = G_IO_STATUS_NORMAL;;
+    size_t count;
+
+    while (size) {
+        status = ga_channel_write(c, buf, size, &count);
+        if (status == G_IO_STATUS_NORMAL) {
+            size -= count;
+            buf += count;
+        } else if (status != G_IO_STATUS_AGAIN) {
+            break;
+        }
+    }
+
+    return status;
+}
+
+static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
+                                const gchar *path)
+{
+    if (!method == GA_CHANNEL_VIRTIO_SERIAL) {
+        g_critical("unsupported communication method");
+        return false;
+    }
+
+    c->handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                           OPEN_EXISTING,
+                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
+    if (c->handle == INVALID_HANDLE_VALUE) {
+        g_critical("error opening path");
+        return false;
+    }
+
+    return true;
+}
+
+GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
+                          GAChannelCallback cb, gpointer opaque)
+{
+    GAChannel *c = g_malloc0(sizeof(GAChannel));
+    SECURITY_ATTRIBUTES sec_attrs;
+
+    if (!ga_channel_open(c, method, path)) {
+        g_critical("error opening channel");
+        g_free(c);
+        return NULL;
+    }
+
+    c->cb = cb;
+    c->user_data = opaque;
+
+    sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sec_attrs.lpSecurityDescriptor = NULL;
+    sec_attrs.bInheritHandle = false;
+
+    c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
+    c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
+    c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);
+
+    c->source = ga_channel_create_watch(c);
+    g_source_attach(c->source, NULL);
+    return c;
+}
+
+void ga_channel_free(GAChannel *c)
+{
+    if (c->source) {
+        g_source_destroy(c->source);
+    }
+    if (c->rstate.ov.hEvent) {
+        CloseHandle(c->rstate.ov.hEvent);
+    }
+    g_free(c->rstate.buf);
+    g_free(c);
+}
diff --git a/qga/channel.h b/qga/channel.h
new file mode 100644
index 0000000000..3704ea9c86
--- /dev/null
+++ b/qga/channel.h
@@ -0,0 +1,33 @@
+/*
+ * QEMU Guest Agent channel declarations
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QGA_CHANNEL_H
+#define QGA_CHANNEL_H
+
+#include <glib.h>
+
+typedef struct GAChannel GAChannel;
+
+typedef enum {
+    GA_CHANNEL_VIRTIO_SERIAL,
+    GA_CHANNEL_ISA_SERIAL,
+    GA_CHANNEL_UNIX_LISTEN,
+} GAChannelMethod;
+
+typedef gboolean (*GAChannelCallback)(GIOCondition condition, gpointer opaque);
+
+GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
+                          GAChannelCallback cb, gpointer opaque);
+void ga_channel_free(GAChannel *c);
+GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count);
+GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size);
+
+#endif
diff --git a/qga/guest-agent-commands.c b/qga/commands-posix.c
index a09c8ca230..126127aae4 100644
--- a/qga/guest-agent-commands.c
+++ b/qga/commands-posix.c
@@ -1,5 +1,5 @@
 /*
- * QEMU Guest Agent commands
+ * QEMU Guest Agent POSIX-specific command implementations
  *
  * Copyright IBM Corp. 2011
  *
@@ -30,63 +30,6 @@
 
 static GAState *ga_state;
 
-/* Note: in some situations, like with the fsfreeze, logging may be
- * temporarilly disabled. if it is necessary that a command be able
- * to log for accounting purposes, check ga_logging_enabled() beforehand,
- * and use the QERR_QGA_LOGGING_DISABLED to generate an error
- */
-static void slog(const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
-    va_end(ap);
-}
-
-int64_t qmp_guest_sync(int64_t id, Error **errp)
-{
-    return id;
-}
-
-void qmp_guest_ping(Error **err)
-{
-    slog("guest-ping called");
-}
-
-struct GuestAgentInfo *qmp_guest_info(Error **err)
-{
-    GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
-    GuestAgentCommandInfo *cmd_info;
-    GuestAgentCommandInfoList *cmd_info_list;
-    char **cmd_list_head, **cmd_list;
-
-    info->version = g_strdup(QGA_VERSION);
-
-    cmd_list_head = cmd_list = qmp_get_command_list();
-    if (*cmd_list_head == NULL) {
-        goto out;
-    }
-
-    while (*cmd_list) {
-        cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo));
-        cmd_info->name = strdup(*cmd_list);
-        cmd_info->enabled = qmp_command_is_enabled(cmd_info->name);
-
-        cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList));
-        cmd_info_list->value = cmd_info;
-        cmd_info_list->next = info->supported_commands;
-        info->supported_commands = cmd_info_list;
-
-        g_free(*cmd_list);
-        cmd_list++;
-    }
-
-out:
-    g_free(cmd_list_head);
-    return info;
-}
-
 void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
 {
     int ret;
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
new file mode 100644
index 0000000000..4aa0f0d1e4
--- /dev/null
+++ b/qga/commands-win32.c
@@ -0,0 +1,130 @@
+/*
+ * QEMU Guest Agent win32-specific command implementations
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+#include "qga/guest-agent-core.h"
+#include "qga-qmp-commands.h"
+#include "qerror.h"
+
+#ifndef SHTDN_REASON_FLAG_PLANNED
+#define SHTDN_REASON_FLAG_PLANNED 0x80000000
+#endif
+
+void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err)
+{
+    HANDLE token;
+    TOKEN_PRIVILEGES priv;
+    UINT shutdown_flag = EWX_FORCE;
+
+    slog("guest-shutdown called, mode: %s", mode);
+
+    if (!has_mode || strcmp(mode, "powerdown") == 0) {
+        shutdown_flag |= EWX_POWEROFF;
+    } else if (strcmp(mode, "halt") == 0) {
+        shutdown_flag |= EWX_SHUTDOWN;
+    } else if (strcmp(mode, "reboot") == 0) {
+        shutdown_flag |= EWX_REBOOT;
+    } else {
+        error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
+                  "halt|powerdown|reboot");
+        return;
+    }
+
+    /* Request a shutdown privilege, but try to shut down the system
+       anyway. */
+    if (OpenProcessToken(GetCurrentProcess(),
+        TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
+    {
+        LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
+            &priv.Privileges[0].Luid);
+
+        priv.PrivilegeCount = 1;
+        priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+        AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0);
+    }
+
+    if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
+        slog("guest-shutdown failed: %d", GetLastError());
+        error_set(err, QERR_UNDEFINED_ERROR);
+    }
+}
+
+int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+void qmp_guest_file_close(int64_t handle, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+}
+
+GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
+                                   int64_t count, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
+                                     bool has_count, int64_t count, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
+                                   int64_t whence, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+void qmp_guest_file_flush(int64_t handle, Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+}
+
+/*
+ * Return status of freeze/thaw
+ */
+GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+/*
+ * Walk list of mounted file systems in the guest, and freeze the ones which
+ * are real local file systems.
+ */
+int64_t qmp_guest_fsfreeze_freeze(Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+/*
+ * Walk list of frozen file systems in the guest, and thaw them.
+ */
+int64_t qmp_guest_fsfreeze_thaw(Error **err)
+{
+    error_set(err, QERR_UNSUPPORTED);
+    return 0;
+}
+
+/* register init/cleanup routines for stateful command groups */
+void ga_command_state_init(GAState *s, GACommandState *cs)
+{
+}
diff --git a/qga/commands.c b/qga/commands.c
new file mode 100644
index 0000000000..b27407d5d7
--- /dev/null
+++ b/qga/commands.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU Guest Agent common/cross-platform command implementations
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+#include "qga/guest-agent-core.h"
+#include "qga-qmp-commands.h"
+#include "qerror.h"
+
+/* Note: in some situations, like with the fsfreeze, logging may be
+ * temporarilly disabled. if it is necessary that a command be able
+ * to log for accounting purposes, check ga_logging_enabled() beforehand,
+ * and use the QERR_QGA_LOGGING_DISABLED to generate an error
+ */
+void slog(const gchar *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
+    va_end(ap);
+}
+
+int64_t qmp_guest_sync(int64_t id, Error **errp)
+{
+    return id;
+}
+
+void qmp_guest_ping(Error **err)
+{
+    slog("guest-ping called");
+}
+
+struct GuestAgentInfo *qmp_guest_info(Error **err)
+{
+    GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
+    GuestAgentCommandInfo *cmd_info;
+    GuestAgentCommandInfoList *cmd_info_list;
+    char **cmd_list_head, **cmd_list;
+
+    info->version = g_strdup(QGA_VERSION);
+
+    cmd_list_head = cmd_list = qmp_get_command_list();
+    if (*cmd_list_head == NULL) {
+        goto out;
+    }
+
+    while (*cmd_list) {
+        cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo));
+        cmd_info->name = strdup(*cmd_list);
+        cmd_info->enabled = qmp_command_is_enabled(cmd_info->name);
+
+        cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList));
+        cmd_info_list->value = cmd_info;
+        cmd_info_list->next = info->supported_commands;
+        info->supported_commands = cmd_info_list;
+
+        g_free(*cmd_list);
+        cmd_list++;
+    }
+
+out:
+    g_free(cmd_list_head);
+    return info;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index e42b91d364..b5dfa5bbda 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -14,7 +14,7 @@
 #include "qemu-common.h"
 
 #define QGA_VERSION "1.0"
-#define QGA_READ_COUNT_DEFAULT 4 << 10
+#define QGA_READ_COUNT_DEFAULT 4096
 
 typedef struct GAState GAState;
 typedef struct GACommandState GACommandState;
@@ -29,3 +29,4 @@ GACommandState *ga_command_state_new(void);
 bool ga_logging_enabled(GAState *s);
 void ga_disable_logging(GAState *s);
 void ga_enable_logging(GAState *s);
+void slog(const gchar *fmt, ...);
diff --git a/qga/service-win32.c b/qga/service-win32.c
new file mode 100644
index 0000000000..09054565d3
--- /dev/null
+++ b/qga/service-win32.c
@@ -0,0 +1,114 @@
+/*
+ * QEMU Guest Agent helpers for win32 service management
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Gal Hammer        <ghammer@redhat.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+#include <windows.h>
+#include "qga/service-win32.h"
+
+static int printf_win_error(const char *text)
+{
+    DWORD err = GetLastError();
+    char *message;
+    int n;
+
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL,
+        err,
+        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+        (char *)&message, 0,
+        NULL);
+    n = printf("%s. (Error: %d) %s", text, err, message);
+    LocalFree(message);
+
+    return n;
+}
+
+int ga_install_service(const char *path, const char *logfile)
+{
+    SC_HANDLE manager;
+    SC_HANDLE service;
+    TCHAR cmdline[MAX_PATH];
+
+    if (GetModuleFileName(NULL, cmdline, MAX_PATH) == 0) {
+        printf_win_error("No full path to service's executable");
+        return EXIT_FAILURE;
+    }
+
+    _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -d", cmdline);
+
+    if (path) {
+        _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -p %s", cmdline, path);
+    }
+    if (logfile) {
+        _snprintf(cmdline, MAX_PATH - strlen(cmdline), "%s -l %s -v",
+            cmdline, logfile);
+    }
+
+    g_debug("service's cmdline: %s", cmdline);
+
+    manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+    if (manager == NULL) {
+        printf_win_error("No handle to service control manager");
+        return EXIT_FAILURE;
+    }
+
+    service = CreateService(manager, QGA_SERVICE_NAME, QGA_SERVICE_DISPLAY_NAME,
+        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
+        SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, NULL, NULL, NULL);
+
+    if (service) {
+        SERVICE_DESCRIPTION desc = { (char *)QGA_SERVICE_DESCRIPTION };
+        ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &desc);
+
+        printf("Service was installed successfully.\n");
+    } else {
+        printf_win_error("Failed to install service");
+    }
+
+    CloseServiceHandle(service);
+    CloseServiceHandle(manager);
+
+    return (service == NULL);
+}
+
+int ga_uninstall_service(void)
+{
+    SC_HANDLE manager;
+    SC_HANDLE service;
+
+    manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+    if (manager == NULL) {
+        printf_win_error("No handle to service control manager");
+        return EXIT_FAILURE;
+    }
+
+    service = OpenService(manager, QGA_SERVICE_NAME, DELETE);
+    if (service == NULL) {
+        printf_win_error("No handle to service");
+        CloseServiceHandle(manager);
+        return EXIT_FAILURE;
+    }
+
+    if (DeleteService(service) == FALSE) {
+        printf_win_error("Failed to delete service");
+    } else {
+        printf("Service was deleted successfully.\n");
+    }
+
+    CloseServiceHandle(service);
+    CloseServiceHandle(manager);
+
+    return EXIT_SUCCESS;
+}
diff --git a/qga/service-win32.h b/qga/service-win32.h
new file mode 100644
index 0000000000..99dfc53348
--- /dev/null
+++ b/qga/service-win32.h
@@ -0,0 +1,30 @@
+/*
+ * QEMU Guest Agent helpers for win32 service management
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Gal Hammer        <ghammer@redhat.com>
+ *  Michael Roth      <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QGA_SERVICE_H
+#define QGA_SERVICE_H
+
+#include <windows.h>
+
+#define QGA_SERVICE_DISPLAY_NAME "QEMU Guest Agent"
+#define QGA_SERVICE_NAME         "qemu-ga"
+#define QGA_SERVICE_DESCRIPTION  "Enables integration with QEMU machine emulator and virtualizer."
+
+typedef struct GAService {
+    SERVICE_STATUS status;
+    SERVICE_STATUS_HANDLE status_handle;
+} GAService;
+
+int ga_install_service(const char *path, const char *logfile);
+int ga_uninstall_service(void);
+
+#endif