summaryrefslogtreecommitdiffstats
path: root/qemu/qga
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/qga')
-rw-r--r--qemu/qga/Makefile.objs8
-rw-r--r--qemu/qga/channel-posix.c270
-rw-r--r--qemu/qga/channel-win32.c357
-rw-r--r--qemu/qga/channel.h33
-rw-r--r--qemu/qga/commands-posix.c2517
-rw-r--r--qemu/qga/commands-win32.c1455
-rw-r--r--qemu/qga/commands.c501
-rw-r--r--qemu/qga/guest-agent-command-state.c74
-rw-r--r--qemu/qga/guest-agent-core.h45
-rw-r--r--qemu/qga/installer/qemu-ga.wxs185
-rw-r--r--qemu/qga/main.c1394
-rw-r--r--qemu/qga/qapi-schema.json1028
-rw-r--r--qemu/qga/service-win32.c191
-rw-r--r--qemu/qga/service-win32.h31
-rw-r--r--qemu/qga/vss-win32.c166
-rw-r--r--qemu/qga/vss-win32.h26
-rw-r--r--qemu/qga/vss-win32/Makefile.objs23
-rw-r--r--qemu/qga/vss-win32/install.cpp464
-rw-r--r--qemu/qga/vss-win32/provider.cpp534
-rw-r--r--qemu/qga/vss-win32/qga-vss.def13
-rw-r--r--qemu/qga/vss-win32/qga-vss.idl20
-rw-r--r--qemu/qga/vss-win32/qga-vss.tlbbin1528 -> 0 bytes
-rw-r--r--qemu/qga/vss-win32/requester.cpp504
-rw-r--r--qemu/qga/vss-win32/requester.h45
-rw-r--r--qemu/qga/vss-win32/vss-common.h128
25 files changed, 0 insertions, 10012 deletions
diff --git a/qemu/qga/Makefile.objs b/qemu/qga/Makefile.objs
deleted file mode 100644
index 1c5986c0b..000000000
--- a/qemu/qga/Makefile.objs
+++ /dev/null
@@ -1,8 +0,0 @@
-qga-obj-y = commands.o guest-agent-command-state.o main.o
-qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
-qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
-qga-obj-$(CONFIG_WIN32) += vss-win32.o
-qga-obj-y += qapi-generated/qga-qapi-types.o qapi-generated/qga-qapi-visit.o
-qga-obj-y += qapi-generated/qga-qmp-marshal.o
-
-qga-vss-dll-obj-$(CONFIG_QGA_VSS) += vss-win32/
diff --git a/qemu/qga/channel-posix.c b/qemu/qga/channel-posix.c
deleted file mode 100644
index 63458c663..000000000
--- a/qemu/qga/channel-posix.c
+++ /dev/null
@@ -1,270 +0,0 @@
-#include "qemu/osdep.h"
-#include <glib.h>
-#include <termios.h>
-#include "qapi/error.h"
-#include "qemu/sockets.h"
-#include "qga/channel.h"
-
-#ifdef CONFIG_SOLARIS
-#include <stropts.h>
-#endif
-
-#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;
- }
- qemu_set_nonblock(client_fd);
- ret = ga_channel_client_add(c, client_fd);
- if (ret) {
- g_warning("error setting up connection");
- close(client_fd);
- 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
-#ifndef CONFIG_SOLARIS
- | O_ASYNC
-#endif
- );
- if (fd == -1) {
- g_critical("error opening channel: %s", strerror(errno));
- return false;
- }
-#ifdef CONFIG_SOLARIS
- ret = ioctl(fd, I_SETSIG, S_OUTPUT | S_INPUT | S_HIPRI);
- if (ret == -1) {
- g_critical("error setting event mask for channel: %s",
- strerror(errno));
- close(fd);
- return false;
- }
-#endif
- ret = ga_channel_client_add(c, fd);
- if (ret) {
- g_critical("error adding channel to main loop");
- close(fd);
- 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));
- return false;
- }
- 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_critical("error adding channel to main loop");
- close(fd);
- return false;
- }
- break;
- }
- case GA_CHANNEL_UNIX_LISTEN: {
- Error *local_err = NULL;
- int fd = unix_listen(path, NULL, strlen(path), &local_err);
- if (local_err != NULL) {
- g_critical("%s", error_get_pretty(local_err));
- error_free(local_err);
- 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) {
- g_debug("sending data, count: %d", (int)size);
- status = g_io_channel_write_chars(c->client_channel, buf, size,
- &written, &err);
- if (status == G_IO_STATUS_NORMAL) {
- size -= written;
- buf += written;
- } else if (status != G_IO_STATUS_AGAIN) {
- g_warning("error writing to channel: %s", err->message);
- return status;
- }
- }
-
- do {
- status = g_io_channel_flush(c->client_channel, &err);
- } while (status == G_IO_STATUS_AGAIN);
-
- if (status != G_IO_STATUS_NORMAL) {
- g_warning("error flushing channel: %s", err->message);
- }
-
- 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_new0(GAChannel, 1);
- 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/qemu/qga/channel-win32.c b/qemu/qga/channel-win32.c
deleted file mode 100644
index bb5966124..000000000
--- a/qemu/qga/channel-win32.c
+++ /dev/null
@@ -1,357 +0,0 @@
-#include "qemu/osdep.h"
-#include <glib.h>
-#include <windows.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;
- }
-
- if (ov.hEvent) {
- CloseHandle(ov.hEvent);
- ov.hEvent = NULL;
- }
- return status;
-}
-
-GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
-{
- GIOStatus status = G_IO_STATUS_NORMAL;
- size_t count = 0;
-
- 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)
-{
- COMMTIMEOUTS comTimeOut = {0};
- gchar newpath[MAXPATHLEN] = {0};
- comTimeOut.ReadIntervalTimeout = 1;
-
- if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
- g_critical("unsupported communication method");
- return false;
- }
-
- if (method == GA_CHANNEL_ISA_SERIAL){
- snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
- }else {
- g_strlcpy(newpath, path, sizeof(newpath));
- }
-
- c->handle = CreateFile(newpath, 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 %s", newpath);
- return false;
- }
-
- if (method == GA_CHANNEL_ISA_SERIAL && !SetCommTimeouts(c->handle,&comTimeOut)) {
- g_critical("error setting timeout for com port: %lu",GetLastError());
- CloseHandle(c->handle);
- return false;
- }
-
- return true;
-}
-
-GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
- GAChannelCallback cb, gpointer opaque)
-{
- GAChannel *c = g_new0(GAChannel, 1);
- 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/qemu/qga/channel.h b/qemu/qga/channel.h
deleted file mode 100644
index 3704ea9c8..000000000
--- a/qemu/qga/channel.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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/qemu/qga/commands-posix.c b/qemu/qga/commands-posix.c
deleted file mode 100644
index 2ae37255d..000000000
--- a/qemu/qga/commands-posix.c
+++ /dev/null
@@ -1,2517 +0,0 @@
-/*
- * QEMU Guest Agent POSIX-specific command implementations
- *
- * Copyright IBM Corp. 2011
- *
- * Authors:
- * Michael Roth <mdroth@linux.vnet.ibm.com>
- * Michal Privoznik <mprivozn@redhat.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 "qemu/osdep.h"
-#include <glib.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <dirent.h>
-#include "qga/guest-agent-core.h"
-#include "qga-qmp-commands.h"
-#include "qapi/qmp/qerror.h"
-#include "qemu/queue.h"
-#include "qemu/host-utils.h"
-#include "qemu/sockets.h"
-#include "qemu/base64.h"
-#include "qemu/cutils.h"
-
-#ifndef CONFIG_HAS_ENVIRON
-#ifdef __APPLE__
-#include <crt_externs.h>
-#define environ (*_NSGetEnviron())
-#else
-extern char **environ;
-#endif
-#endif
-
-#if defined(__linux__)
-#include <mntent.h>
-#include <linux/fs.h>
-#include <ifaddrs.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <net/if.h>
-
-#ifdef FIFREEZE
-#define CONFIG_FSFREEZE
-#endif
-#ifdef FITRIM
-#define CONFIG_FSTRIM
-#endif
-#endif
-
-static void ga_wait_child(pid_t pid, int *status, Error **errp)
-{
- pid_t rpid;
-
- *status = 0;
-
- do {
- rpid = waitpid(pid, status, 0);
- } while (rpid == -1 && errno == EINTR);
-
- if (rpid == -1) {
- error_setg_errno(errp, errno, "failed to wait for child (pid: %d)",
- pid);
- return;
- }
-
- g_assert(rpid == pid);
-}
-
-void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
-{
- const char *shutdown_flag;
- Error *local_err = NULL;
- pid_t pid;
- int status;
-
- slog("guest-shutdown called, mode: %s", mode);
- if (!has_mode || strcmp(mode, "powerdown") == 0) {
- shutdown_flag = "-P";
- } else if (strcmp(mode, "halt") == 0) {
- shutdown_flag = "-H";
- } else if (strcmp(mode, "reboot") == 0) {
- shutdown_flag = "-r";
- } else {
- error_setg(errp,
- "mode is invalid (valid values are: halt|powerdown|reboot");
- return;
- }
-
- pid = fork();
- if (pid == 0) {
- /* child, start the shutdown */
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char*)NULL, environ);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to shutdown");
- return;
- }
-
- /* succeeded */
-}
-
-int64_t qmp_guest_get_time(Error **errp)
-{
- int ret;
- qemu_timeval tq;
- int64_t time_ns;
-
- ret = qemu_gettimeofday(&tq);
- if (ret < 0) {
- error_setg_errno(errp, errno, "Failed to get time");
- return -1;
- }
-
- time_ns = tq.tv_sec * 1000000000LL + tq.tv_usec * 1000;
- return time_ns;
-}
-
-void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
-{
- int ret;
- int status;
- pid_t pid;
- Error *local_err = NULL;
- struct timeval tv;
-
- /* If user has passed a time, validate and set it. */
- if (has_time) {
- GDate date = { 0, };
-
- /* year-2038 will overflow in case time_t is 32bit */
- if (time_ns / 1000000000 != (time_t)(time_ns / 1000000000)) {
- error_setg(errp, "Time %" PRId64 " is too large", time_ns);
- return;
- }
-
- tv.tv_sec = time_ns / 1000000000;
- tv.tv_usec = (time_ns % 1000000000) / 1000;
- g_date_set_time_t(&date, tv.tv_sec);
- if (date.year < 1970 || date.year >= 2070) {
- error_setg_errno(errp, errno, "Invalid time");
- return;
- }
-
- ret = settimeofday(&tv, NULL);
- if (ret < 0) {
- error_setg_errno(errp, errno, "Failed to set time to guest");
- return;
- }
- }
-
- /* Now, if user has passed a time to set and the system time is set, we
- * just need to synchronize the hardware clock. However, if no time was
- * passed, user is requesting the opposite: set the system time from the
- * hardware clock (RTC). */
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- /* Use '/sbin/hwclock -w' to set RTC from the system time,
- * or '/sbin/hwclock -s' to set the system time from RTC. */
- execle("/sbin/hwclock", "hwclock", has_time ? "-w" : "-s",
- NULL, environ);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "hwclock failed to set hardware clock to system time");
- return;
- }
-}
-
-typedef enum {
- RW_STATE_NEW,
- RW_STATE_READING,
- RW_STATE_WRITING,
-} RwState;
-
-typedef struct GuestFileHandle {
- uint64_t id;
- FILE *fh;
- RwState state;
- QTAILQ_ENTRY(GuestFileHandle) next;
-} GuestFileHandle;
-
-static struct {
- QTAILQ_HEAD(, GuestFileHandle) filehandles;
-} guest_file_state = {
- .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
-};
-
-static int64_t guest_file_handle_add(FILE *fh, Error **errp)
-{
- GuestFileHandle *gfh;
- int64_t handle;
-
- handle = ga_get_fd_handle(ga_state, errp);
- if (handle < 0) {
- return -1;
- }
-
- gfh = g_new0(GuestFileHandle, 1);
- gfh->id = handle;
- gfh->fh = fh;
- QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
-
- return handle;
-}
-
-static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
-{
- GuestFileHandle *gfh;
-
- QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next)
- {
- if (gfh->id == id) {
- return gfh;
- }
- }
-
- error_setg(errp, "handle '%" PRId64 "' has not been found", id);
- return NULL;
-}
-
-typedef const char * const ccpc;
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-/* http://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html */
-static const struct {
- ccpc *forms;
- int oflag_base;
-} guest_file_open_modes[] = {
- { (ccpc[]){ "r", NULL }, O_RDONLY },
- { (ccpc[]){ "rb", NULL }, O_RDONLY | O_BINARY },
- { (ccpc[]){ "w", NULL }, O_WRONLY | O_CREAT | O_TRUNC },
- { (ccpc[]){ "wb", NULL }, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY },
- { (ccpc[]){ "a", NULL }, O_WRONLY | O_CREAT | O_APPEND },
- { (ccpc[]){ "ab", NULL }, O_WRONLY | O_CREAT | O_APPEND | O_BINARY },
- { (ccpc[]){ "r+", NULL }, O_RDWR },
- { (ccpc[]){ "rb+", "r+b", NULL }, O_RDWR | O_BINARY },
- { (ccpc[]){ "w+", NULL }, O_RDWR | O_CREAT | O_TRUNC },
- { (ccpc[]){ "wb+", "w+b", NULL }, O_RDWR | O_CREAT | O_TRUNC | O_BINARY },
- { (ccpc[]){ "a+", NULL }, O_RDWR | O_CREAT | O_APPEND },
- { (ccpc[]){ "ab+", "a+b", NULL }, O_RDWR | O_CREAT | O_APPEND | O_BINARY }
-};
-
-static int
-find_open_flag(const char *mode_str, Error **errp)
-{
- unsigned mode;
-
- for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
- ccpc *form;
-
- form = guest_file_open_modes[mode].forms;
- while (*form != NULL && strcmp(*form, mode_str) != 0) {
- ++form;
- }
- if (*form != NULL) {
- break;
- }
- }
-
- if (mode == ARRAY_SIZE(guest_file_open_modes)) {
- error_setg(errp, "invalid file open mode '%s'", mode_str);
- return -1;
- }
- return guest_file_open_modes[mode].oflag_base | O_NOCTTY | O_NONBLOCK;
-}
-
-#define DEFAULT_NEW_FILE_MODE (S_IRUSR | S_IWUSR | \
- S_IRGRP | S_IWGRP | \
- S_IROTH | S_IWOTH)
-
-static FILE *
-safe_open_or_create(const char *path, const char *mode, Error **errp)
-{
- Error *local_err = NULL;
- int oflag;
-
- oflag = find_open_flag(mode, &local_err);
- if (local_err == NULL) {
- int fd;
-
- /* If the caller wants / allows creation of a new file, we implement it
- * with a two step process: open() + (open() / fchmod()).
- *
- * First we insist on creating the file exclusively as a new file. If
- * that succeeds, we're free to set any file-mode bits on it. (The
- * motivation is that we want to set those file-mode bits independently
- * of the current umask.)
- *
- * If the exclusive creation fails because the file already exists
- * (EEXIST is not possible for any other reason), we just attempt to
- * open the file, but in this case we won't be allowed to change the
- * file-mode bits on the preexistent file.
- *
- * The pathname should never disappear between the two open()s in
- * practice. If it happens, then someone very likely tried to race us.
- * In this case just go ahead and report the ENOENT from the second
- * open() to the caller.
- *
- * If the caller wants to open a preexistent file, then the first
- * open() is decisive and its third argument is ignored, and the second
- * open() and the fchmod() are never called.
- */
- fd = open(path, oflag | ((oflag & O_CREAT) ? O_EXCL : 0), 0);
- if (fd == -1 && errno == EEXIST) {
- oflag &= ~(unsigned)O_CREAT;
- fd = open(path, oflag);
- }
-
- if (fd == -1) {
- error_setg_errno(&local_err, errno, "failed to open file '%s' "
- "(mode: '%s')", path, mode);
- } else {
- qemu_set_cloexec(fd);
-
- if ((oflag & O_CREAT) && fchmod(fd, DEFAULT_NEW_FILE_MODE) == -1) {
- error_setg_errno(&local_err, errno, "failed to set permission "
- "0%03o on new file '%s' (mode: '%s')",
- (unsigned)DEFAULT_NEW_FILE_MODE, path, mode);
- } else {
- FILE *f;
-
- f = fdopen(fd, mode);
- if (f == NULL) {
- error_setg_errno(&local_err, errno, "failed to associate "
- "stdio stream with file descriptor %d, "
- "file '%s' (mode: '%s')", fd, path, mode);
- } else {
- return f;
- }
- }
-
- close(fd);
- if (oflag & O_CREAT) {
- unlink(path);
- }
- }
- }
-
- error_propagate(errp, local_err);
- return NULL;
-}
-
-int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode,
- Error **errp)
-{
- FILE *fh;
- Error *local_err = NULL;
- int64_t handle;
-
- if (!has_mode) {
- mode = "r";
- }
- slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
- fh = safe_open_or_create(path, mode, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return -1;
- }
-
- /* set fd non-blocking to avoid common use cases (like reading from a
- * named pipe) from hanging the agent
- */
- qemu_set_nonblock(fileno(fh));
-
- handle = guest_file_handle_add(fh, errp);
- if (handle < 0) {
- fclose(fh);
- return -1;
- }
-
- slog("guest-file-open, handle: %" PRId64, handle);
- return handle;
-}
-
-void qmp_guest_file_close(int64_t handle, Error **errp)
-{
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- int ret;
-
- slog("guest-file-close called, handle: %" PRId64, handle);
- if (!gfh) {
- return;
- }
-
- ret = fclose(gfh->fh);
- if (ret == EOF) {
- error_setg_errno(errp, errno, "failed to close handle");
- return;
- }
-
- QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
- g_free(gfh);
-}
-
-struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
- int64_t count, Error **errp)
-{
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- GuestFileRead *read_data = NULL;
- guchar *buf;
- FILE *fh;
- size_t read_count;
-
- if (!gfh) {
- return NULL;
- }
-
- if (!has_count) {
- count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
- error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
- count);
- return NULL;
- }
-
- fh = gfh->fh;
-
- /* explicitly flush when switching from writing to reading */
- if (gfh->state == RW_STATE_WRITING) {
- int ret = fflush(fh);
- if (ret == EOF) {
- error_setg_errno(errp, errno, "failed to flush file");
- return NULL;
- }
- gfh->state = RW_STATE_NEW;
- }
-
- buf = g_malloc0(count+1);
- read_count = fread(buf, 1, count, fh);
- if (ferror(fh)) {
- error_setg_errno(errp, errno, "failed to read file");
- slog("guest-file-read failed, handle: %" PRId64, handle);
- } else {
- buf[read_count] = 0;
- read_data = g_new0(GuestFileRead, 1);
- read_data->count = read_count;
- read_data->eof = feof(fh);
- if (read_count) {
- read_data->buf_b64 = g_base64_encode(buf, read_count);
- }
- gfh->state = RW_STATE_READING;
- }
- g_free(buf);
- clearerr(fh);
-
- return read_data;
-}
-
-GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
- bool has_count, int64_t count,
- Error **errp)
-{
- GuestFileWrite *write_data = NULL;
- guchar *buf;
- gsize buf_len;
- int write_count;
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- FILE *fh;
-
- if (!gfh) {
- return NULL;
- }
-
- fh = gfh->fh;
-
- if (gfh->state == RW_STATE_READING) {
- int ret = fseek(fh, 0, SEEK_CUR);
- if (ret == -1) {
- error_setg_errno(errp, errno, "failed to seek file");
- return NULL;
- }
- gfh->state = RW_STATE_NEW;
- }
-
- buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
- if (!buf) {
- return NULL;
- }
-
- if (!has_count) {
- count = buf_len;
- } else if (count < 0 || count > buf_len) {
- error_setg(errp, "value '%" PRId64 "' is invalid for argument count",
- count);
- g_free(buf);
- return NULL;
- }
-
- write_count = fwrite(buf, 1, count, fh);
- if (ferror(fh)) {
- error_setg_errno(errp, errno, "failed to write to file");
- slog("guest-file-write failed, handle: %" PRId64, handle);
- } else {
- write_data = g_new0(GuestFileWrite, 1);
- write_data->count = write_count;
- write_data->eof = feof(fh);
- gfh->state = RW_STATE_WRITING;
- }
- g_free(buf);
- clearerr(fh);
-
- return write_data;
-}
-
-struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
- GuestFileWhence *whence_code,
- Error **errp)
-{
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- GuestFileSeek *seek_data = NULL;
- FILE *fh;
- int ret;
- int whence;
- Error *err = NULL;
-
- if (!gfh) {
- return NULL;
- }
-
- /* We stupidly exposed 'whence':'int' in our qapi */
- whence = ga_parse_whence(whence_code, &err);
- if (err) {
- error_propagate(errp, err);
- return NULL;
- }
-
- fh = gfh->fh;
- ret = fseek(fh, offset, whence);
- if (ret == -1) {
- error_setg_errno(errp, errno, "failed to seek file");
- if (errno == ESPIPE) {
- /* file is non-seekable, stdio shouldn't be buffering anyways */
- gfh->state = RW_STATE_NEW;
- }
- } else {
- seek_data = g_new0(GuestFileSeek, 1);
- seek_data->position = ftell(fh);
- seek_data->eof = feof(fh);
- gfh->state = RW_STATE_NEW;
- }
- clearerr(fh);
-
- return seek_data;
-}
-
-void qmp_guest_file_flush(int64_t handle, Error **errp)
-{
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- FILE *fh;
- int ret;
-
- if (!gfh) {
- return;
- }
-
- fh = gfh->fh;
- ret = fflush(fh);
- if (ret == EOF) {
- error_setg_errno(errp, errno, "failed to flush file");
- } else {
- gfh->state = RW_STATE_NEW;
- }
-}
-
-/* linux-specific implementations. avoid this if at all possible. */
-#if defined(__linux__)
-
-#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
-typedef struct FsMount {
- char *dirname;
- char *devtype;
- unsigned int devmajor, devminor;
- QTAILQ_ENTRY(FsMount) next;
-} FsMount;
-
-typedef QTAILQ_HEAD(FsMountList, FsMount) FsMountList;
-
-static void free_fs_mount_list(FsMountList *mounts)
-{
- FsMount *mount, *temp;
-
- if (!mounts) {
- return;
- }
-
- QTAILQ_FOREACH_SAFE(mount, mounts, next, temp) {
- QTAILQ_REMOVE(mounts, mount, next);
- g_free(mount->dirname);
- g_free(mount->devtype);
- g_free(mount);
- }
-}
-
-static int dev_major_minor(const char *devpath,
- unsigned int *devmajor, unsigned int *devminor)
-{
- struct stat st;
-
- *devmajor = 0;
- *devminor = 0;
-
- if (stat(devpath, &st) < 0) {
- slog("failed to stat device file '%s': %s", devpath, strerror(errno));
- return -1;
- }
- if (S_ISDIR(st.st_mode)) {
- /* It is bind mount */
- return -2;
- }
- if (S_ISBLK(st.st_mode)) {
- *devmajor = major(st.st_rdev);
- *devminor = minor(st.st_rdev);
- return 0;
- }
- return -1;
-}
-
-/*
- * Walk the mount table and build a list of local file systems
- */
-static void build_fs_mount_list_from_mtab(FsMountList *mounts, Error **errp)
-{
- struct mntent *ment;
- FsMount *mount;
- char const *mtab = "/proc/self/mounts";
- FILE *fp;
- unsigned int devmajor, devminor;
-
- fp = setmntent(mtab, "r");
- if (!fp) {
- error_setg(errp, "failed to open mtab file: '%s'", mtab);
- return;
- }
-
- while ((ment = getmntent(fp))) {
- /*
- * An entry which device name doesn't start with a '/' is
- * either a dummy file system or a network file system.
- * Add special handling for smbfs and cifs as is done by
- * coreutils as well.
- */
- if ((ment->mnt_fsname[0] != '/') ||
- (strcmp(ment->mnt_type, "smbfs") == 0) ||
- (strcmp(ment->mnt_type, "cifs") == 0)) {
- continue;
- }
- if (dev_major_minor(ment->mnt_fsname, &devmajor, &devminor) == -2) {
- /* Skip bind mounts */
- continue;
- }
-
- mount = g_new0(FsMount, 1);
- mount->dirname = g_strdup(ment->mnt_dir);
- mount->devtype = g_strdup(ment->mnt_type);
- mount->devmajor = devmajor;
- mount->devminor = devminor;
-
- QTAILQ_INSERT_TAIL(mounts, mount, next);
- }
-
- endmntent(fp);
-}
-
-static void decode_mntname(char *name, int len)
-{
- int i, j = 0;
- for (i = 0; i <= len; i++) {
- if (name[i] != '\\') {
- name[j++] = name[i];
- } else if (name[i + 1] == '\\') {
- name[j++] = '\\';
- i++;
- } else if (name[i + 1] >= '0' && name[i + 1] <= '3' &&
- name[i + 2] >= '0' && name[i + 2] <= '7' &&
- name[i + 3] >= '0' && name[i + 3] <= '7') {
- name[j++] = (name[i + 1] - '0') * 64 +
- (name[i + 2] - '0') * 8 +
- (name[i + 3] - '0');
- i += 3;
- } else {
- name[j++] = name[i];
- }
- }
-}
-
-static void build_fs_mount_list(FsMountList *mounts, Error **errp)
-{
- FsMount *mount;
- char const *mountinfo = "/proc/self/mountinfo";
- FILE *fp;
- char *line = NULL, *dash;
- size_t n;
- char check;
- unsigned int devmajor, devminor;
- int ret, dir_s, dir_e, type_s, type_e, dev_s, dev_e;
-
- fp = fopen(mountinfo, "r");
- if (!fp) {
- build_fs_mount_list_from_mtab(mounts, errp);
- return;
- }
-
- while (getline(&line, &n, fp) != -1) {
- ret = sscanf(line, "%*u %*u %u:%u %*s %n%*s%n%c",
- &devmajor, &devminor, &dir_s, &dir_e, &check);
- if (ret < 3) {
- continue;
- }
- dash = strstr(line + dir_e, " - ");
- if (!dash) {
- continue;
- }
- ret = sscanf(dash, " - %n%*s%n %n%*s%n%c",
- &type_s, &type_e, &dev_s, &dev_e, &check);
- if (ret < 1) {
- continue;
- }
- line[dir_e] = 0;
- dash[type_e] = 0;
- dash[dev_e] = 0;
- decode_mntname(line + dir_s, dir_e - dir_s);
- decode_mntname(dash + dev_s, dev_e - dev_s);
- if (devmajor == 0) {
- /* btrfs reports major number = 0 */
- if (strcmp("btrfs", dash + type_s) != 0 ||
- dev_major_minor(dash + dev_s, &devmajor, &devminor) < 0) {
- continue;
- }
- }
-
- mount = g_new0(FsMount, 1);
- mount->dirname = g_strdup(line + dir_s);
- mount->devtype = g_strdup(dash + type_s);
- mount->devmajor = devmajor;
- mount->devminor = devminor;
-
- QTAILQ_INSERT_TAIL(mounts, mount, next);
- }
- free(line);
-
- fclose(fp);
-}
-#endif
-
-#if defined(CONFIG_FSFREEZE)
-
-static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
-{
- char *path;
- char *dpath;
- char *driver = NULL;
- char buf[PATH_MAX];
- ssize_t len;
-
- path = g_strndup(syspath, pathlen);
- dpath = g_strdup_printf("%s/driver", path);
- len = readlink(dpath, buf, sizeof(buf) - 1);
- if (len != -1) {
- buf[len] = 0;
- driver = g_strdup(basename(buf));
- }
- g_free(dpath);
- g_free(path);
- return driver;
-}
-
-static int compare_uint(const void *_a, const void *_b)
-{
- unsigned int a = *(unsigned int *)_a;
- unsigned int b = *(unsigned int *)_b;
-
- return a < b ? -1 : a > b ? 1 : 0;
-}
-
-/* Walk the specified sysfs and build a sorted list of host or ata numbers */
-static int build_hosts(char const *syspath, char const *host, bool ata,
- unsigned int *hosts, int hosts_max, Error **errp)
-{
- char *path;
- DIR *dir;
- struct dirent *entry;
- int i = 0;
-
- path = g_strndup(syspath, host - syspath);
- dir = opendir(path);
- if (!dir) {
- error_setg_errno(errp, errno, "opendir(\"%s\")", path);
- g_free(path);
- return -1;
- }
-
- while (i < hosts_max) {
- entry = readdir(dir);
- if (!entry) {
- break;
- }
- if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
- ++i;
- } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
- ++i;
- }
- }
-
- qsort(hosts, i, sizeof(hosts[0]), compare_uint);
-
- g_free(path);
- closedir(dir);
- return i;
-}
-
-/* Store disk device info specified by @sysfs into @fs */
-static void build_guest_fsinfo_for_real_device(char const *syspath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- unsigned int pci[4], host, hosts[8], tgt[3];
- int i, nhosts = 0, pcilen;
- GuestDiskAddress *disk;
- GuestPCIAddress *pciaddr;
- GuestDiskAddressList *list = NULL;
- bool has_ata = false, has_host = false, has_tgt = false;
- char *p, *q, *driver = NULL;
-
- p = strstr(syspath, "/devices/pci");
- if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
- pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
- g_debug("only pci device is supported: sysfs path \"%s\"", syspath);
- return;
- }
-
- driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp);
- if (!driver) {
- goto cleanup;
- }
-
- p = strstr(syspath, "/target");
- if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
- tgt, tgt + 1, tgt + 2) == 3) {
- has_tgt = true;
- }
-
- p = strstr(syspath, "/ata");
- if (p) {
- q = p + 4;
- has_ata = true;
- } else {
- p = strstr(syspath, "/host");
- q = p + 5;
- }
- if (p && sscanf(q, "%u", &host) == 1) {
- has_host = true;
- nhosts = build_hosts(syspath, p, has_ata, hosts,
- sizeof(hosts) / sizeof(hosts[0]), errp);
- if (nhosts < 0) {
- goto cleanup;
- }
- }
-
- pciaddr = g_malloc0(sizeof(*pciaddr));
- pciaddr->domain = pci[0];
- pciaddr->bus = pci[1];
- pciaddr->slot = pci[2];
- pciaddr->function = pci[3];
-
- disk = g_malloc0(sizeof(*disk));
- disk->pci_controller = pciaddr;
-
- list = g_malloc0(sizeof(*list));
- list->value = disk;
-
- if (strcmp(driver, "ata_piix") == 0) {
- /* a host per ide bus, target*:0:<unit>:0 */
- if (!has_host || !has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- for (i = 0; i < nhosts; i++) {
- if (host == hosts[i]) {
- disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
- disk->bus = i;
- disk->unit = tgt[1];
- break;
- }
- }
- if (i >= nhosts) {
- g_debug("no host for '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- } else if (strcmp(driver, "sym53c8xx") == 0) {
- /* scsi(LSI Logic): target*:0:<unit>:0 */
- if (!has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
- disk->unit = tgt[1];
- } else if (strcmp(driver, "virtio-pci") == 0) {
- if (has_tgt) {
- /* virtio-scsi: target*:0:0:<unit> */
- disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
- disk->unit = tgt[2];
- } else {
- /* virtio-blk: 1 disk per 1 device */
- disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
- }
- } else if (strcmp(driver, "ahci") == 0) {
- /* ahci: 1 host per 1 unit */
- if (!has_host || !has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- for (i = 0; i < nhosts; i++) {
- if (host == hosts[i]) {
- disk->unit = i;
- disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
- break;
- }
- }
- if (i >= nhosts) {
- g_debug("no host for '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- } else {
- g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
- goto cleanup;
- }
-
- list->next = fs->disk;
- fs->disk = list;
- g_free(driver);
- return;
-
-cleanup:
- if (list) {
- qapi_free_GuestDiskAddressList(list);
- }
- g_free(driver);
-}
-
-static void build_guest_fsinfo_for_device(char const *devpath,
- GuestFilesystemInfo *fs,
- Error **errp);
-
-/* Store a list of slave devices of virtual volume specified by @syspath into
- * @fs */
-static void build_guest_fsinfo_for_virtual_device(char const *syspath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- DIR *dir;
- char *dirpath;
- struct dirent *entry;
-
- dirpath = g_strdup_printf("%s/slaves", syspath);
- dir = opendir(dirpath);
- if (!dir) {
- error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
- g_free(dirpath);
- return;
- }
-
- for (;;) {
- errno = 0;
- entry = readdir(dir);
- if (entry == NULL) {
- if (errno) {
- error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
- }
- break;
- }
-
- if (entry->d_type == DT_LNK) {
- char *path;
-
- g_debug(" slave device '%s'", entry->d_name);
- path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
- build_guest_fsinfo_for_device(path, fs, errp);
- g_free(path);
-
- if (*errp) {
- break;
- }
- }
- }
-
- g_free(dirpath);
- closedir(dir);
-}
-
-/* Dispatch to functions for virtual/real device */
-static void build_guest_fsinfo_for_device(char const *devpath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- char *syspath = realpath(devpath, NULL);
-
- if (!syspath) {
- error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
- return;
- }
-
- if (!fs->name) {
- fs->name = g_strdup(basename(syspath));
- }
-
- g_debug(" parse sysfs path '%s'", syspath);
-
- if (strstr(syspath, "/devices/virtual/block/")) {
- build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
- } else {
- build_guest_fsinfo_for_real_device(syspath, fs, errp);
- }
-
- free(syspath);
-}
-
-/* Return a list of the disk device(s)' info which @mount lies on */
-static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
- Error **errp)
-{
- GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
- char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
- mount->devmajor, mount->devminor);
-
- fs->mountpoint = g_strdup(mount->dirname);
- fs->type = g_strdup(mount->devtype);
- build_guest_fsinfo_for_device(devpath, fs, errp);
-
- g_free(devpath);
- return fs;
-}
-
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- FsMountList mounts;
- struct FsMount *mount;
- GuestFilesystemInfoList *new, *ret = NULL;
- Error *local_err = NULL;
-
- QTAILQ_INIT(&mounts);
- build_fs_mount_list(&mounts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return NULL;
- }
-
- QTAILQ_FOREACH(mount, &mounts, next) {
- g_debug("Building guest fsinfo for '%s'", mount->dirname);
-
- new = g_malloc0(sizeof(*ret));
- new->value = build_guest_fsinfo(mount, &local_err);
- new->next = ret;
- ret = new;
- if (local_err) {
- error_propagate(errp, local_err);
- qapi_free_GuestFilesystemInfoList(ret);
- ret = NULL;
- break;
- }
- }
-
- free_fs_mount_list(&mounts);
- return ret;
-}
-
-
-typedef enum {
- FSFREEZE_HOOK_THAW = 0,
- FSFREEZE_HOOK_FREEZE,
-} FsfreezeHookArg;
-
-static const char *fsfreeze_hook_arg_string[] = {
- "thaw",
- "freeze",
-};
-
-static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
-{
- int status;
- pid_t pid;
- const char *hook;
- const char *arg_str = fsfreeze_hook_arg_string[arg];
- Error *local_err = NULL;
-
- hook = ga_fsfreeze_hook(ga_state);
- if (!hook) {
- return;
- }
- if (access(hook, X_OK) != 0) {
- error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
- return;
- }
-
- slog("executing fsfreeze hook with arg '%s'", arg_str);
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- execle(hook, hook, arg_str, NULL, environ);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "fsfreeze hook has terminated abnormally");
- return;
- }
-
- status = WEXITSTATUS(status);
- if (status) {
- error_setg(errp, "fsfreeze hook has failed with status %d", status);
- return;
- }
-}
-
-/*
- * Return status of freeze/thaw
- */
-GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
-{
- if (ga_is_frozen(ga_state)) {
- return GUEST_FSFREEZE_STATUS_FROZEN;
- }
-
- return GUEST_FSFREEZE_STATUS_THAWED;
-}
-
-int64_t qmp_guest_fsfreeze_freeze(Error **errp)
-{
- return qmp_guest_fsfreeze_freeze_list(false, NULL, errp);
-}
-
-/*
- * 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_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- int ret = 0, i = 0;
- strList *list;
- FsMountList mounts;
- struct FsMount *mount;
- Error *local_err = NULL;
- int fd;
-
- slog("guest-fsfreeze called");
-
- execute_fsfreeze_hook(FSFREEZE_HOOK_FREEZE, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return -1;
- }
-
- QTAILQ_INIT(&mounts);
- build_fs_mount_list(&mounts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return -1;
- }
-
- /* cannot risk guest agent blocking itself on a write in this state */
- ga_set_frozen(ga_state);
-
- QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) {
- /* To issue fsfreeze in the reverse order of mounts, check if the
- * mount is listed in the list here */
- if (has_mountpoints) {
- for (list = mountpoints; list; list = list->next) {
- if (strcmp(list->value, mount->dirname) == 0) {
- break;
- }
- }
- if (!list) {
- continue;
- }
- }
-
- fd = qemu_open(mount->dirname, O_RDONLY);
- if (fd == -1) {
- error_setg_errno(errp, errno, "failed to open %s", mount->dirname);
- goto error;
- }
-
- /* we try to cull filesytems we know won't work in advance, but other
- * filesytems may not implement fsfreeze for less obvious reasons.
- * these will report EOPNOTSUPP. we simply ignore these when tallying
- * the number of frozen filesystems.
- *
- * any other error means a failure to freeze a filesystem we
- * expect to be freezable, so return an error in those cases
- * and return system to thawed state.
- */
- ret = ioctl(fd, FIFREEZE);
- if (ret == -1) {
- if (errno != EOPNOTSUPP) {
- error_setg_errno(errp, errno, "failed to freeze %s",
- mount->dirname);
- close(fd);
- goto error;
- }
- } else {
- i++;
- }
- close(fd);
- }
-
- free_fs_mount_list(&mounts);
- return i;
-
-error:
- free_fs_mount_list(&mounts);
- qmp_guest_fsfreeze_thaw(NULL);
- return 0;
-}
-
-/*
- * Walk list of frozen file systems in the guest, and thaw them.
- */
-int64_t qmp_guest_fsfreeze_thaw(Error **errp)
-{
- int ret;
- FsMountList mounts;
- FsMount *mount;
- int fd, i = 0, logged;
- Error *local_err = NULL;
-
- QTAILQ_INIT(&mounts);
- build_fs_mount_list(&mounts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return 0;
- }
-
- QTAILQ_FOREACH(mount, &mounts, next) {
- logged = false;
- fd = qemu_open(mount->dirname, O_RDONLY);
- if (fd == -1) {
- continue;
- }
- /* we have no way of knowing whether a filesystem was actually unfrozen
- * as a result of a successful call to FITHAW, only that if an error
- * was returned the filesystem was *not* unfrozen by that particular
- * call.
- *
- * since multiple preceding FIFREEZEs require multiple calls to FITHAW
- * to unfreeze, continuing issuing FITHAW until an error is returned,
- * in which case either the filesystem is in an unfreezable state, or,
- * more likely, it was thawed previously (and remains so afterward).
- *
- * also, since the most recent successful call is the one that did
- * the actual unfreeze, we can use this to provide an accurate count
- * of the number of filesystems unfrozen by guest-fsfreeze-thaw, which
- * may * be useful for determining whether a filesystem was unfrozen
- * during the freeze/thaw phase by a process other than qemu-ga.
- */
- do {
- ret = ioctl(fd, FITHAW);
- if (ret == 0 && !logged) {
- i++;
- logged = true;
- }
- } while (ret == 0);
- close(fd);
- }
-
- ga_unset_frozen(ga_state);
- free_fs_mount_list(&mounts);
-
- execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp);
-
- return i;
-}
-
-static void guest_fsfreeze_cleanup(void)
-{
- Error *err = NULL;
-
- if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
- qmp_guest_fsfreeze_thaw(&err);
- if (err) {
- slog("failed to clean up frozen filesystems: %s",
- error_get_pretty(err));
- error_free(err);
- }
- }
-}
-#endif /* CONFIG_FSFREEZE */
-
-#if defined(CONFIG_FSTRIM)
-/*
- * Walk list of mounted file systems in the guest, and trim them.
- */
-GuestFilesystemTrimResponse *
-qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
-{
- GuestFilesystemTrimResponse *response;
- GuestFilesystemTrimResultList *list;
- GuestFilesystemTrimResult *result;
- int ret = 0;
- FsMountList mounts;
- struct FsMount *mount;
- int fd;
- Error *local_err = NULL;
- struct fstrim_range r;
-
- slog("guest-fstrim called");
-
- QTAILQ_INIT(&mounts);
- build_fs_mount_list(&mounts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return NULL;
- }
-
- response = g_malloc0(sizeof(*response));
-
- QTAILQ_FOREACH(mount, &mounts, next) {
- result = g_malloc0(sizeof(*result));
- result->path = g_strdup(mount->dirname);
-
- list = g_malloc0(sizeof(*list));
- list->value = result;
- list->next = response->paths;
- response->paths = list;
-
- fd = qemu_open(mount->dirname, O_RDONLY);
- if (fd == -1) {
- result->error = g_strdup_printf("failed to open: %s",
- strerror(errno));
- result->has_error = true;
- continue;
- }
-
- /* We try to cull filesytems we know won't work in advance, but other
- * filesytems may not implement fstrim for less obvious reasons. These
- * will report EOPNOTSUPP; while in some other cases ENOTTY will be
- * reported (e.g. CD-ROMs).
- * Any other error means an unexpected error.
- */
- r.start = 0;
- r.len = -1;
- r.minlen = has_minimum ? minimum : 0;
- ret = ioctl(fd, FITRIM, &r);
- if (ret == -1) {
- result->has_error = true;
- if (errno == ENOTTY || errno == EOPNOTSUPP) {
- result->error = g_strdup("trim not supported");
- } else {
- result->error = g_strdup_printf("failed to trim: %s",
- strerror(errno));
- }
- close(fd);
- continue;
- }
-
- result->has_minimum = true;
- result->minimum = r.minlen;
- result->has_trimmed = true;
- result->trimmed = r.len;
- close(fd);
- }
-
- free_fs_mount_list(&mounts);
- return response;
-}
-#endif /* CONFIG_FSTRIM */
-
-
-#define LINUX_SYS_STATE_FILE "/sys/power/state"
-#define SUSPEND_SUPPORTED 0
-#define SUSPEND_NOT_SUPPORTED 1
-
-static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg,
- const char *sysfile_str, Error **errp)
-{
- Error *local_err = NULL;
- char *pmutils_path;
- pid_t pid;
- int status;
-
- pmutils_path = g_find_program_in_path(pmutils_bin);
-
- pid = fork();
- if (!pid) {
- char buf[32]; /* hopefully big enough */
- ssize_t ret;
- int fd;
-
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ);
- }
-
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
-
- if (!sysfile_str) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
-
- fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
- if (fd < 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
-
- ret = read(fd, buf, sizeof(buf)-1);
- if (ret <= 0) {
- _exit(SUSPEND_NOT_SUPPORTED);
- }
- buf[ret] = '\0';
-
- if (strstr(buf, sysfile_str)) {
- _exit(SUSPEND_SUPPORTED);
- }
-
- _exit(SUSPEND_NOT_SUPPORTED);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto out;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
- }
-
- switch (WEXITSTATUS(status)) {
- case SUSPEND_SUPPORTED:
- goto out;
- case SUSPEND_NOT_SUPPORTED:
- error_setg(errp,
- "the requested suspend mode is not supported by the guest");
- goto out;
- default:
- error_setg(errp,
- "the helper program '%s' returned an unexpected exit status"
- " code (%d)", pmutils_path, WEXITSTATUS(status));
- goto out;
- }
-
-out:
- g_free(pmutils_path);
-}
-
-static void guest_suspend(const char *pmutils_bin, const char *sysfile_str,
- Error **errp)
-{
- Error *local_err = NULL;
- char *pmutils_path;
- pid_t pid;
- int status;
-
- pmutils_path = g_find_program_in_path(pmutils_bin);
-
- pid = fork();
- if (pid == 0) {
- /* child */
- int fd;
-
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- if (pmutils_path) {
- execle(pmutils_path, pmutils_bin, NULL, environ);
- }
-
- /*
- * If we get here either pm-utils is not installed or execle() has
- * failed. Let's try the manual method if the caller wants it.
- */
-
- if (!sysfile_str) {
- _exit(EXIT_FAILURE);
- }
-
- fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
- if (fd < 0) {
- _exit(EXIT_FAILURE);
- }
-
- if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto out;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to suspend");
- goto out;
- }
-
-out:
- g_free(pmutils_path);
-}
-
-void qmp_guest_suspend_disk(Error **errp)
-{
- Error *local_err = NULL;
-
- bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- guest_suspend("pm-hibernate", "disk", errp);
-}
-
-void qmp_guest_suspend_ram(Error **errp)
-{
- Error *local_err = NULL;
-
- bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- guest_suspend("pm-suspend", "mem", errp);
-}
-
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- Error *local_err = NULL;
-
- bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- guest_suspend("pm-suspend-hybrid", NULL, errp);
-}
-
-static GuestNetworkInterfaceList *
-guest_find_interface(GuestNetworkInterfaceList *head,
- const char *name)
-{
- for (; head; head = head->next) {
- if (strcmp(head->value->name, name) == 0) {
- break;
- }
- }
-
- return head;
-}
-
-/*
- * Build information about guest interfaces
- */
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
-{
- GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
- struct ifaddrs *ifap, *ifa;
-
- if (getifaddrs(&ifap) < 0) {
- error_setg_errno(errp, errno, "getifaddrs failed");
- goto error;
- }
-
- for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- GuestNetworkInterfaceList *info;
- GuestIpAddressList **address_list = NULL, *address_item = NULL;
- char addr4[INET_ADDRSTRLEN];
- char addr6[INET6_ADDRSTRLEN];
- int sock;
- struct ifreq ifr;
- unsigned char *mac_addr;
- void *p;
-
- g_debug("Processing %s interface", ifa->ifa_name);
-
- info = guest_find_interface(head, ifa->ifa_name);
-
- if (!info) {
- info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(ifa->ifa_name);
-
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
- }
-
- if (!info->value->has_hardware_address &&
- ifa->ifa_flags & SIOCGIFHWADDR) {
- /* we haven't obtained HW address yet */
- sock = socket(PF_INET, SOCK_STREAM, 0);
- if (sock == -1) {
- error_setg_errno(errp, errno, "failed to create socket");
- goto error;
- }
-
- memset(&ifr, 0, sizeof(ifr));
- pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name);
- if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
- error_setg_errno(errp, errno,
- "failed to get MAC address of %s",
- ifa->ifa_name);
- close(sock);
- goto error;
- }
-
- close(sock);
- mac_addr = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
-
- info->value->hardware_address =
- g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
- (int) mac_addr[0], (int) mac_addr[1],
- (int) mac_addr[2], (int) mac_addr[3],
- (int) mac_addr[4], (int) mac_addr[5]);
-
- info->value->has_hardware_address = true;
- }
-
- if (ifa->ifa_addr &&
- ifa->ifa_addr->sa_family == AF_INET) {
- /* interface with IPv4 address */
- p = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
- if (!inet_ntop(AF_INET, p, addr4, sizeof(addr4))) {
- error_setg_errno(errp, errno, "inet_ntop failed");
- goto error;
- }
-
- address_item = g_malloc0(sizeof(*address_item));
- address_item->value = g_malloc0(sizeof(*address_item->value));
- address_item->value->ip_address = g_strdup(addr4);
- address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV4;
-
- if (ifa->ifa_netmask) {
- /* Count the number of set bits in netmask.
- * This is safe as '1' and '0' cannot be shuffled in netmask. */
- p = &((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr;
- address_item->value->prefix = ctpop32(((uint32_t *) p)[0]);
- }
- } else if (ifa->ifa_addr &&
- ifa->ifa_addr->sa_family == AF_INET6) {
- /* interface with IPv6 address */
- p = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
- if (!inet_ntop(AF_INET6, p, addr6, sizeof(addr6))) {
- error_setg_errno(errp, errno, "inet_ntop failed");
- goto error;
- }
-
- address_item = g_malloc0(sizeof(*address_item));
- address_item->value = g_malloc0(sizeof(*address_item->value));
- address_item->value->ip_address = g_strdup(addr6);
- address_item->value->ip_address_type = GUEST_IP_ADDRESS_TYPE_IPV6;
-
- if (ifa->ifa_netmask) {
- /* Count the number of set bits in netmask.
- * This is safe as '1' and '0' cannot be shuffled in netmask. */
- p = &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
- address_item->value->prefix =
- ctpop32(((uint32_t *) p)[0]) +
- ctpop32(((uint32_t *) p)[1]) +
- ctpop32(((uint32_t *) p)[2]) +
- ctpop32(((uint32_t *) p)[3]);
- }
- }
-
- if (!address_item) {
- continue;
- }
-
- address_list = &info->value->ip_addresses;
-
- while (*address_list && (*address_list)->next) {
- address_list = &(*address_list)->next;
- }
-
- if (!*address_list) {
- *address_list = address_item;
- } else {
- (*address_list)->next = address_item;
- }
-
- info->value->has_ip_addresses = true;
-
-
- }
-
- freeifaddrs(ifap);
- return head;
-
-error:
- freeifaddrs(ifap);
- qapi_free_GuestNetworkInterfaceList(head);
- return NULL;
-}
-
-#define SYSCONF_EXACT(name, errp) sysconf_exact((name), #name, (errp))
-
-static long sysconf_exact(int name, const char *name_str, Error **errp)
-{
- long ret;
-
- errno = 0;
- ret = sysconf(name);
- if (ret == -1) {
- if (errno == 0) {
- error_setg(errp, "sysconf(%s): value indefinite", name_str);
- } else {
- error_setg_errno(errp, errno, "sysconf(%s)", name_str);
- }
- }
- return ret;
-}
-
-/* Transfer online/offline status between @vcpu and the guest system.
- *
- * On input either @errp or *@errp must be NULL.
- *
- * In system-to-@vcpu direction, the following @vcpu fields are accessed:
- * - R: vcpu->logical_id
- * - W: vcpu->online
- * - W: vcpu->can_offline
- *
- * In @vcpu-to-system direction, the following @vcpu fields are accessed:
- * - R: vcpu->logical_id
- * - R: vcpu->online
- *
- * Written members remain unmodified on error.
- */
-static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
- Error **errp)
-{
- char *dirpath;
- int dirfd;
-
- dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
- vcpu->logical_id);
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- } else {
- static const char fn[] = "online";
- int fd;
- int res;
-
- fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
- if (fd == -1) {
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
- } else if (sys2vcpu) {
- vcpu->online = true;
- vcpu->can_offline = false;
- } else if (!vcpu->online) {
- error_setg(errp, "logical processor #%" PRId64 " can't be "
- "offlined", vcpu->logical_id);
- } /* otherwise pretend successful re-onlining */
- } else {
- unsigned char status;
-
- res = pread(fd, &status, 1, 0);
- if (res == -1) {
- error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
- } else if (res == 0) {
- error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
- fn);
- } else if (sys2vcpu) {
- vcpu->online = (status != '0');
- vcpu->can_offline = true;
- } else if (vcpu->online != (status != '0')) {
- status = '0' + vcpu->online;
- if (pwrite(fd, &status, 1, 0) == -1) {
- error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
- fn);
- }
- } /* otherwise pretend successful re-(on|off)-lining */
-
- res = close(fd);
- g_assert(res == 0);
- }
-
- res = close(dirfd);
- g_assert(res == 0);
- }
-
- g_free(dirpath);
-}
-
-GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
-{
- int64_t current;
- GuestLogicalProcessorList *head, **link;
- long sc_max;
- Error *local_err = NULL;
-
- current = 0;
- head = NULL;
- link = &head;
- sc_max = SYSCONF_EXACT(_SC_NPROCESSORS_CONF, &local_err);
-
- while (local_err == NULL && current < sc_max) {
- GuestLogicalProcessor *vcpu;
- GuestLogicalProcessorList *entry;
-
- vcpu = g_malloc0(sizeof *vcpu);
- vcpu->logical_id = current++;
- vcpu->has_can_offline = true; /* lolspeak ftw */
- transfer_vcpu(vcpu, true, &local_err);
-
- entry = g_malloc0(sizeof *entry);
- entry->value = vcpu;
-
- *link = entry;
- link = &entry->next;
- }
-
- if (local_err == NULL) {
- /* there's no guest with zero VCPUs */
- g_assert(head != NULL);
- return head;
- }
-
- qapi_free_GuestLogicalProcessorList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- int64_t processed;
- Error *local_err = NULL;
-
- processed = 0;
- while (vcpus != NULL) {
- transfer_vcpu(vcpus->value, false, &local_err);
- if (local_err != NULL) {
- break;
- }
- ++processed;
- vcpus = vcpus->next;
- }
-
- if (local_err != NULL) {
- if (processed == 0) {
- error_propagate(errp, local_err);
- } else {
- error_free(local_err);
- }
- }
-
- return processed;
-}
-
-void qmp_guest_set_user_password(const char *username,
- const char *password,
- bool crypted,
- Error **errp)
-{
- Error *local_err = NULL;
- char *passwd_path = NULL;
- pid_t pid;
- int status;
- int datafd[2] = { -1, -1 };
- char *rawpasswddata = NULL;
- size_t rawpasswdlen;
- char *chpasswddata = NULL;
- size_t chpasswdlen;
-
- rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
- if (!rawpasswddata) {
- return;
- }
- rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
- rawpasswddata[rawpasswdlen] = '\0';
-
- if (strchr(rawpasswddata, '\n')) {
- error_setg(errp, "forbidden characters in raw password");
- goto out;
- }
-
- if (strchr(username, '\n') ||
- strchr(username, ':')) {
- error_setg(errp, "forbidden characters in username");
- goto out;
- }
-
- chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
- chpasswdlen = strlen(chpasswddata);
-
- passwd_path = g_find_program_in_path("chpasswd");
-
- if (!passwd_path) {
- error_setg(errp, "cannot find 'passwd' program in PATH");
- goto out;
- }
-
- if (pipe(datafd) < 0) {
- error_setg(errp, "cannot create pipe FDs");
- goto out;
- }
-
- pid = fork();
- if (pid == 0) {
- close(datafd[1]);
- /* child */
- setsid();
- dup2(datafd[0], 0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- if (crypted) {
- execle(passwd_path, "chpasswd", "-e", NULL, environ);
- } else {
- execle(passwd_path, "chpasswd", NULL, environ);
- }
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- goto out;
- }
- close(datafd[0]);
- datafd[0] = -1;
-
- if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) {
- error_setg_errno(errp, errno, "cannot write new account password");
- goto out;
- }
- close(datafd[1]);
- datafd[1] = -1;
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto out;
- }
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- goto out;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to set user password");
- goto out;
- }
-
-out:
- g_free(chpasswddata);
- g_free(rawpasswddata);
- g_free(passwd_path);
- if (datafd[0] != -1) {
- close(datafd[0]);
- }
- if (datafd[1] != -1) {
- close(datafd[1]);
- }
-}
-
-static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
- int size, Error **errp)
-{
- int fd;
- int res;
-
- errno = 0;
- fd = openat(dirfd, pathname, O_RDONLY);
- if (fd == -1) {
- error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
- return;
- }
-
- res = pread(fd, buf, size, 0);
- if (res == -1) {
- error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
- } else if (res == 0) {
- error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
- }
- close(fd);
-}
-
-static void ga_write_sysfs_file(int dirfd, const char *pathname,
- const char *buf, int size, Error **errp)
-{
- int fd;
-
- errno = 0;
- fd = openat(dirfd, pathname, O_WRONLY);
- if (fd == -1) {
- error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
- return;
- }
-
- if (pwrite(fd, buf, size, 0) == -1) {
- error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
- }
-
- close(fd);
-}
-
-/* Transfer online/offline status between @mem_blk and the guest system.
- *
- * On input either @errp or *@errp must be NULL.
- *
- * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
- * - R: mem_blk->phys_index
- * - W: mem_blk->online
- * - W: mem_blk->can_offline
- *
- * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
- * - R: mem_blk->phys_index
- * - R: mem_blk->online
- *- R: mem_blk->can_offline
- * Written members remain unmodified on error.
- */
-static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
- GuestMemoryBlockResponse *result,
- Error **errp)
-{
- char *dirpath;
- int dirfd;
- char *status;
- Error *local_err = NULL;
-
- if (!sys2memblk) {
- DIR *dp;
-
- if (!result) {
- error_setg(errp, "Internal error, 'result' should not be NULL");
- return;
- }
- errno = 0;
- dp = opendir("/sys/devices/system/memory/");
- /* if there is no 'memory' directory in sysfs,
- * we think this VM does not support online/offline memory block,
- * any other solution?
- */
- if (!dp && errno == ENOENT) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
- goto out1;
- }
- closedir(dp);
- }
-
- dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
- mem_blk->phys_index);
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- if (sys2memblk) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- } else {
- if (errno == ENOENT) {
- result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
- } else {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- }
- }
- g_free(dirpath);
- goto out1;
- }
- g_free(dirpath);
-
- status = g_malloc0(10);
- ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
- if (local_err) {
- /* treat with sysfs file that not exist in old kernel */
- if (errno == ENOENT) {
- error_free(local_err);
- if (sys2memblk) {
- mem_blk->online = true;
- mem_blk->can_offline = false;
- } else if (!mem_blk->online) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
- }
- } else {
- if (sys2memblk) {
- error_propagate(errp, local_err);
- } else {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- }
- }
- goto out2;
- }
-
- if (sys2memblk) {
- char removable = '0';
-
- mem_blk->online = (strncmp(status, "online", 6) == 0);
-
- ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
- if (local_err) {
- /* if no 'removable' file, it doesn't support offline mem blk */
- if (errno == ENOENT) {
- error_free(local_err);
- mem_blk->can_offline = false;
- } else {
- error_propagate(errp, local_err);
- }
- } else {
- mem_blk->can_offline = (removable != '0');
- }
- } else {
- if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
- char *new_state = mem_blk->online ? g_strdup("online") :
- g_strdup("offline");
-
- ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
- &local_err);
- g_free(new_state);
- if (local_err) {
- error_free(local_err);
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- goto out2;
- }
-
- result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
- result->has_error_code = false;
- } /* otherwise pretend successful re-(on|off)-lining */
- }
- g_free(status);
- close(dirfd);
- return;
-
-out2:
- g_free(status);
- close(dirfd);
-out1:
- if (!sys2memblk) {
- result->has_error_code = true;
- result->error_code = errno;
- }
-}
-
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- GuestMemoryBlockList *head, **link;
- Error *local_err = NULL;
- struct dirent *de;
- DIR *dp;
-
- head = NULL;
- link = &head;
-
- dp = opendir("/sys/devices/system/memory/");
- if (!dp) {
- /* it's ok if this happens to be a system that doesn't expose
- * memory blocks via sysfs, but otherwise we should report
- * an error
- */
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "Can't open directory"
- "\"/sys/devices/system/memory/\"");
- }
- return NULL;
- }
-
- /* Note: the phys_index of memory block may be discontinuous,
- * this is because a memblk is the unit of the Sparse Memory design, which
- * allows discontinuous memory ranges (ex. NUMA), so here we should
- * traverse the memory block directory.
- */
- while ((de = readdir(dp)) != NULL) {
- GuestMemoryBlock *mem_blk;
- GuestMemoryBlockList *entry;
-
- if ((strncmp(de->d_name, "memory", 6) != 0) ||
- !(de->d_type & DT_DIR)) {
- continue;
- }
-
- mem_blk = g_malloc0(sizeof *mem_blk);
- /* The d_name is "memoryXXX", phys_index is block id, same as XXX */
- mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
- mem_blk->has_can_offline = true; /* lolspeak ftw */
- transfer_memory_block(mem_blk, true, NULL, &local_err);
-
- entry = g_malloc0(sizeof *entry);
- entry->value = mem_blk;
-
- *link = entry;
- link = &entry->next;
- }
-
- closedir(dp);
- if (local_err == NULL) {
- /* there's no guest with zero memory blocks */
- if (head == NULL) {
- error_setg(errp, "guest reported zero memory blocks!");
- }
- return head;
- }
-
- qapi_free_GuestMemoryBlockList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- GuestMemoryBlockResponseList *head, **link;
- Error *local_err = NULL;
-
- head = NULL;
- link = &head;
-
- while (mem_blks != NULL) {
- GuestMemoryBlockResponse *result;
- GuestMemoryBlockResponseList *entry;
- GuestMemoryBlock *current_mem_blk = mem_blks->value;
-
- result = g_malloc0(sizeof(*result));
- result->phys_index = current_mem_blk->phys_index;
- transfer_memory_block(current_mem_blk, false, result, &local_err);
- if (local_err) { /* should never happen */
- goto err;
- }
- entry = g_malloc0(sizeof *entry);
- entry->value = result;
-
- *link = entry;
- link = &entry->next;
- mem_blks = mem_blks->next;
- }
-
- return head;
-err:
- qapi_free_GuestMemoryBlockResponseList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- Error *local_err = NULL;
- char *dirpath;
- int dirfd;
- char *buf;
- GuestMemoryBlockInfo *info;
-
- dirpath = g_strdup_printf("/sys/devices/system/memory/");
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- g_free(dirpath);
- return NULL;
- }
- g_free(dirpath);
-
- buf = g_malloc0(20);
- ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
- close(dirfd);
- if (local_err) {
- g_free(buf);
- error_propagate(errp, local_err);
- return NULL;
- }
-
- info = g_new0(GuestMemoryBlockInfo, 1);
- info->size = strtol(buf, NULL, 16); /* the unit is bytes */
-
- g_free(buf);
-
- return info;
-}
-
-#else /* defined(__linux__) */
-
-void qmp_guest_suspend_disk(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-void qmp_guest_suspend_ram(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return -1;
-}
-
-void qmp_guest_set_user_password(const char *username,
- const char *password,
- bool crypted,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-#endif
-
-#if !defined(CONFIG_FSFREEZE)
-
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_freeze(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_thaw(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-#endif /* CONFIG_FSFREEZE */
-
-#if !defined(CONFIG_FSTRIM)
-GuestFilesystemTrimResponse *
-qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-#endif
-
-/* add unsupported commands to the blacklist */
-GList *ga_command_blacklist_init(GList *blacklist)
-{
-#if !defined(__linux__)
- {
- const char *list[] = {
- "guest-suspend-disk", "guest-suspend-ram",
- "guest-suspend-hybrid", "guest-network-get-interfaces",
- "guest-get-vcpus", "guest-set-vcpus",
- "guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-size", NULL};
- char **p = (char **)list;
-
- while (*p) {
- blacklist = g_list_append(blacklist, g_strdup(*p++));
- }
- }
-#endif
-
-#if !defined(CONFIG_FSFREEZE)
- {
- const char *list[] = {
- "guest-get-fsinfo", "guest-fsfreeze-status",
- "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
- "guest-fsfreeze-thaw", "guest-get-fsinfo", NULL};
- char **p = (char **)list;
-
- while (*p) {
- blacklist = g_list_append(blacklist, g_strdup(*p++));
- }
- }
-#endif
-
-#if !defined(CONFIG_FSTRIM)
- blacklist = g_list_append(blacklist, g_strdup("guest-fstrim"));
-#endif
-
- return blacklist;
-}
-
-/* register init/cleanup routines for stateful command groups */
-void ga_command_state_init(GAState *s, GACommandState *cs)
-{
-#if defined(CONFIG_FSFREEZE)
- ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
-#endif
-}
diff --git a/qemu/qga/commands-win32.c b/qemu/qga/commands-win32.c
deleted file mode 100644
index d76327f5a..000000000
--- a/qemu/qga/commands-win32.c
+++ /dev/null
@@ -1,1455 +0,0 @@
-/*
- * QEMU Guest Agent win32-specific command implementations
- *
- * Copyright IBM Corp. 2012
- *
- * Authors:
- * Michael Roth <mdroth@linux.vnet.ibm.com>
- * Gal Hammer <ghammer@redhat.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 "qemu/osdep.h"
-#include <glib.h>
-#include <wtypes.h>
-#include <powrprof.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <iptypes.h>
-#include <iphlpapi.h>
-#ifdef CONFIG_QGA_NTDDSCSI
-#include <winioctl.h>
-#include <ntddscsi.h>
-#include <setupapi.h>
-#include <initguid.h>
-#endif
-#include <lm.h>
-
-#include "qga/guest-agent-core.h"
-#include "qga/vss-win32.h"
-#include "qga-qmp-commands.h"
-#include "qapi/qmp/qerror.h"
-#include "qemu/queue.h"
-#include "qemu/host-utils.h"
-#include "qemu/base64.h"
-
-#ifndef SHTDN_REASON_FLAG_PLANNED
-#define SHTDN_REASON_FLAG_PLANNED 0x80000000
-#endif
-
-/* multiple of 100 nanoseconds elapsed between windows baseline
- * (1/1/1601) and Unix Epoch (1/1/1970), accounting for leap years */
-#define W32_FT_OFFSET (10000000ULL * 60 * 60 * 24 * \
- (365 * (1970 - 1601) + \
- (1970 - 1601) / 4 - 3))
-
-#define INVALID_SET_FILE_POINTER ((DWORD)-1)
-
-typedef struct GuestFileHandle {
- int64_t id;
- HANDLE fh;
- QTAILQ_ENTRY(GuestFileHandle) next;
-} GuestFileHandle;
-
-static struct {
- QTAILQ_HEAD(, GuestFileHandle) filehandles;
-} guest_file_state = {
- .filehandles = QTAILQ_HEAD_INITIALIZER(guest_file_state.filehandles),
-};
-
-#define FILE_GENERIC_APPEND (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
-
-typedef struct OpenFlags {
- const char *forms;
- DWORD desired_access;
- DWORD creation_disposition;
-} OpenFlags;
-static OpenFlags guest_file_open_modes[] = {
- {"r", GENERIC_READ, OPEN_EXISTING},
- {"rb", GENERIC_READ, OPEN_EXISTING},
- {"w", GENERIC_WRITE, CREATE_ALWAYS},
- {"wb", GENERIC_WRITE, CREATE_ALWAYS},
- {"a", FILE_GENERIC_APPEND, OPEN_ALWAYS },
- {"r+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
- {"rb+", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
- {"r+b", GENERIC_WRITE|GENERIC_READ, OPEN_EXISTING},
- {"w+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
- {"wb+", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
- {"w+b", GENERIC_WRITE|GENERIC_READ, CREATE_ALWAYS},
- {"a+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS },
- {"ab+", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS },
- {"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS }
-};
-
-static OpenFlags *find_open_flag(const char *mode_str)
-{
- int mode;
- Error **errp = NULL;
-
- for (mode = 0; mode < ARRAY_SIZE(guest_file_open_modes); ++mode) {
- OpenFlags *flags = guest_file_open_modes + mode;
-
- if (strcmp(flags->forms, mode_str) == 0) {
- return flags;
- }
- }
-
- error_setg(errp, "invalid file open mode '%s'", mode_str);
- return NULL;
-}
-
-static int64_t guest_file_handle_add(HANDLE fh, Error **errp)
-{
- GuestFileHandle *gfh;
- int64_t handle;
-
- handle = ga_get_fd_handle(ga_state, errp);
- if (handle < 0) {
- return -1;
- }
- gfh = g_new0(GuestFileHandle, 1);
- gfh->id = handle;
- gfh->fh = fh;
- QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next);
-
- return handle;
-}
-
-static GuestFileHandle *guest_file_handle_find(int64_t id, Error **errp)
-{
- GuestFileHandle *gfh;
- QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) {
- if (gfh->id == id) {
- return gfh;
- }
- }
- error_setg(errp, "handle '%" PRId64 "' has not been found", id);
- return NULL;
-}
-
-static void handle_set_nonblocking(HANDLE fh)
-{
- DWORD file_type, pipe_state;
- file_type = GetFileType(fh);
- if (file_type != FILE_TYPE_PIPE) {
- return;
- }
- /* If file_type == FILE_TYPE_PIPE, according to MSDN
- * the specified file is socket or named pipe */
- if (!GetNamedPipeHandleState(fh, &pipe_state, NULL,
- NULL, NULL, NULL, 0)) {
- return;
- }
- /* The fd is named pipe fd */
- if (pipe_state & PIPE_NOWAIT) {
- return;
- }
-
- pipe_state |= PIPE_NOWAIT;
- SetNamedPipeHandleState(fh, &pipe_state, NULL, NULL);
-}
-
-int64_t qmp_guest_file_open(const char *path, bool has_mode,
- const char *mode, Error **errp)
-{
- int64_t fd;
- HANDLE fh;
- HANDLE templ_file = NULL;
- DWORD share_mode = FILE_SHARE_READ;
- DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL;
- LPSECURITY_ATTRIBUTES sa_attr = NULL;
- OpenFlags *guest_flags;
-
- if (!has_mode) {
- mode = "r";
- }
- slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
- guest_flags = find_open_flag(mode);
- if (guest_flags == NULL) {
- error_setg(errp, "invalid file open mode");
- return -1;
- }
-
- fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr,
- guest_flags->creation_disposition, flags_and_attr,
- templ_file);
- if (fh == INVALID_HANDLE_VALUE) {
- error_setg_win32(errp, GetLastError(), "failed to open file '%s'",
- path);
- return -1;
- }
-
- /* set fd non-blocking to avoid common use cases (like reading from a
- * named pipe) from hanging the agent
- */
- handle_set_nonblocking(fh);
-
- fd = guest_file_handle_add(fh, errp);
- if (fd < 0) {
- CloseHandle(fh);
- error_setg(errp, "failed to add handle to qmp handle table");
- return -1;
- }
-
- slog("guest-file-open, handle: % " PRId64, fd);
- return fd;
-}
-
-void qmp_guest_file_close(int64_t handle, Error **errp)
-{
- bool ret;
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- slog("guest-file-close called, handle: %" PRId64, handle);
- if (gfh == NULL) {
- return;
- }
- ret = CloseHandle(gfh->fh);
- if (!ret) {
- error_setg_win32(errp, GetLastError(), "failed close handle");
- return;
- }
-
- QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next);
- g_free(gfh);
-}
-
-static void acquire_privilege(const char *name, Error **errp)
-{
- HANDLE token = NULL;
- TOKEN_PRIVILEGES priv;
- Error *local_err = NULL;
-
- if (OpenProcessToken(GetCurrentProcess(),
- TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &token))
- {
- if (!LookupPrivilegeValue(NULL, name, &priv.Privileges[0].Luid)) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "no luid for requested privilege");
- goto out;
- }
-
- priv.PrivilegeCount = 1;
- priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- if (!AdjustTokenPrivileges(token, FALSE, &priv, 0, NULL, 0)) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "unable to acquire requested privilege");
- goto out;
- }
-
- } else {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to open privilege token");
- }
-
-out:
- if (token) {
- CloseHandle(token);
- }
- if (local_err) {
- error_propagate(errp, local_err);
- }
-}
-
-static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque,
- Error **errp)
-{
- Error *local_err = NULL;
-
- HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL);
- if (!thread) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to dispatch asynchronous command");
- error_propagate(errp, local_err);
- }
-}
-
-void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp)
-{
- Error *local_err = NULL;
- 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_setg(errp, QERR_INVALID_PARAMETER_VALUE, "mode",
- "halt|powerdown|reboot");
- return;
- }
-
- /* Request a shutdown privilege, but try to shut down the system
- anyway. */
- acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (!ExitWindowsEx(shutdown_flag, SHTDN_REASON_FLAG_PLANNED)) {
- slog("guest-shutdown failed: %lu", GetLastError());
- error_setg(errp, QERR_UNDEFINED_ERROR);
- }
-}
-
-GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count,
- int64_t count, Error **errp)
-{
- GuestFileRead *read_data = NULL;
- guchar *buf;
- HANDLE fh;
- bool is_ok;
- DWORD read_count;
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
-
- if (!gfh) {
- return NULL;
- }
- if (!has_count) {
- count = QGA_READ_COUNT_DEFAULT;
- } else if (count < 0) {
- error_setg(errp, "value '%" PRId64
- "' is invalid for argument count", count);
- return NULL;
- }
-
- fh = gfh->fh;
- buf = g_malloc0(count+1);
- is_ok = ReadFile(fh, buf, count, &read_count, NULL);
- if (!is_ok) {
- error_setg_win32(errp, GetLastError(), "failed to read file");
- slog("guest-file-read failed, handle %" PRId64, handle);
- } else {
- buf[read_count] = 0;
- read_data = g_new0(GuestFileRead, 1);
- read_data->count = (size_t)read_count;
- read_data->eof = read_count == 0;
-
- if (read_count != 0) {
- read_data->buf_b64 = g_base64_encode(buf, read_count);
- }
- }
- g_free(buf);
-
- return read_data;
-}
-
-GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64,
- bool has_count, int64_t count,
- Error **errp)
-{
- GuestFileWrite *write_data = NULL;
- guchar *buf;
- gsize buf_len;
- bool is_ok;
- DWORD write_count;
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- HANDLE fh;
-
- if (!gfh) {
- return NULL;
- }
- fh = gfh->fh;
- buf = qbase64_decode(buf_b64, -1, &buf_len, errp);
- if (!buf) {
- return NULL;
- }
-
- if (!has_count) {
- count = buf_len;
- } else if (count < 0 || count > buf_len) {
- error_setg(errp, "value '%" PRId64
- "' is invalid for argument count", count);
- goto done;
- }
-
- is_ok = WriteFile(fh, buf, count, &write_count, NULL);
- if (!is_ok) {
- error_setg_win32(errp, GetLastError(), "failed to write to file");
- slog("guest-file-write-failed, handle: %" PRId64, handle);
- } else {
- write_data = g_new0(GuestFileWrite, 1);
- write_data->count = (size_t) write_count;
- }
-
-done:
- g_free(buf);
- return write_data;
-}
-
-GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
- GuestFileWhence *whence_code,
- Error **errp)
-{
- GuestFileHandle *gfh;
- GuestFileSeek *seek_data;
- HANDLE fh;
- LARGE_INTEGER new_pos, off_pos;
- off_pos.QuadPart = offset;
- BOOL res;
- int whence;
- Error *err = NULL;
-
- gfh = guest_file_handle_find(handle, errp);
- if (!gfh) {
- return NULL;
- }
-
- /* We stupidly exposed 'whence':'int' in our qapi */
- whence = ga_parse_whence(whence_code, &err);
- if (err) {
- error_propagate(errp, err);
- return NULL;
- }
-
- fh = gfh->fh;
- res = SetFilePointerEx(fh, off_pos, &new_pos, whence);
- if (!res) {
- error_setg_win32(errp, GetLastError(), "failed to seek file");
- return NULL;
- }
- seek_data = g_new0(GuestFileSeek, 1);
- seek_data->position = new_pos.QuadPart;
- return seek_data;
-}
-
-void qmp_guest_file_flush(int64_t handle, Error **errp)
-{
- HANDLE fh;
- GuestFileHandle *gfh = guest_file_handle_find(handle, errp);
- if (!gfh) {
- return;
- }
-
- fh = gfh->fh;
- if (!FlushFileBuffers(fh)) {
- error_setg_win32(errp, GetLastError(), "failed to flush file");
- }
-}
-
-#ifdef CONFIG_QGA_NTDDSCSI
-
-static STORAGE_BUS_TYPE win2qemu[] = {
- [BusTypeUnknown] = GUEST_DISK_BUS_TYPE_UNKNOWN,
- [BusTypeScsi] = GUEST_DISK_BUS_TYPE_SCSI,
- [BusTypeAtapi] = GUEST_DISK_BUS_TYPE_IDE,
- [BusTypeAta] = GUEST_DISK_BUS_TYPE_IDE,
- [BusType1394] = GUEST_DISK_BUS_TYPE_IEEE1394,
- [BusTypeSsa] = GUEST_DISK_BUS_TYPE_SSA,
- [BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA,
- [BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB,
- [BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID,
-#if (_WIN32_WINNT >= 0x0600)
- [BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI,
- [BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS,
- [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA,
- [BusTypeSd] = GUEST_DISK_BUS_TYPE_SD,
- [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC,
-#endif
-#if (_WIN32_WINNT >= 0x0601)
- [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL,
- [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL,
-#endif
-};
-
-static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
-{
- if (bus > ARRAY_SIZE(win2qemu) || (int)bus < 0) {
- return GUEST_DISK_BUS_TYPE_UNKNOWN;
- }
- return win2qemu[(int)bus];
-}
-
-DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,
- 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2,
- 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-
-static GuestPCIAddress *get_pci_info(char *guid, Error **errp)
-{
- HDEVINFO dev_info;
- SP_DEVINFO_DATA dev_info_data;
- DWORD size = 0;
- int i;
- char dev_name[MAX_PATH];
- char *buffer = NULL;
- GuestPCIAddress *pci = NULL;
- char *name = g_strdup(&guid[4]);
-
- if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) {
- error_setg_win32(errp, GetLastError(), "failed to get dos device name");
- goto out;
- }
-
- dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME, 0, 0,
- DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
- if (dev_info == INVALID_HANDLE_VALUE) {
- error_setg_win32(errp, GetLastError(), "failed to get devices tree");
- goto out;
- }
-
- dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
- for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
- DWORD addr, bus, slot, func, dev, data, size2;
- while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,
- &data, (PBYTE)buffer, size,
- &size2)) {
- size = MAX(size, size2);
- if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
- g_free(buffer);
- /* Double the size to avoid problems on
- * W2k MBCS systems per KB 888609.
- * https://support.microsoft.com/en-us/kb/259695 */
- buffer = g_malloc(size * 2);
- } else {
- error_setg_win32(errp, GetLastError(),
- "failed to get device name");
- goto out;
- }
- }
-
- if (g_strcmp0(buffer, dev_name)) {
- continue;
- }
-
- /* There is no need to allocate buffer in the next functions. The size
- * is known and ULONG according to
- * https://support.microsoft.com/en-us/kb/253232
- * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
- */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) {
- break;
- }
-
- /* The function retrieves the device's address. This value will be
- * transformed into device function and number */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) {
- break;
- }
-
- /* This call returns UINumber of DEVICE_CAPABILITIES structure.
- * This number is typically a user-perceived slot number. */
- if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data,
- SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) {
- break;
- }
-
- /* SetupApi gives us the same information as driver with
- * IoGetDeviceProperty. According to Microsoft
- * https://support.microsoft.com/en-us/kb/253232
- * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF);
- * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
- * SPDRP_ADDRESS is propertyAddress, so we do the same.*/
-
- func = addr & 0x0000FFFF;
- dev = (addr >> 16) & 0x0000FFFF;
- pci = g_malloc0(sizeof(*pci));
- pci->domain = dev;
- pci->slot = slot;
- pci->function = func;
- pci->bus = bus;
- break;
- }
-out:
- g_free(buffer);
- g_free(name);
- return pci;
-}
-
-static int get_disk_bus_type(HANDLE vol_h, Error **errp)
-{
- STORAGE_PROPERTY_QUERY query;
- STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf;
- DWORD received;
-
- dev_desc = &buf;
- dev_desc->Size = sizeof(buf);
- query.PropertyId = StorageDeviceProperty;
- query.QueryType = PropertyStandardQuery;
-
- if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query,
- sizeof(STORAGE_PROPERTY_QUERY), dev_desc,
- dev_desc->Size, &received, NULL)) {
- error_setg_win32(errp, GetLastError(), "failed to get bus type");
- return -1;
- }
-
- return dev_desc->BusType;
-}
-
-/* VSS provider works with volumes, thus there is no difference if
- * the volume consist of spanned disks. Info about the first disk in the
- * volume is returned for the spanned disk group (LVM) */
-static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
-{
- GuestDiskAddressList *list = NULL;
- GuestDiskAddress *disk;
- SCSI_ADDRESS addr, *scsi_ad;
- DWORD len;
- int bus;
- HANDLE vol_h;
-
- scsi_ad = &addr;
- char *name = g_strndup(guid, strlen(guid)-1);
-
- vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
- 0, NULL);
- if (vol_h == INVALID_HANDLE_VALUE) {
- error_setg_win32(errp, GetLastError(), "failed to open volume");
- goto out_free;
- }
-
- bus = get_disk_bus_type(vol_h, errp);
- if (bus < 0) {
- goto out_close;
- }
-
- disk = g_malloc0(sizeof(*disk));
- disk->bus_type = find_bus_type(bus);
- if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID
-#if (_WIN32_WINNT >= 0x0600)
- /* This bus type is not supported before Windows Server 2003 SP1 */
- || bus == BusTypeSas
-#endif
- ) {
- /* We are able to use the same ioctls for different bus types
- * according to Microsoft docs
- * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */
- if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad,
- sizeof(SCSI_ADDRESS), &len, NULL)) {
- disk->unit = addr.Lun;
- disk->target = addr.TargetId;
- disk->bus = addr.PathId;
- disk->pci_controller = get_pci_info(name, errp);
- }
- /* We do not set error in this case, because we still have enough
- * information about volume. */
- } else {
- disk->pci_controller = NULL;
- }
-
- list = g_malloc0(sizeof(*list));
- list->value = disk;
- list->next = NULL;
-out_close:
- CloseHandle(vol_h);
-out_free:
- g_free(name);
- return list;
-}
-
-#else
-
-static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp)
-{
- return NULL;
-}
-
-#endif /* CONFIG_QGA_NTDDSCSI */
-
-static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp)
-{
- DWORD info_size;
- char mnt, *mnt_point;
- char fs_name[32];
- char vol_info[MAX_PATH+1];
- size_t len;
- GuestFilesystemInfo *fs = NULL;
-
- GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size);
- if (GetLastError() != ERROR_MORE_DATA) {
- error_setg_win32(errp, GetLastError(), "failed to get volume name");
- return NULL;
- }
-
- mnt_point = g_malloc(info_size + 1);
- if (!GetVolumePathNamesForVolumeName(guid, mnt_point, info_size,
- &info_size)) {
- error_setg_win32(errp, GetLastError(), "failed to get volume name");
- goto free;
- }
-
- len = strlen(mnt_point);
- mnt_point[len] = '\\';
- mnt_point[len+1] = 0;
- if (!GetVolumeInformation(mnt_point, vol_info, sizeof(vol_info), NULL, NULL,
- NULL, (LPSTR)&fs_name, sizeof(fs_name))) {
- if (GetLastError() != ERROR_NOT_READY) {
- error_setg_win32(errp, GetLastError(), "failed to get volume info");
- }
- goto free;
- }
-
- fs_name[sizeof(fs_name) - 1] = 0;
- fs = g_malloc(sizeof(*fs));
- fs->name = g_strdup(guid);
- if (len == 0) {
- fs->mountpoint = g_strdup("System Reserved");
- } else {
- fs->mountpoint = g_strndup(mnt_point, len);
- }
- fs->type = g_strdup(fs_name);
- fs->disk = build_guest_disk_info(guid, errp);
-free:
- g_free(mnt_point);
- return fs;
-}
-
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- HANDLE vol_h;
- GuestFilesystemInfoList *new, *ret = NULL;
- char guid[256];
-
- vol_h = FindFirstVolume(guid, sizeof(guid));
- if (vol_h == INVALID_HANDLE_VALUE) {
- error_setg_win32(errp, GetLastError(), "failed to find any volume");
- return NULL;
- }
-
- do {
- GuestFilesystemInfo *info = build_guest_fsinfo(guid, errp);
- if (info == NULL) {
- continue;
- }
- new = g_malloc(sizeof(*ret));
- new->value = info;
- new->next = ret;
- ret = new;
- } while (FindNextVolume(vol_h, guid, sizeof(guid)));
-
- if (GetLastError() != ERROR_NO_MORE_FILES) {
- error_setg_win32(errp, GetLastError(), "failed to find next volume");
- }
-
- FindVolumeClose(vol_h);
- return ret;
-}
-
-/*
- * Return status of freeze/thaw
- */
-GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
-{
- if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
- return 0;
- }
-
- if (ga_is_frozen(ga_state)) {
- return GUEST_FSFREEZE_STATUS_FROZEN;
- }
-
- return GUEST_FSFREEZE_STATUS_THAWED;
-}
-
-/*
- * Freeze local file systems using Volume Shadow-copy Service.
- * The frozen state is limited for up to 10 seconds by VSS.
- */
-int64_t qmp_guest_fsfreeze_freeze(Error **errp)
-{
- int i;
- Error *local_err = NULL;
-
- if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
- return 0;
- }
-
- slog("guest-fsfreeze called");
-
- /* cannot risk guest agent blocking itself on a write in this state */
- ga_set_frozen(ga_state);
-
- qga_vss_fsfreeze(&i, &local_err, true);
- if (local_err) {
- error_propagate(errp, local_err);
- goto error;
- }
-
- return i;
-
-error:
- local_err = NULL;
- qmp_guest_fsfreeze_thaw(&local_err);
- if (local_err) {
- g_debug("cleanup thaw: %s", error_get_pretty(local_err));
- error_free(local_err);
- }
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-/*
- * Thaw local file systems using Volume Shadow-copy Service.
- */
-int64_t qmp_guest_fsfreeze_thaw(Error **errp)
-{
- int i;
-
- if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
- return 0;
- }
-
- qga_vss_fsfreeze(&i, errp, false);
-
- ga_unset_frozen(ga_state);
- return i;
-}
-
-static void guest_fsfreeze_cleanup(void)
-{
- Error *err = NULL;
-
- if (!vss_initialized()) {
- return;
- }
-
- if (ga_is_frozen(ga_state) == GUEST_FSFREEZE_STATUS_FROZEN) {
- qmp_guest_fsfreeze_thaw(&err);
- if (err) {
- slog("failed to clean up frozen filesystems: %s",
- error_get_pretty(err));
- error_free(err);
- }
- }
-
- vss_deinit(true);
-}
-
-/*
- * Walk list of mounted file systems in the guest, and discard unused
- * areas.
- */
-GuestFilesystemTrimResponse *
-qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-typedef enum {
- GUEST_SUSPEND_MODE_DISK,
- GUEST_SUSPEND_MODE_RAM
-} GuestSuspendMode;
-
-static void check_suspend_mode(GuestSuspendMode mode, Error **errp)
-{
- SYSTEM_POWER_CAPABILITIES sys_pwr_caps;
- Error *local_err = NULL;
-
- ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps));
- if (!GetPwrCapabilities(&sys_pwr_caps)) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "failed to determine guest suspend capabilities");
- goto out;
- }
-
- switch (mode) {
- case GUEST_SUSPEND_MODE_DISK:
- if (!sys_pwr_caps.SystemS4) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "suspend-to-disk not supported by OS");
- }
- break;
- case GUEST_SUSPEND_MODE_RAM:
- if (!sys_pwr_caps.SystemS3) {
- error_setg(&local_err, QERR_QGA_COMMAND_FAILED,
- "suspend-to-ram not supported by OS");
- }
- break;
- default:
- error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "mode",
- "GuestSuspendMode");
- }
-
-out:
- if (local_err) {
- error_propagate(errp, local_err);
- }
-}
-
-static DWORD WINAPI do_suspend(LPVOID opaque)
-{
- GuestSuspendMode *mode = opaque;
- DWORD ret = 0;
-
- if (!SetSuspendState(*mode == GUEST_SUSPEND_MODE_DISK, TRUE, TRUE)) {
- slog("failed to suspend guest, %lu", GetLastError());
- ret = -1;
- }
- g_free(mode);
- return ret;
-}
-
-void qmp_guest_suspend_disk(Error **errp)
-{
- Error *local_err = NULL;
- GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
-
- *mode = GUEST_SUSPEND_MODE_DISK;
- check_suspend_mode(*mode, &local_err);
- acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
- execute_async(do_suspend, mode, &local_err);
-
- if (local_err) {
- error_propagate(errp, local_err);
- g_free(mode);
- }
-}
-
-void qmp_guest_suspend_ram(Error **errp)
-{
- Error *local_err = NULL;
- GuestSuspendMode *mode = g_new(GuestSuspendMode, 1);
-
- *mode = GUEST_SUSPEND_MODE_RAM;
- check_suspend_mode(*mode, &local_err);
- acquire_privilege(SE_SHUTDOWN_NAME, &local_err);
- execute_async(do_suspend, mode, &local_err);
-
- if (local_err) {
- error_propagate(errp, local_err);
- g_free(mode);
- }
-}
-
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
-{
- IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
- ULONG adptr_addrs_len = 0;
- DWORD ret;
-
- /* Call the first time to get the adptr_addrs_len. */
- GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
- NULL, adptr_addrs, &adptr_addrs_len);
-
- adptr_addrs = g_malloc(adptr_addrs_len);
- ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX,
- NULL, adptr_addrs, &adptr_addrs_len);
- if (ret != ERROR_SUCCESS) {
- error_setg_win32(errp, ret, "failed to get adapters addresses");
- g_free(adptr_addrs);
- adptr_addrs = NULL;
- }
- return adptr_addrs;
-}
-
-static char *guest_wctomb_dup(WCHAR *wstr)
-{
- char *str;
- size_t i;
-
- i = wcslen(wstr) + 1;
- str = g_malloc(i);
- WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK,
- wstr, -1, str, i, NULL, NULL);
- return str;
-}
-
-static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr,
- Error **errp)
-{
- char addr_str[INET6_ADDRSTRLEN + INET_ADDRSTRLEN];
- DWORD len;
- int ret;
-
- if (ip_addr->Address.lpSockaddr->sa_family == AF_INET ||
- ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
- len = sizeof(addr_str);
- ret = WSAAddressToString(ip_addr->Address.lpSockaddr,
- ip_addr->Address.iSockaddrLength,
- NULL,
- addr_str,
- &len);
- if (ret != 0) {
- error_setg_win32(errp, WSAGetLastError(),
- "failed address presentation form conversion");
- return NULL;
- }
- return g_strdup(addr_str);
- }
- return NULL;
-}
-
-#if (_WIN32_WINNT >= 0x0600)
-static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
-{
- /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength
- * field to obtain the prefix.
- */
- return ip_addr->OnLinkPrefixLength;
-}
-#else
-/* When using the Windows XP and 2003 build environment, do the best we can to
- * figure out the prefix.
- */
-static IP_ADAPTER_INFO *guest_get_adapters_info(void)
-{
- IP_ADAPTER_INFO *adptr_info = NULL;
- ULONG adptr_info_len = 0;
- DWORD ret;
-
- /* Call the first time to get the adptr_info_len. */
- GetAdaptersInfo(adptr_info, &adptr_info_len);
-
- adptr_info = g_malloc(adptr_info_len);
- ret = GetAdaptersInfo(adptr_info, &adptr_info_len);
- if (ret != ERROR_SUCCESS) {
- g_free(adptr_info);
- adptr_info = NULL;
- }
- return adptr_info;
-}
-
-static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr)
-{
- int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */
- IP_ADAPTER_INFO *adptr_info, *info;
- IP_ADDR_STRING *ip;
- struct in_addr *p;
-
- if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) {
- return prefix;
- }
- adptr_info = guest_get_adapters_info();
- if (adptr_info == NULL) {
- return prefix;
- }
-
- /* Match up the passed in ip_addr with one found in adaptr_info.
- * The matching one in adptr_info will have the netmask.
- */
- p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr;
- for (info = adptr_info; info; info = info->Next) {
- for (ip = &info->IpAddressList; ip; ip = ip->Next) {
- if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) {
- prefix = ctpop32(inet_addr(ip->IpMask.String));
- goto out;
- }
- }
- }
-out:
- g_free(adptr_info);
- return prefix;
-}
-#endif
-
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
-{
- IP_ADAPTER_ADDRESSES *adptr_addrs, *addr;
- IP_ADAPTER_UNICAST_ADDRESS *ip_addr = NULL;
- GuestNetworkInterfaceList *head = NULL, *cur_item = NULL;
- GuestIpAddressList *head_addr, *cur_addr;
- GuestNetworkInterfaceList *info;
- GuestIpAddressList *address_item = NULL;
- unsigned char *mac_addr;
- char *addr_str;
- WORD wsa_version;
- WSADATA wsa_data;
- int ret;
-
- adptr_addrs = guest_get_adapters_addresses(errp);
- if (adptr_addrs == NULL) {
- return NULL;
- }
-
- /* Make WSA APIs available. */
- wsa_version = MAKEWORD(2, 2);
- ret = WSAStartup(wsa_version, &wsa_data);
- if (ret != 0) {
- error_setg_win32(errp, ret, "failed socket startup");
- goto out;
- }
-
- for (addr = adptr_addrs; addr; addr = addr->Next) {
- info = g_malloc0(sizeof(*info));
-
- if (cur_item == NULL) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
-
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = guest_wctomb_dup(addr->FriendlyName);
-
- if (addr->PhysicalAddressLength != 0) {
- mac_addr = addr->PhysicalAddress;
-
- info->value->hardware_address =
- g_strdup_printf("%02x:%02x:%02x:%02x:%02x:%02x",
- (int) mac_addr[0], (int) mac_addr[1],
- (int) mac_addr[2], (int) mac_addr[3],
- (int) mac_addr[4], (int) mac_addr[5]);
-
- info->value->has_hardware_address = true;
- }
-
- head_addr = NULL;
- cur_addr = NULL;
- for (ip_addr = addr->FirstUnicastAddress;
- ip_addr;
- ip_addr = ip_addr->Next) {
- addr_str = guest_addr_to_str(ip_addr, errp);
- if (addr_str == NULL) {
- continue;
- }
-
- address_item = g_malloc0(sizeof(*address_item));
-
- if (!cur_addr) {
- head_addr = cur_addr = address_item;
- } else {
- cur_addr->next = address_item;
- cur_addr = address_item;
- }
-
- address_item->value = g_malloc0(sizeof(*address_item->value));
- address_item->value->ip_address = addr_str;
- address_item->value->prefix = guest_ip_prefix(ip_addr);
- if (ip_addr->Address.lpSockaddr->sa_family == AF_INET) {
- address_item->value->ip_address_type =
- GUEST_IP_ADDRESS_TYPE_IPV4;
- } else if (ip_addr->Address.lpSockaddr->sa_family == AF_INET6) {
- address_item->value->ip_address_type =
- GUEST_IP_ADDRESS_TYPE_IPV6;
- }
- }
- if (head_addr) {
- info->value->has_ip_addresses = true;
- info->value->ip_addresses = head_addr;
- }
- }
- WSACleanup();
-out:
- g_free(adptr_addrs);
- return head;
-}
-
-int64_t qmp_guest_get_time(Error **errp)
-{
- SYSTEMTIME ts = {0};
- int64_t time_ns;
- FILETIME tf;
-
- GetSystemTime(&ts);
- if (ts.wYear < 1601 || ts.wYear > 30827) {
- error_setg(errp, "Failed to get time");
- return -1;
- }
-
- if (!SystemTimeToFileTime(&ts, &tf)) {
- error_setg(errp, "Failed to convert system time: %d", (int)GetLastError());
- return -1;
- }
-
- time_ns = ((((int64_t)tf.dwHighDateTime << 32) | tf.dwLowDateTime)
- - W32_FT_OFFSET) * 100;
-
- return time_ns;
-}
-
-void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
-{
- Error *local_err = NULL;
- SYSTEMTIME ts;
- FILETIME tf;
- LONGLONG time;
-
- if (!has_time) {
- /* Unfortunately, Windows libraries don't provide an easy way to access
- * RTC yet:
- *
- * https://msdn.microsoft.com/en-us/library/aa908981.aspx
- */
- error_setg(errp, "Time argument is required on this platform");
- return;
- }
-
- /* Validate time passed by user. */
- if (time_ns < 0 || time_ns / 100 > INT64_MAX - W32_FT_OFFSET) {
- error_setg(errp, "Time %" PRId64 "is invalid", time_ns);
- return;
- }
-
- time = time_ns / 100 + W32_FT_OFFSET;
-
- tf.dwLowDateTime = (DWORD) time;
- tf.dwHighDateTime = (DWORD) (time >> 32);
-
- if (!FileTimeToSystemTime(&tf, &ts)) {
- error_setg(errp, "Failed to convert system time %d",
- (int)GetLastError());
- return;
- }
-
- acquire_privilege(SE_SYSTEMTIME_NAME, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (!SetSystemTime(&ts)) {
- error_setg(errp, "Failed to set time to guest: %d", (int)GetLastError());
- return;
- }
-}
-
-GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
-{
- PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pslpi, ptr;
- DWORD length;
- GuestLogicalProcessorList *head, **link;
- Error *local_err = NULL;
- int64_t current;
-
- ptr = pslpi = NULL;
- length = 0;
- current = 0;
- head = NULL;
- link = &head;
-
- if ((GetLogicalProcessorInformation(pslpi, &length) == FALSE) &&
- (GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
- (length > sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION))) {
- ptr = pslpi = g_malloc0(length);
- if (GetLogicalProcessorInformation(pslpi, &length) == FALSE) {
- error_setg(&local_err, "Failed to get processor information: %d",
- (int)GetLastError());
- }
- } else {
- error_setg(&local_err,
- "Failed to get processor information buffer length: %d",
- (int)GetLastError());
- }
-
- while ((local_err == NULL) && (length > 0)) {
- if (pslpi->Relationship == RelationProcessorCore) {
- ULONG_PTR cpu_bits = pslpi->ProcessorMask;
-
- while (cpu_bits > 0) {
- if (!!(cpu_bits & 1)) {
- GuestLogicalProcessor *vcpu;
- GuestLogicalProcessorList *entry;
-
- vcpu = g_malloc0(sizeof *vcpu);
- vcpu->logical_id = current++;
- vcpu->online = true;
- vcpu->has_can_offline = false;
-
- entry = g_malloc0(sizeof *entry);
- entry->value = vcpu;
-
- *link = entry;
- link = &entry->next;
- }
- cpu_bits >>= 1;
- }
- }
- length -= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
- pslpi++; /* next entry */
- }
-
- g_free(ptr);
-
- if (local_err == NULL) {
- if (head != NULL) {
- return head;
- }
- /* there's no guest with zero VCPUs */
- error_setg(&local_err, "Guest reported zero VCPUs");
- }
-
- qapi_free_GuestLogicalProcessorList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return -1;
-}
-
-static gchar *
-get_net_error_message(gint error)
-{
- HMODULE module = NULL;
- gchar *retval = NULL;
- wchar_t *msg = NULL;
- int flags;
- size_t nchars;
-
- flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_FROM_SYSTEM;
-
- if (error >= NERR_BASE && error <= MAX_NERR) {
- module = LoadLibraryExW(L"netmsg.dll", NULL, LOAD_LIBRARY_AS_DATAFILE);
-
- if (module != NULL) {
- flags |= FORMAT_MESSAGE_FROM_HMODULE;
- }
- }
-
- FormatMessageW(flags, module, error, 0, (LPWSTR)&msg, 0, NULL);
-
- if (msg != NULL) {
- nchars = wcslen(msg);
-
- if (nchars >= 2 &&
- msg[nchars - 1] == L'\n' &&
- msg[nchars - 2] == L'\r') {
- msg[nchars - 2] = L'\0';
- }
-
- retval = g_utf16_to_utf8(msg, -1, NULL, NULL, NULL);
-
- LocalFree(msg);
- }
-
- if (module != NULL) {
- FreeLibrary(module);
- }
-
- return retval;
-}
-
-void qmp_guest_set_user_password(const char *username,
- const char *password,
- bool crypted,
- Error **errp)
-{
- NET_API_STATUS nas;
- char *rawpasswddata = NULL;
- size_t rawpasswdlen;
- wchar_t *user = NULL, *wpass = NULL;
- USER_INFO_1003 pi1003 = { 0, };
- GError *gerr = NULL;
-
- if (crypted) {
- error_setg(errp, QERR_UNSUPPORTED);
- return;
- }
-
- rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
- if (!rawpasswddata) {
- return;
- }
- rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1);
- rawpasswddata[rawpasswdlen] = '\0';
-
- user = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr);
- if (!user) {
- goto done;
- }
-
- wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, &gerr);
- if (!wpass) {
- goto done;
- }
-
- pi1003.usri1003_password = wpass;
- nas = NetUserSetInfo(NULL, user,
- 1003, (LPBYTE)&pi1003,
- NULL);
-
- if (nas != NERR_Success) {
- gchar *msg = get_net_error_message(nas);
- error_setg(errp, "failed to set password: %s", msg);
- g_free(msg);
- }
-
-done:
- if (gerr) {
- error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message);
- g_error_free(gerr);
- }
- g_free(user);
- g_free(wpass);
- g_free(rawpasswddata);
-}
-
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-/* add unsupported commands to the blacklist */
-GList *ga_command_blacklist_init(GList *blacklist)
-{
- const char *list_unsupported[] = {
- "guest-suspend-hybrid",
- "guest-set-vcpus",
- "guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-size",
- "guest-fsfreeze-freeze-list",
- "guest-fstrim", NULL};
- char **p = (char **)list_unsupported;
-
- while (*p) {
- blacklist = g_list_append(blacklist, g_strdup(*p++));
- }
-
- if (!vss_init(true)) {
- g_debug("vss_init failed, vss commands are going to be disabled");
- const char *list[] = {
- "guest-get-fsinfo", "guest-fsfreeze-status",
- "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
- p = (char **)list;
-
- while (*p) {
- blacklist = g_list_append(blacklist, g_strdup(*p++));
- }
- }
-
- return blacklist;
-}
-
-/* register init/cleanup routines for stateful command groups */
-void ga_command_state_init(GAState *s, GACommandState *cs)
-{
- if (!vss_initialized()) {
- ga_command_state_add(cs, NULL, guest_fsfreeze_cleanup);
- }
-}
diff --git a/qemu/qga/commands.c b/qemu/qga/commands.c
deleted file mode 100644
index b653a460b..000000000
--- a/qemu/qga/commands.c
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * 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 "qemu/osdep.h"
-#include <glib.h>
-#include "qga/guest-agent-core.h"
-#include "qga-qmp-commands.h"
-#include "qapi/qmp/qerror.h"
-#include "qemu/base64.h"
-#include "qemu/cutils.h"
-
-/* Maximum captured guest-exec out_data/err_data - 16MB */
-#define GUEST_EXEC_MAX_OUTPUT (16*1024*1024)
-/* Allocation and I/O buffer for reading guest-exec out_data/err_data - 4KB */
-#define GUEST_EXEC_IO_SIZE (4*1024)
-
-/* 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_delimited(int64_t id, Error **errp)
-{
- ga_set_response_delimited(ga_state);
- return id;
-}
-
-int64_t qmp_guest_sync(int64_t id, Error **errp)
-{
- return id;
-}
-
-void qmp_guest_ping(Error **errp)
-{
- slog("guest-ping called");
-}
-
-static void qmp_command_info(QmpCommand *cmd, void *opaque)
-{
- GuestAgentInfo *info = opaque;
- GuestAgentCommandInfo *cmd_info;
- GuestAgentCommandInfoList *cmd_info_list;
-
- cmd_info = g_new0(GuestAgentCommandInfo, 1);
- cmd_info->name = g_strdup(qmp_command_name(cmd));
- cmd_info->enabled = qmp_command_is_enabled(cmd);
- cmd_info->success_response = qmp_has_success_response(cmd);
-
- cmd_info_list = g_new0(GuestAgentCommandInfoList, 1);
- cmd_info_list->value = cmd_info;
- cmd_info_list->next = info->supported_commands;
- info->supported_commands = cmd_info_list;
-}
-
-struct GuestAgentInfo *qmp_guest_info(Error **errp)
-{
- GuestAgentInfo *info = g_new0(GuestAgentInfo, 1);
-
- info->version = g_strdup(QEMU_VERSION);
- qmp_for_each_command(qmp_command_info, info);
- return info;
-}
-
-struct GuestExecIOData {
- guchar *data;
- gsize size;
- gsize length;
- gint closed;
- bool truncated;
- const char *name;
-};
-typedef struct GuestExecIOData GuestExecIOData;
-
-struct GuestExecInfo {
- GPid pid;
- int64_t pid_numeric;
- gint status;
- bool has_output;
- gint finished;
- GuestExecIOData in;
- GuestExecIOData out;
- GuestExecIOData err;
- QTAILQ_ENTRY(GuestExecInfo) next;
-};
-typedef struct GuestExecInfo GuestExecInfo;
-
-static struct {
- QTAILQ_HEAD(, GuestExecInfo) processes;
-} guest_exec_state = {
- .processes = QTAILQ_HEAD_INITIALIZER(guest_exec_state.processes),
-};
-
-static int64_t gpid_to_int64(GPid pid)
-{
-#ifdef G_OS_WIN32
- return GetProcessId(pid);
-#else
- return (int64_t)pid;
-#endif
-}
-
-static GuestExecInfo *guest_exec_info_add(GPid pid)
-{
- GuestExecInfo *gei;
-
- gei = g_new0(GuestExecInfo, 1);
- gei->pid = pid;
- gei->pid_numeric = gpid_to_int64(pid);
- QTAILQ_INSERT_TAIL(&guest_exec_state.processes, gei, next);
-
- return gei;
-}
-
-static GuestExecInfo *guest_exec_info_find(int64_t pid_numeric)
-{
- GuestExecInfo *gei;
-
- QTAILQ_FOREACH(gei, &guest_exec_state.processes, next) {
- if (gei->pid_numeric == pid_numeric) {
- return gei;
- }
- }
-
- return NULL;
-}
-
-GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **err)
-{
- GuestExecInfo *gei;
- GuestExecStatus *ges;
-
- slog("guest-exec-status called, pid: %u", (uint32_t)pid);
-
- gei = guest_exec_info_find(pid);
- if (gei == NULL) {
- error_setg(err, QERR_INVALID_PARAMETER, "pid");
- return NULL;
- }
-
- ges = g_new0(GuestExecStatus, 1);
-
- bool finished = g_atomic_int_get(&gei->finished);
-
- /* need to wait till output channels are closed
- * to be sure we captured all output at this point */
- if (gei->has_output) {
- finished = finished && g_atomic_int_get(&gei->out.closed);
- finished = finished && g_atomic_int_get(&gei->err.closed);
- }
-
- ges->exited = finished;
- if (finished) {
- /* Glib has no portable way to parse exit status.
- * On UNIX, we can get either exit code from normal termination
- * or signal number.
- * On Windows, it is either the same exit code or the exception
- * value for an unhandled exception that caused the process
- * to terminate.
- * See MSDN for GetExitCodeProcess() and ntstatus.h for possible
- * well-known codes, e.g. C0000005 ACCESS_DENIED - analog of SIGSEGV
- * References:
- * https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx
- * https://msdn.microsoft.com/en-us/library/aa260331(v=vs.60).aspx
- */
-#ifdef G_OS_WIN32
- /* Additionally WIN32 does not provide any additional information
- * on whetherthe child exited or terminated via signal.
- * We use this simple range check to distingish application exit code
- * (usually value less then 256) and unhandled exception code with
- * ntstatus (always value greater then 0xC0000005). */
- if ((uint32_t)gei->status < 0xC0000000U) {
- ges->has_exitcode = true;
- ges->exitcode = gei->status;
- } else {
- ges->has_signal = true;
- ges->signal = gei->status;
- }
-#else
- if (WIFEXITED(gei->status)) {
- ges->has_exitcode = true;
- ges->exitcode = WEXITSTATUS(gei->status);
- } else if (WIFSIGNALED(gei->status)) {
- ges->has_signal = true;
- ges->signal = WTERMSIG(gei->status);
- }
-#endif
- if (gei->out.length > 0) {
- ges->has_out_data = true;
- ges->out_data = g_base64_encode(gei->out.data, gei->out.length);
- g_free(gei->out.data);
- ges->has_out_truncated = gei->out.truncated;
- }
-
- if (gei->err.length > 0) {
- ges->has_err_data = true;
- ges->err_data = g_base64_encode(gei->err.data, gei->err.length);
- g_free(gei->err.data);
- ges->has_err_truncated = gei->err.truncated;
- }
-
- QTAILQ_REMOVE(&guest_exec_state.processes, gei, next);
- g_free(gei);
- }
-
- return ges;
-}
-
-/* Get environment variables or arguments array for execve(). */
-static char **guest_exec_get_args(const strList *entry, bool log)
-{
- const strList *it;
- int count = 1, i = 0; /* reserve for NULL terminator */
- char **args;
- char *str; /* for logging array of arguments */
- size_t str_size = 1;
-
- for (it = entry; it != NULL; it = it->next) {
- count++;
- str_size += 1 + strlen(it->value);
- }
-
- str = g_malloc(str_size);
- *str = 0;
- args = g_malloc(count * sizeof(char *));
- for (it = entry; it != NULL; it = it->next) {
- args[i++] = it->value;
- pstrcat(str, str_size, it->value);
- if (it->next) {
- pstrcat(str, str_size, " ");
- }
- }
- args[i] = NULL;
-
- if (log) {
- slog("guest-exec called: \"%s\"", str);
- }
- g_free(str);
-
- return args;
-}
-
-static void guest_exec_child_watch(GPid pid, gint status, gpointer data)
-{
- GuestExecInfo *gei = (GuestExecInfo *)data;
-
- g_debug("guest_exec_child_watch called, pid: %d, status: %u",
- (int32_t)gpid_to_int64(pid), (uint32_t)status);
-
- gei->status = status;
- gei->finished = true;
-
- g_spawn_close_pid(pid);
-}
-
-/** Reset ignored signals back to default. */
-static void guest_exec_task_setup(gpointer data)
-{
-#if !defined(G_OS_WIN32)
- struct sigaction sigact;
-
- memset(&sigact, 0, sizeof(struct sigaction));
- sigact.sa_handler = SIG_DFL;
-
- if (sigaction(SIGPIPE, &sigact, NULL) != 0) {
- slog("sigaction() failed to reset child process's SIGPIPE: %s",
- strerror(errno));
- }
-#endif
-}
-
-static gboolean guest_exec_input_watch(GIOChannel *ch,
- GIOCondition cond, gpointer p_)
-{
- GuestExecIOData *p = (GuestExecIOData *)p_;
- gsize bytes_written = 0;
- GIOStatus status;
- GError *gerr = NULL;
-
- /* nothing left to write */
- if (p->size == p->length) {
- goto done;
- }
-
- status = g_io_channel_write_chars(ch, (gchar *)p->data + p->length,
- p->size - p->length, &bytes_written, &gerr);
-
- /* can be not 0 even if not G_IO_STATUS_NORMAL */
- if (bytes_written != 0) {
- p->length += bytes_written;
- }
-
- /* continue write, our callback will be called again */
- if (status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_AGAIN) {
- return true;
- }
-
- if (gerr) {
- g_warning("qga: i/o error writing to input_data channel: %s",
- gerr->message);
- g_error_free(gerr);
- }
-
-done:
- g_io_channel_shutdown(ch, true, NULL);
- g_io_channel_unref(ch);
- g_atomic_int_set(&p->closed, 1);
- g_free(p->data);
-
- return false;
-}
-
-static gboolean guest_exec_output_watch(GIOChannel *ch,
- GIOCondition cond, gpointer p_)
-{
- GuestExecIOData *p = (GuestExecIOData *)p_;
- gsize bytes_read;
- GIOStatus gstatus;
-
- if (cond == G_IO_HUP || cond == G_IO_ERR) {
- goto close;
- }
-
- if (p->size == p->length) {
- gpointer t = NULL;
- if (!p->truncated && p->size < GUEST_EXEC_MAX_OUTPUT) {
- t = g_try_realloc(p->data, p->size + GUEST_EXEC_IO_SIZE);
- }
- if (t == NULL) {
- /* ignore truncated output */
- gchar buf[GUEST_EXEC_IO_SIZE];
-
- p->truncated = true;
- gstatus = g_io_channel_read_chars(ch, buf, sizeof(buf),
- &bytes_read, NULL);
- if (gstatus == G_IO_STATUS_EOF || gstatus == G_IO_STATUS_ERROR) {
- goto close;
- }
-
- return true;
- }
- p->size += GUEST_EXEC_IO_SIZE;
- p->data = t;
- }
-
- /* Calling read API once.
- * On next available data our callback will be called again */
- gstatus = g_io_channel_read_chars(ch, (gchar *)p->data + p->length,
- p->size - p->length, &bytes_read, NULL);
- if (gstatus == G_IO_STATUS_EOF || gstatus == G_IO_STATUS_ERROR) {
- goto close;
- }
-
- p->length += bytes_read;
-
- return true;
-
-close:
- g_io_channel_shutdown(ch, true, NULL);
- g_io_channel_unref(ch);
- g_atomic_int_set(&p->closed, 1);
- return false;
-}
-
-GuestExec *qmp_guest_exec(const char *path,
- bool has_arg, strList *arg,
- bool has_env, strList *env,
- bool has_input_data, const char *input_data,
- bool has_capture_output, bool capture_output,
- Error **err)
-{
- GPid pid;
- GuestExec *ge = NULL;
- GuestExecInfo *gei;
- char **argv, **envp;
- strList arglist;
- gboolean ret;
- GError *gerr = NULL;
- gint in_fd, out_fd, err_fd;
- GIOChannel *in_ch, *out_ch, *err_ch;
- GSpawnFlags flags;
- bool has_output = (has_capture_output && capture_output);
- uint8_t *input = NULL;
- size_t ninput = 0;
-
- arglist.value = (char *)path;
- arglist.next = has_arg ? arg : NULL;
-
- if (has_input_data) {
- input = qbase64_decode(input_data, -1, &ninput, err);
- if (!input) {
- return NULL;
- }
- }
-
- argv = guest_exec_get_args(&arglist, true);
- envp = has_env ? guest_exec_get_args(env, false) : NULL;
-
- flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD;
-#if GLIB_CHECK_VERSION(2, 33, 2)
- flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
-#endif
- if (!has_output) {
- flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL;
- }
-
- ret = g_spawn_async_with_pipes(NULL, argv, envp, flags,
- guest_exec_task_setup, NULL, &pid, has_input_data ? &in_fd : NULL,
- has_output ? &out_fd : NULL, has_output ? &err_fd : NULL, &gerr);
- if (!ret) {
- error_setg(err, QERR_QGA_COMMAND_FAILED, gerr->message);
- g_error_free(gerr);
- goto done;
- }
-
- ge = g_new0(GuestExec, 1);
- ge->pid = gpid_to_int64(pid);
-
- gei = guest_exec_info_add(pid);
- gei->has_output = has_output;
- g_child_watch_add(pid, guest_exec_child_watch, gei);
-
- if (has_input_data) {
- gei->in.data = input;
- gei->in.size = ninput;
-#ifdef G_OS_WIN32
- in_ch = g_io_channel_win32_new_fd(in_fd);
-#else
- in_ch = g_io_channel_unix_new(in_fd);
-#endif
- g_io_channel_set_encoding(in_ch, NULL, NULL);
- g_io_channel_set_buffered(in_ch, false);
- g_io_channel_set_flags(in_ch, G_IO_FLAG_NONBLOCK, NULL);
- g_io_channel_set_close_on_unref(in_ch, true);
- g_io_add_watch(in_ch, G_IO_OUT, guest_exec_input_watch, &gei->in);
- }
-
- if (has_output) {
-#ifdef G_OS_WIN32
- out_ch = g_io_channel_win32_new_fd(out_fd);
- err_ch = g_io_channel_win32_new_fd(err_fd);
-#else
- out_ch = g_io_channel_unix_new(out_fd);
- err_ch = g_io_channel_unix_new(err_fd);
-#endif
- g_io_channel_set_encoding(out_ch, NULL, NULL);
- g_io_channel_set_encoding(err_ch, NULL, NULL);
- g_io_channel_set_buffered(out_ch, false);
- g_io_channel_set_buffered(err_ch, false);
- g_io_channel_set_close_on_unref(out_ch, true);
- g_io_channel_set_close_on_unref(err_ch, true);
- g_io_add_watch(out_ch, G_IO_IN | G_IO_HUP,
- guest_exec_output_watch, &gei->out);
- g_io_add_watch(err_ch, G_IO_IN | G_IO_HUP,
- guest_exec_output_watch, &gei->err);
- }
-
-done:
- g_free(argv);
- g_free(envp);
-
- return ge;
-}
-
-/* Convert GuestFileWhence (either a raw integer or an enum value) into
- * the guest's SEEK_ constants. */
-int ga_parse_whence(GuestFileWhence *whence, Error **errp)
-{
- /* Exploit the fact that we picked values to match QGA_SEEK_*. */
- if (whence->type == QTYPE_QSTRING) {
- whence->type = QTYPE_QINT;
- whence->u.value = whence->u.name;
- }
- switch (whence->u.value) {
- case QGA_SEEK_SET:
- return SEEK_SET;
- case QGA_SEEK_CUR:
- return SEEK_CUR;
- case QGA_SEEK_END:
- return SEEK_END;
- }
- error_setg(errp, "invalid whence code %"PRId64, whence->u.value);
- return -1;
-}
diff --git a/qemu/qga/guest-agent-command-state.c b/qemu/qga/guest-agent-command-state.c
deleted file mode 100644
index 20b9b2222..000000000
--- a/qemu/qga/guest-agent-command-state.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * QEMU Guest Agent command state interfaces
- *
- * Copyright IBM Corp. 2011
- *
- * 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 "qemu/osdep.h"
-#include <glib.h>
-#include "qga/guest-agent-core.h"
-
-struct GACommandState {
- GSList *groups;
-};
-
-typedef struct GACommandGroup {
- void (*init)(void);
- void (*cleanup)(void);
-} GACommandGroup;
-
-/* handle init/cleanup for stateful guest commands */
-
-void ga_command_state_add(GACommandState *cs,
- void (*init)(void),
- void (*cleanup)(void))
-{
- GACommandGroup *cg = g_new0(GACommandGroup, 1);
- cg->init = init;
- cg->cleanup = cleanup;
- cs->groups = g_slist_append(cs->groups, cg);
-}
-
-static void ga_command_group_init(gpointer opaque, gpointer unused)
-{
- GACommandGroup *cg = opaque;
-
- g_assert(cg);
- if (cg->init) {
- cg->init();
- }
-}
-
-void ga_command_state_init_all(GACommandState *cs)
-{
- g_assert(cs);
- g_slist_foreach(cs->groups, ga_command_group_init, NULL);
-}
-
-static void ga_command_group_cleanup(gpointer opaque, gpointer unused)
-{
- GACommandGroup *cg = opaque;
-
- g_assert(cg);
- if (cg->cleanup) {
- cg->cleanup();
- }
-}
-
-void ga_command_state_cleanup_all(GACommandState *cs)
-{
- g_assert(cs);
- g_slist_foreach(cs->groups, ga_command_group_cleanup, NULL);
-}
-
-GACommandState *ga_command_state_new(void)
-{
- GACommandState *cs = g_new0(GACommandState, 1);
- cs->groups = NULL;
- return cs;
-}
diff --git a/qemu/qga/guest-agent-core.h b/qemu/qga/guest-agent-core.h
deleted file mode 100644
index 0a4951604..000000000
--- a/qemu/qga/guest-agent-core.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * QEMU Guest Agent core declarations
- *
- * Copyright IBM Corp. 2011
- *
- * Authors:
- * Adam Litke <aglitke@linux.vnet.ibm.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 "qapi/qmp/dispatch.h"
-#include "qemu-common.h"
-#include "qga-qmp-commands.h"
-
-#define QGA_READ_COUNT_DEFAULT 4096
-
-typedef struct GAState GAState;
-typedef struct GACommandState GACommandState;
-extern GAState *ga_state;
-
-GList *ga_command_blacklist_init(GList *blacklist);
-void ga_command_state_init(GAState *s, GACommandState *cs);
-void ga_command_state_add(GACommandState *cs,
- void (*init)(void),
- void (*cleanup)(void));
-void ga_command_state_init_all(GACommandState *cs);
-void ga_command_state_cleanup_all(GACommandState *cs);
-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 GCC_FMT_ATTR(1, 2) slog(const gchar *fmt, ...);
-void ga_set_response_delimited(GAState *s);
-bool ga_is_frozen(GAState *s);
-void ga_set_frozen(GAState *s);
-void ga_unset_frozen(GAState *s);
-const char *ga_fsfreeze_hook(GAState *s);
-int64_t ga_get_fd_handle(GAState *s, Error **errp);
-int ga_parse_whence(GuestFileWhence *whence, Error **errp);
-
-#ifndef _WIN32
-void reopen_fd_to_null(int fd);
-#endif
diff --git a/qemu/qga/installer/qemu-ga.wxs b/qemu/qga/installer/qemu-ga.wxs
deleted file mode 100644
index fa2260caf..000000000
--- a/qemu/qga/installer/qemu-ga.wxs
+++ /dev/null
@@ -1,185 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
- <?ifndef env.QEMU_GA_VERSION ?>
- <?error Environment variable QEMU_GA_VERSION undefined?>
- <?endif?>
-
- <?ifndef env.QEMU_GA_DISTRO ?>
- <?error Environment variable QEMU_GA_DISTRO undefined?>
- <?endif?>
-
- <?ifndef env.QEMU_GA_MANUFACTURER ?>
- <?error Environment variable QEMU_GA_MANUFACTURER undefined?>
- <?endif?>
-
- <?ifndef var.Arch?>
- <?error Define Arch to 32 or 64?>
- <?endif?>
-
- <?ifndef var.Mingw_bin?>
- <?if $(var.Arch) = "64"?>
- <?define Mingw_bin=/usr/x86_64-w64-mingw32/sys-root/mingw/bin ?>
- <?endif?>
- <?if $(var.Arch) = "32"?>
- <?define Mingw_bin=/usr/i686-w64-mingw32/sys-root/mingw/bin ?>
- <?endif?>
- <?endif?>
-
- <?if $(var.Arch) = "64"?>
- <?define ArchLib=libgcc_s_seh-1.dll?>
- <?define GaProgramFilesFolder="ProgramFiles64Folder" ?>
- <?endif?>
-
- <?if $(var.Arch) = "32"?>
- <?define ArchLib=libgcc_s_sjlj-1.dll?>
- <?define GaProgramFilesFolder="ProgramFilesFolder" ?>
- <?endif?>
-
- <?ifndef var.ArchLib ?>
- <?error Unexpected Arch value $(var.Arch)?>
- <?endif?>
-
- <Product
- Name="QEMU guest agent"
- Id="{DF9974AD-E41A-4304-81AD-69AA8F299766}"
- UpgradeCode="{EB6B8302-C06E-4BEC-ADAC-932C68A3A98D}"
- Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
- Version="$(env.QEMU_GA_VERSION)"
- Language="1033">
- <?if $(var.Arch) = 32 ?>
- <Condition Message="Error: 32-bit version of Qemu GA can not be installed on 64-bit Windows.">NOT VersionNT64</Condition>
- <?endif?>
- <Package
- Manufacturer="$(env.QEMU_GA_MANUFACTURER)"
- InstallerVersion="200"
- Languages="1033"
- Compressed="yes"
- InstallScope="perMachine"
- />
- <Media Id="1" Cabinet="qemu_ga.$(env.QEMU_GA_VERSION).cab" EmbedCab="yes" />
- <Property Id="WHSLogo">1</Property>
- <MajorUpgrade
- DowngradeErrorMessage="Error: A newer version of QEMU guest agent is already installed."
- />
-
- <Directory Id="TARGETDIR" Name="SourceDir">
- <Directory Id="$(var.GaProgramFilesFolder)" Name="QEMU Guest Agent">
- <Directory Id="qemu_ga_directory" Name="Qemu-ga">
- <Component Id="qemu_ga" Guid="{908B7199-DE2A-4DC6-A8D0-27A5AE444FEA}">
- <File Id="qemu_ga.exe" Name="qemu-ga.exe" Source="$(env.BUILD_DIR)/qemu-ga.exe" KeyPath="yes" DiskId="1"/>
- <ServiceInstall
- Id="ServiceInstaller"
- Type="ownProcess"
- Vital="yes"
- Name="QEMU-GA"
- DisplayName="QEMU Guest Agent"
- Description="QEMU Guest Agent"
- Start="auto"
- Account="LocalSystem"
- ErrorControl="ignore"
- Interactive="no"
- Arguments="-d"
- >
- </ServiceInstall>
- <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="QEMU-GA" Wait="no" />
- </Component>
- <?ifdef var.InstallVss?>
- <Component Id="qga_vss_dll" Guid="{CB19C453-FABB-4BB1-ABAB-6B74F687BFBB}">
- <File Id="qga_vss.dll" Name="qga-vss.dll" Source="$(env.BUILD_DIR)/qga/vss-win32/qga-vss.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="qga_vss_tlb" Guid="{D8D584B1-59C2-4FB7-A91F-636FF7BFA66E}">
- <File Id="qga_vss.tlb" Name="qga-vss.tlb" Source="$(env.BUILD_DIR)/qga/vss-win32/qga-vss.tlb" KeyPath="yes" DiskId="1"/>
- </Component>
- <?endif?>
- <?if $(var.Arch) = "32"?>
- <Component Id="gspawn-helper-console" Guid="{446185B3-87BE-43D2-96B8-0FEFD9E8696D}">
- <File Id="gspawn-win32-helper-console.exe" Name="gspawn-win32-helper-console.exe" Source="$(var.Mingw_bin)/gspawn-win32-helper-console.exe" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="gspawn-helper" Guid="{CD67A5A3-2DB1-4DA1-A67A-8D71E797B466}">
- <File Id="gspawn-win32-helper.exe" Name="gspawn-win32-helper.exe" Source="$(var.Mingw_bin)/gspawn-win32-helper-console.exe" KeyPath="yes" DiskId="1"/>
- </Component>
- <?endif?>
- <?if $(var.Arch) = "64"?>
- <Component Id="gspawn-helper-console" Guid="{9E615A9F-349A-4992-A5C2-C10BAD173660}">
- <File Id="gspawn-win64-helper-console.exe" Name="gspawn-win64-helper-console.exe" Source="$(var.Mingw_bin)/gspawn-win64-helper-console.exe" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="gspawn-helper" Guid="{D201AD22-1846-4E4F-B6E1-C7A908ED2457}">
- <File Id="gspawn-win64-helper.exe" Name="gspawn-win64-helper.exe" Source="$(var.Mingw_bin)/gspawn-win64-helper-console.exe" KeyPath="yes" DiskId="1"/>
- </Component>
- <?endif?>
- <Component Id="iconv" Guid="{35EE3558-D34B-4F0A-B8BD-430FF0775246}">
- <File Id="iconv.dll" Name="iconv.dll" Source="$(var.Mingw_bin)/iconv.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="libgcc_arch_lib" Guid="{ADD4D07D-4515-4AB6-AF3E-C904961B4BB0}">
- <File Id="libgcc_arch_lib" Name="$(var.ArchLib)" Source="$(var.Mingw_bin)/$(var.ArchLib)" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="libglib" Guid="{D31BFD83-2773-4B65-B45A-E0D2ADA58679}">
- <File Id="libglib_2.0_0.dll" Name="libglib-2.0-0.dll" Source="$(var.Mingw_bin)/libglib-2.0-0.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="libintl" Guid="{A641BC2D-A907-4A94-9149-F30ED430878F}">
- <File Id="libintl_8.dll" Name="libintl-8.dll" Source="$(var.Mingw_bin)/libintl-8.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="libssp" Guid="{7880087B-02B4-4EF6-A5D3-D18F8E3D90E1}">
- <File Id="libssp_0.dll" Name="libssp-0.dll" Source="$(var.Mingw_bin)/libssp-0.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="libwinpthread" Guid="{6C117C78-0F47-4B07-8F34-6BEE11643829}">
- <File Id="libwinpthread_1.dll" Name="libwinpthread-1.dll" Source="$(var.Mingw_bin)/libwinpthread-1.dll" KeyPath="yes" DiskId="1"/>
- </Component>
- <Component Id="registry_entries" Guid="{D075D109-51CA-11E3-9F8B-000C29858960}">
- <RegistryKey Root="HKLM"
- Key="Software\$(env.QEMU_GA_MANUFACTURER)\$(env.QEMU_GA_DISTRO)\Tools\QemuGA">
- <RegistryValue Type="string" Name="ProductID" Value="fb0a0d66-c7fb-4e2e-a16b-c4a3bfe8d13b" />
- <RegistryValue Type="string" Name="Version" Value="$(env.QEMU_GA_VERSION)" />
- </RegistryKey>
- </Component>
- </Directory>
- </Directory>
- </Directory>
-
- <Property Id="cmd" Value="cmd.exe"/>
- <Property Id="REINSTALLMODE" Value="amus"/>
-
- <?ifdef var.InstallVss?>
- <CustomAction Id="RegisterCom"
- ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-install'
- Execute="deferred"
- Property="cmd"
- Impersonate="no"
- Return="check"
- >
- </CustomAction>
- <CustomAction Id="UnRegisterCom"
- ExeCommand='/c "[qemu_ga_directory]qemu-ga.exe" -s vss-uninstall'
- Execute="deferred"
- Property="cmd"
- Impersonate="no"
- Return="check"
- >
- </CustomAction>
- <?endif?>
-
- <Feature Id="QEMUFeature" Title="QEMU Guest Agent" Level="1">
- <ComponentRef Id="qemu_ga" />
- <?ifdef var.InstallVss?>
- <ComponentRef Id="qga_vss_dll" />
- <ComponentRef Id="qga_vss_tlb" />
- <?endif?>
- <ComponentRef Id="gspawn-helper-console" />
- <ComponentRef Id="gspawn-helper" />
- <ComponentRef Id="iconv" />
- <ComponentRef Id="libgcc_arch_lib" />
- <ComponentRef Id="libglib" />
- <ComponentRef Id="libintl" />
- <ComponentRef Id="libssp" />
- <ComponentRef Id="libwinpthread" />
- <ComponentRef Id="registry_entries" />
- </Feature>
-
- <InstallExecuteSequence>
- <?ifdef var.InstallVss?>
- <Custom Action="UnRegisterCom" After="StopServices">Installed</Custom>
- <Custom Action="RegisterCom" After="InstallServices">NOT REMOVE</Custom>
- <?endif?>
- </InstallExecuteSequence>
- </Product>
-</Wix>
diff --git a/qemu/qga/main.c b/qemu/qga/main.c
deleted file mode 100644
index c55278210..000000000
--- a/qemu/qga/main.c
+++ /dev/null
@@ -1,1394 +0,0 @@
-/*
- * QEMU Guest Agent
- *
- * Copyright IBM Corp. 2011
- *
- * Authors:
- * Adam Litke <aglitke@linux.vnet.ibm.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 "qemu/osdep.h"
-#include <glib.h>
-#include <getopt.h>
-#include <glib/gstdio.h>
-#ifndef _WIN32
-#include <syslog.h>
-#include <sys/wait.h>
-#endif
-#include "qapi/qmp/json-streamer.h"
-#include "qapi/qmp/json-parser.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qjson.h"
-#include "qga/guest-agent-core.h"
-#include "qemu/module.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/qmp/dispatch.h"
-#include "qga/channel.h"
-#include "qemu/bswap.h"
-#include "qemu/help_option.h"
-#ifdef _WIN32
-#include "qga/service-win32.h"
-#include "qga/vss-win32.h"
-#endif
-#ifdef __linux__
-#include <linux/fs.h>
-#ifdef FIFREEZE
-#define CONFIG_FSFREEZE
-#endif
-#endif
-
-#ifndef _WIN32
-#define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0"
-#define QGA_STATE_RELATIVE_DIR "run"
-#define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0"
-#else
-#define QGA_VIRTIO_PATH_DEFAULT "\\\\.\\Global\\org.qemu.guest_agent.0"
-#define QGA_STATE_RELATIVE_DIR "qemu-ga"
-#define QGA_SERIAL_PATH_DEFAULT "COM1"
-#endif
-#ifdef CONFIG_FSFREEZE
-#define QGA_FSFREEZE_HOOK_DEFAULT CONFIG_QEMU_CONFDIR "/fsfreeze-hook"
-#endif
-#define QGA_SENTINEL_BYTE 0xFF
-#define QGA_CONF_DEFAULT CONFIG_QEMU_CONFDIR G_DIR_SEPARATOR_S "qemu-ga.conf"
-
-static struct {
- const char *state_dir;
- const char *pidfile;
-} dfl_pathnames;
-
-typedef struct GAPersistentState {
-#define QGA_PSTATE_DEFAULT_FD_COUNTER 1000
- int64_t fd_counter;
-} GAPersistentState;
-
-struct GAState {
- JSONMessageParser parser;
- GMainLoop *main_loop;
- GAChannel *channel;
- bool virtio; /* fastpath to check for virtio to deal with poll() quirks */
- GACommandState *command_state;
- GLogLevelFlags log_level;
- FILE *log_file;
- bool logging_enabled;
-#ifdef _WIN32
- GAService service;
-#endif
- bool delimit_response;
- bool frozen;
- GList *blacklist;
- char *state_filepath_isfrozen;
- struct {
- const char *log_filepath;
- const char *pid_filepath;
- } deferred_options;
-#ifdef CONFIG_FSFREEZE
- const char *fsfreeze_hook;
-#endif
- gchar *pstate_filepath;
- GAPersistentState pstate;
-};
-
-struct GAState *ga_state;
-
-/* commands that are safe to issue while filesystems are frozen */
-static const char *ga_freeze_whitelist[] = {
- "guest-ping",
- "guest-info",
- "guest-sync",
- "guest-sync-delimited",
- "guest-fsfreeze-status",
- "guest-fsfreeze-thaw",
- NULL
-};
-
-#ifdef _WIN32
-DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
- LPVOID ctx);
-VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
-#endif
-
-static void
-init_dfl_pathnames(void)
-{
- g_assert(dfl_pathnames.state_dir == NULL);
- g_assert(dfl_pathnames.pidfile == NULL);
- dfl_pathnames.state_dir = qemu_get_local_state_pathname(
- QGA_STATE_RELATIVE_DIR);
- dfl_pathnames.pidfile = qemu_get_local_state_pathname(
- QGA_STATE_RELATIVE_DIR G_DIR_SEPARATOR_S "qemu-ga.pid");
-}
-
-static void quit_handler(int sig)
-{
- /* if we're frozen, don't exit unless we're absolutely forced to,
- * because it's basically impossible for graceful exit to complete
- * unless all log/pid files are on unfreezable filesystems. there's
- * also a very likely chance killing the agent before unfreezing
- * the filesystems is a mistake (or will be viewed as one later).
- */
- if (ga_is_frozen(ga_state)) {
- return;
- }
- g_debug("received signal num %d, quitting", sig);
-
- if (g_main_loop_is_running(ga_state->main_loop)) {
- g_main_loop_quit(ga_state->main_loop);
- }
-}
-
-#ifndef _WIN32
-static gboolean register_signal_handlers(void)
-{
- struct sigaction sigact;
- int ret;
-
- memset(&sigact, 0, sizeof(struct sigaction));
- sigact.sa_handler = quit_handler;
-
- ret = sigaction(SIGINT, &sigact, NULL);
- if (ret == -1) {
- g_error("error configuring signal handler: %s", strerror(errno));
- }
- ret = sigaction(SIGTERM, &sigact, NULL);
- if (ret == -1) {
- g_error("error configuring signal handler: %s", strerror(errno));
- }
-
- sigact.sa_handler = SIG_IGN;
- if (sigaction(SIGPIPE, &sigact, NULL) != 0) {
- g_error("error configuring SIGPIPE signal handler: %s",
- strerror(errno));
- }
-
- return true;
-}
-
-/* TODO: use this in place of all post-fork() fclose(std*) callers */
-void reopen_fd_to_null(int fd)
-{
- int nullfd;
-
- nullfd = open("/dev/null", O_RDWR);
- if (nullfd < 0) {
- return;
- }
-
- dup2(nullfd, fd);
-
- if (nullfd != fd) {
- close(nullfd);
- }
-}
-#endif
-
-static void usage(const char *cmd)
-{
- printf(
-"Usage: %s [-m <method> -p <path>] [<options>]\n"
-"QEMU Guest Agent %s\n"
-"\n"
-" -m, --method transport method: one of unix-listen, virtio-serial, or\n"
-" isa-serial (virtio-serial is the default)\n"
-" -p, --path device/socket path (the default for virtio-serial is:\n"
-" %s,\n"
-" the default for isa-serial is:\n"
-" %s)\n"
-" -l, --logfile set logfile path, logs to stderr by default\n"
-" -f, --pidfile specify pidfile (default is %s)\n"
-#ifdef CONFIG_FSFREEZE
-" -F, --fsfreeze-hook\n"
-" enable fsfreeze hook. Accepts an optional argument that\n"
-" specifies script to run on freeze/thaw. Script will be\n"
-" called with 'freeze'/'thaw' arguments accordingly.\n"
-" (default is %s)\n"
-" If using -F with an argument, do not follow -F with a\n"
-" space.\n"
-" (for example: -F/var/run/fsfreezehook.sh)\n"
-#endif
-" -t, --statedir specify dir to store state information (absolute paths\n"
-" only, default is %s)\n"
-" -v, --verbose log extra debugging information\n"
-" -V, --version print version information and exit\n"
-" -d, --daemonize become a daemon\n"
-#ifdef _WIN32
-" -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n"
-#endif
-" -b, --blacklist comma-separated list of RPCs to disable (no spaces, \"?\"\n"
-" to list available RPCs)\n"
-" -D, --dump-conf dump a qemu-ga config file based on current config\n"
-" options / command-line parameters to stdout\n"
-" -h, --help display this help and exit\n"
-"\n"
-"Report bugs to <mdroth@linux.vnet.ibm.com>\n"
- , cmd, QEMU_VERSION, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT,
- dfl_pathnames.pidfile,
-#ifdef CONFIG_FSFREEZE
- QGA_FSFREEZE_HOOK_DEFAULT,
-#endif
- dfl_pathnames.state_dir);
-}
-
-static const char *ga_log_level_str(GLogLevelFlags level)
-{
- switch (level & G_LOG_LEVEL_MASK) {
- case G_LOG_LEVEL_ERROR:
- return "error";
- case G_LOG_LEVEL_CRITICAL:
- return "critical";
- case G_LOG_LEVEL_WARNING:
- return "warning";
- case G_LOG_LEVEL_MESSAGE:
- return "message";
- case G_LOG_LEVEL_INFO:
- return "info";
- case G_LOG_LEVEL_DEBUG:
- return "debug";
- default:
- return "user";
- }
-}
-
-bool ga_logging_enabled(GAState *s)
-{
- return s->logging_enabled;
-}
-
-void ga_disable_logging(GAState *s)
-{
- s->logging_enabled = false;
-}
-
-void ga_enable_logging(GAState *s)
-{
- s->logging_enabled = true;
-}
-
-static void ga_log(const gchar *domain, GLogLevelFlags level,
- const gchar *msg, gpointer opaque)
-{
- GAState *s = opaque;
- GTimeVal time;
- const char *level_str = ga_log_level_str(level);
-
- if (!ga_logging_enabled(s)) {
- return;
- }
-
- level &= G_LOG_LEVEL_MASK;
-#ifndef _WIN32
- if (g_strcmp0(domain, "syslog") == 0) {
- syslog(LOG_INFO, "%s: %s", level_str, msg);
- } else if (level & s->log_level) {
-#else
- if (level & s->log_level) {
-#endif
- g_get_current_time(&time);
- fprintf(s->log_file,
- "%lu.%lu: %s: %s\n", time.tv_sec, time.tv_usec, level_str, msg);
- fflush(s->log_file);
- }
-}
-
-void ga_set_response_delimited(GAState *s)
-{
- s->delimit_response = true;
-}
-
-static FILE *ga_open_logfile(const char *logfile)
-{
- FILE *f;
-
- f = fopen(logfile, "a");
- if (!f) {
- return NULL;
- }
-
- qemu_set_cloexec(fileno(f));
- return f;
-}
-
-#ifndef _WIN32
-static bool ga_open_pidfile(const char *pidfile)
-{
- int pidfd;
- char pidstr[32];
-
- pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
- if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) {
- g_critical("Cannot lock pid file, %s", strerror(errno));
- if (pidfd != -1) {
- close(pidfd);
- }
- return false;
- }
-
- if (ftruncate(pidfd, 0)) {
- g_critical("Failed to truncate pid file");
- goto fail;
- }
- snprintf(pidstr, sizeof(pidstr), "%d\n", getpid());
- if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
- g_critical("Failed to write pid file");
- goto fail;
- }
-
- /* keep pidfile open & locked forever */
- return true;
-
-fail:
- unlink(pidfile);
- close(pidfd);
- return false;
-}
-#else /* _WIN32 */
-static bool ga_open_pidfile(const char *pidfile)
-{
- return true;
-}
-#endif
-
-static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
-{
- return strcmp(str1, str2);
-}
-
-/* disable commands that aren't safe for fsfreeze */
-static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque)
-{
- bool whitelisted = false;
- int i = 0;
- const char *name = qmp_command_name(cmd);
-
- while (ga_freeze_whitelist[i] != NULL) {
- if (strcmp(name, ga_freeze_whitelist[i]) == 0) {
- whitelisted = true;
- }
- i++;
- }
- if (!whitelisted) {
- g_debug("disabling command: %s", name);
- qmp_disable_command(name);
- }
-}
-
-/* [re-]enable all commands, except those explicitly blacklisted by user */
-static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque)
-{
- GList *blacklist = opaque;
- const char *name = qmp_command_name(cmd);
-
- if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL &&
- !qmp_command_is_enabled(cmd)) {
- g_debug("enabling command: %s", name);
- qmp_enable_command(name);
- }
-}
-
-static bool ga_create_file(const char *path)
-{
- int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR);
- if (fd == -1) {
- g_warning("unable to open/create file %s: %s", path, strerror(errno));
- return false;
- }
- close(fd);
- return true;
-}
-
-static bool ga_delete_file(const char *path)
-{
- int ret = unlink(path);
- if (ret == -1) {
- g_warning("unable to delete file: %s: %s", path, strerror(errno));
- return false;
- }
-
- return true;
-}
-
-bool ga_is_frozen(GAState *s)
-{
- return s->frozen;
-}
-
-void ga_set_frozen(GAState *s)
-{
- if (ga_is_frozen(s)) {
- return;
- }
- /* disable all non-whitelisted (for frozen state) commands */
- qmp_for_each_command(ga_disable_non_whitelisted, NULL);
- g_warning("disabling logging due to filesystem freeze");
- ga_disable_logging(s);
- s->frozen = true;
- if (!ga_create_file(s->state_filepath_isfrozen)) {
- g_warning("unable to create %s, fsfreeze may not function properly",
- s->state_filepath_isfrozen);
- }
-}
-
-void ga_unset_frozen(GAState *s)
-{
- if (!ga_is_frozen(s)) {
- return;
- }
-
- /* if we delayed creation/opening of pid/log files due to being
- * in a frozen state at start up, do it now
- */
- if (s->deferred_options.log_filepath) {
- s->log_file = ga_open_logfile(s->deferred_options.log_filepath);
- if (!s->log_file) {
- s->log_file = stderr;
- }
- s->deferred_options.log_filepath = NULL;
- }
- ga_enable_logging(s);
- g_warning("logging re-enabled due to filesystem unfreeze");
- if (s->deferred_options.pid_filepath) {
- if (!ga_open_pidfile(s->deferred_options.pid_filepath)) {
- g_warning("failed to create/open pid file");
- }
- s->deferred_options.pid_filepath = NULL;
- }
-
- /* enable all disabled, non-blacklisted commands */
- qmp_for_each_command(ga_enable_non_blacklisted, s->blacklist);
- s->frozen = false;
- if (!ga_delete_file(s->state_filepath_isfrozen)) {
- g_warning("unable to delete %s, fsfreeze may not function properly",
- s->state_filepath_isfrozen);
- }
-}
-
-#ifdef CONFIG_FSFREEZE
-const char *ga_fsfreeze_hook(GAState *s)
-{
- return s->fsfreeze_hook;
-}
-#endif
-
-static void become_daemon(const char *pidfile)
-{
-#ifndef _WIN32
- pid_t pid, sid;
-
- pid = fork();
- if (pid < 0) {
- exit(EXIT_FAILURE);
- }
- if (pid > 0) {
- exit(EXIT_SUCCESS);
- }
-
- if (pidfile) {
- if (!ga_open_pidfile(pidfile)) {
- g_critical("failed to create pidfile");
- exit(EXIT_FAILURE);
- }
- }
-
- umask(S_IRWXG | S_IRWXO);
- sid = setsid();
- if (sid < 0) {
- goto fail;
- }
- if ((chdir("/")) < 0) {
- goto fail;
- }
-
- reopen_fd_to_null(STDIN_FILENO);
- reopen_fd_to_null(STDOUT_FILENO);
- reopen_fd_to_null(STDERR_FILENO);
- return;
-
-fail:
- if (pidfile) {
- unlink(pidfile);
- }
- g_critical("failed to daemonize");
- exit(EXIT_FAILURE);
-#endif
-}
-
-static int send_response(GAState *s, QObject *payload)
-{
- const char *buf;
- QString *payload_qstr, *response_qstr;
- GIOStatus status;
-
- g_assert(payload && s->channel);
-
- payload_qstr = qobject_to_json(payload);
- if (!payload_qstr) {
- return -EINVAL;
- }
-
- if (s->delimit_response) {
- s->delimit_response = false;
- response_qstr = qstring_new();
- qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE);
- qstring_append(response_qstr, qstring_get_str(payload_qstr));
- QDECREF(payload_qstr);
- } else {
- response_qstr = payload_qstr;
- }
-
- qstring_append_chr(response_qstr, '\n');
- buf = qstring_get_str(response_qstr);
- status = ga_channel_write_all(s->channel, buf, strlen(buf));
- QDECREF(response_qstr);
- if (status != G_IO_STATUS_NORMAL) {
- return -EIO;
- }
-
- return 0;
-}
-
-static void process_command(GAState *s, QDict *req)
-{
- QObject *rsp = NULL;
- int ret;
-
- g_assert(req);
- g_debug("processing command");
- rsp = qmp_dispatch(QOBJECT(req));
- if (rsp) {
- ret = send_response(s, rsp);
- if (ret) {
- g_warning("error sending response: %s", strerror(ret));
- }
- qobject_decref(rsp);
- }
-}
-
-/* handle requests/control events coming in over the channel */
-static void process_event(JSONMessageParser *parser, GQueue *tokens)
-{
- GAState *s = container_of(parser, GAState, parser);
- QDict *qdict;
- Error *err = NULL;
- int ret;
-
- g_assert(s && parser);
-
- g_debug("process_event: called");
- qdict = qobject_to_qdict(json_parser_parse_err(tokens, NULL, &err));
- if (err || !qdict) {
- QDECREF(qdict);
- qdict = qdict_new();
- if (!err) {
- g_warning("failed to parse event: unknown error");
- error_setg(&err, QERR_JSON_PARSING);
- } else {
- g_warning("failed to parse event: %s", error_get_pretty(err));
- }
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
- }
-
- /* handle host->guest commands */
- if (qdict_haskey(qdict, "execute")) {
- process_command(s, qdict);
- } else {
- if (!qdict_haskey(qdict, "error")) {
- QDECREF(qdict);
- qdict = qdict_new();
- g_warning("unrecognized payload format");
- error_setg(&err, QERR_UNSUPPORTED);
- qdict_put_obj(qdict, "error", qmp_build_error_object(err));
- error_free(err);
- }
- ret = send_response(s, QOBJECT(qdict));
- if (ret < 0) {
- g_warning("error sending error response: %s", strerror(-ret));
- }
- }
-
- QDECREF(qdict);
-}
-
-/* false return signals GAChannel to close the current client connection */
-static gboolean channel_event_cb(GIOCondition condition, gpointer data)
-{
- GAState *s = data;
- gchar buf[QGA_READ_COUNT_DEFAULT+1];
- gsize count;
- GIOStatus status = ga_channel_read(s->channel, buf, QGA_READ_COUNT_DEFAULT, &count);
- switch (status) {
- case G_IO_STATUS_ERROR:
- g_warning("error reading channel");
- return false;
- case G_IO_STATUS_NORMAL:
- buf[count] = 0;
- g_debug("read data, count: %d, data: %s", (int)count, buf);
- json_message_parser_feed(&s->parser, (char *)buf, (int)count);
- break;
- case G_IO_STATUS_EOF:
- g_debug("received EOF");
- if (!s->virtio) {
- return false;
- }
- /* fall through */
- case G_IO_STATUS_AGAIN:
- /* virtio causes us to spin here when no process is attached to
- * host-side chardev. sleep a bit to mitigate this
- */
- if (s->virtio) {
- usleep(100*1000);
- }
- return true;
- default:
- g_warning("unknown channel read status, closing");
- return false;
- }
- return true;
-}
-
-static gboolean channel_init(GAState *s, const gchar *method, const gchar *path)
-{
- GAChannelMethod channel_method;
-
- if (strcmp(method, "virtio-serial") == 0) {
- s->virtio = true; /* virtio requires special handling in some cases */
- channel_method = GA_CHANNEL_VIRTIO_SERIAL;
- } else if (strcmp(method, "isa-serial") == 0) {
- channel_method = GA_CHANNEL_ISA_SERIAL;
- } else if (strcmp(method, "unix-listen") == 0) {
- channel_method = GA_CHANNEL_UNIX_LISTEN;
- } else {
- g_critical("unsupported channel method/type: %s", method);
- return false;
- }
-
- s->channel = ga_channel_new(channel_method, path, channel_event_cb, s);
- if (!s->channel) {
- g_critical("failed to create guest agent channel");
- return false;
- }
-
- return true;
-}
-
-#ifdef _WIN32
-DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
- LPVOID ctx)
-{
- DWORD ret = NO_ERROR;
- GAService *service = &ga_state->service;
-
- switch (ctrl)
- {
- case SERVICE_CONTROL_STOP:
- case SERVICE_CONTROL_SHUTDOWN:
- quit_handler(SIGTERM);
- service->status.dwCurrentState = SERVICE_STOP_PENDING;
- SetServiceStatus(service->status_handle, &service->status);
- break;
-
- default:
- ret = ERROR_CALL_NOT_IMPLEMENTED;
- }
- return ret;
-}
-
-VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
-{
- GAService *service = &ga_state->service;
-
- service->status_handle = RegisterServiceCtrlHandlerEx(QGA_SERVICE_NAME,
- service_ctrl_handler, NULL);
-
- if (service->status_handle == 0) {
- g_critical("Failed to register extended requests function!\n");
- return;
- }
-
- service->status.dwServiceType = SERVICE_WIN32;
- service->status.dwCurrentState = SERVICE_RUNNING;
- service->status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
- service->status.dwWin32ExitCode = NO_ERROR;
- service->status.dwServiceSpecificExitCode = NO_ERROR;
- service->status.dwCheckPoint = 0;
- service->status.dwWaitHint = 0;
- SetServiceStatus(service->status_handle, &service->status);
-
- g_main_loop_run(ga_state->main_loop);
-
- service->status.dwCurrentState = SERVICE_STOPPED;
- SetServiceStatus(service->status_handle, &service->status);
-}
-#endif
-
-static void set_persistent_state_defaults(GAPersistentState *pstate)
-{
- g_assert(pstate);
- pstate->fd_counter = QGA_PSTATE_DEFAULT_FD_COUNTER;
-}
-
-static void persistent_state_from_keyfile(GAPersistentState *pstate,
- GKeyFile *keyfile)
-{
- g_assert(pstate);
- g_assert(keyfile);
- /* if any fields are missing, either because the file was tampered with
- * by agents of chaos, or because the field wasn't present at the time the
- * file was created, the best we can ever do is start over with the default
- * values. so load them now, and ignore any errors in accessing key-value
- * pairs
- */
- set_persistent_state_defaults(pstate);
-
- if (g_key_file_has_key(keyfile, "global", "fd_counter", NULL)) {
- pstate->fd_counter =
- g_key_file_get_integer(keyfile, "global", "fd_counter", NULL);
- }
-}
-
-static void persistent_state_to_keyfile(const GAPersistentState *pstate,
- GKeyFile *keyfile)
-{
- g_assert(pstate);
- g_assert(keyfile);
-
- g_key_file_set_integer(keyfile, "global", "fd_counter", pstate->fd_counter);
-}
-
-static gboolean write_persistent_state(const GAPersistentState *pstate,
- const gchar *path)
-{
- GKeyFile *keyfile = g_key_file_new();
- GError *gerr = NULL;
- gboolean ret = true;
- gchar *data = NULL;
- gsize data_len;
-
- g_assert(pstate);
-
- persistent_state_to_keyfile(pstate, keyfile);
- data = g_key_file_to_data(keyfile, &data_len, &gerr);
- if (gerr) {
- g_critical("failed to convert persistent state to string: %s",
- gerr->message);
- ret = false;
- goto out;
- }
-
- g_file_set_contents(path, data, data_len, &gerr);
- if (gerr) {
- g_critical("failed to write persistent state to %s: %s",
- path, gerr->message);
- ret = false;
- goto out;
- }
-
-out:
- if (gerr) {
- g_error_free(gerr);
- }
- if (keyfile) {
- g_key_file_free(keyfile);
- }
- g_free(data);
- return ret;
-}
-
-static gboolean read_persistent_state(GAPersistentState *pstate,
- const gchar *path, gboolean frozen)
-{
- GKeyFile *keyfile = NULL;
- GError *gerr = NULL;
- struct stat st;
- gboolean ret = true;
-
- g_assert(pstate);
-
- if (stat(path, &st) == -1) {
- /* it's okay if state file doesn't exist, but any other error
- * indicates a permissions issue or some other misconfiguration
- * that we likely won't be able to recover from.
- */
- if (errno != ENOENT) {
- g_critical("unable to access state file at path %s: %s",
- path, strerror(errno));
- ret = false;
- goto out;
- }
-
- /* file doesn't exist. initialize state to default values and
- * attempt to save now. (we could wait till later when we have
- * modified state we need to commit, but if there's a problem,
- * such as a missing parent directory, we want to catch it now)
- *
- * there is a potential scenario where someone either managed to
- * update the agent from a version that didn't use a key store
- * while qemu-ga thought the filesystem was frozen, or
- * deleted the key store prior to issuing a fsfreeze, prior
- * to restarting the agent. in this case we go ahead and defer
- * initial creation till we actually have modified state to
- * write, otherwise fail to recover from freeze.
- */
- set_persistent_state_defaults(pstate);
- if (!frozen) {
- ret = write_persistent_state(pstate, path);
- if (!ret) {
- g_critical("unable to create state file at path %s", path);
- ret = false;
- goto out;
- }
- }
- ret = true;
- goto out;
- }
-
- keyfile = g_key_file_new();
- g_key_file_load_from_file(keyfile, path, 0, &gerr);
- if (gerr) {
- g_critical("error loading persistent state from path: %s, %s",
- path, gerr->message);
- ret = false;
- goto out;
- }
-
- persistent_state_from_keyfile(pstate, keyfile);
-
-out:
- if (keyfile) {
- g_key_file_free(keyfile);
- }
- if (gerr) {
- g_error_free(gerr);
- }
-
- return ret;
-}
-
-int64_t ga_get_fd_handle(GAState *s, Error **errp)
-{
- int64_t handle;
-
- g_assert(s->pstate_filepath);
- /* we blacklist commands and avoid operations that potentially require
- * writing to disk when we're in a frozen state. this includes opening
- * new files, so we should never get here in that situation
- */
- g_assert(!ga_is_frozen(s));
-
- handle = s->pstate.fd_counter++;
-
- /* This should never happen on a reasonable timeframe, as guest-file-open
- * would have to be issued 2^63 times */
- if (s->pstate.fd_counter == INT64_MAX) {
- abort();
- }
-
- if (!write_persistent_state(&s->pstate, s->pstate_filepath)) {
- error_setg(errp, "failed to commit persistent state to disk");
- return -1;
- }
-
- return handle;
-}
-
-static void ga_print_cmd(QmpCommand *cmd, void *opaque)
-{
- printf("%s\n", qmp_command_name(cmd));
-}
-
-static GList *split_list(const gchar *str, const gchar *delim)
-{
- GList *list = NULL;
- int i;
- gchar **strv;
-
- strv = g_strsplit(str, delim, -1);
- for (i = 0; strv[i]; i++) {
- list = g_list_prepend(list, strv[i]);
- }
- g_free(strv);
-
- return list;
-}
-
-typedef struct GAConfig {
- char *channel_path;
- char *method;
- char *log_filepath;
- char *pid_filepath;
-#ifdef CONFIG_FSFREEZE
- char *fsfreeze_hook;
-#endif
- char *state_dir;
-#ifdef _WIN32
- const char *service;
-#endif
- gchar *bliststr; /* blacklist may point to this string */
- GList *blacklist;
- int daemonize;
- GLogLevelFlags log_level;
- int dumpconf;
-} GAConfig;
-
-static void config_load(GAConfig *config)
-{
- GError *gerr = NULL;
- GKeyFile *keyfile;
- const char *conf = g_getenv("QGA_CONF") ?: QGA_CONF_DEFAULT;
-
- /* read system config */
- keyfile = g_key_file_new();
- if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) {
- goto end;
- }
- if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) {
- config->daemonize =
- g_key_file_get_boolean(keyfile, "general", "daemon", &gerr);
- }
- if (g_key_file_has_key(keyfile, "general", "method", NULL)) {
- config->method =
- g_key_file_get_string(keyfile, "general", "method", &gerr);
- }
- if (g_key_file_has_key(keyfile, "general", "path", NULL)) {
- config->channel_path =
- g_key_file_get_string(keyfile, "general", "path", &gerr);
- }
- if (g_key_file_has_key(keyfile, "general", "logfile", NULL)) {
- config->log_filepath =
- g_key_file_get_string(keyfile, "general", "logfile", &gerr);
- }
- if (g_key_file_has_key(keyfile, "general", "pidfile", NULL)) {
- config->pid_filepath =
- g_key_file_get_string(keyfile, "general", "pidfile", &gerr);
- }
-#ifdef CONFIG_FSFREEZE
- if (g_key_file_has_key(keyfile, "general", "fsfreeze-hook", NULL)) {
- config->fsfreeze_hook =
- g_key_file_get_string(keyfile,
- "general", "fsfreeze-hook", &gerr);
- }
-#endif
- if (g_key_file_has_key(keyfile, "general", "statedir", NULL)) {
- config->state_dir =
- g_key_file_get_string(keyfile, "general", "statedir", &gerr);
- }
- if (g_key_file_has_key(keyfile, "general", "verbose", NULL) &&
- g_key_file_get_boolean(keyfile, "general", "verbose", &gerr)) {
- /* enable all log levels */
- config->log_level = G_LOG_LEVEL_MASK;
- }
- if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) {
- config->bliststr =
- g_key_file_get_string(keyfile, "general", "blacklist", &gerr);
- config->blacklist = g_list_concat(config->blacklist,
- split_list(config->bliststr, ","));
- }
-
-end:
- g_key_file_free(keyfile);
- if (gerr &&
- !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) {
- g_critical("error loading configuration from path: %s, %s",
- QGA_CONF_DEFAULT, gerr->message);
- exit(EXIT_FAILURE);
- }
- g_clear_error(&gerr);
-}
-
-static gchar *list_join(GList *list, const gchar separator)
-{
- GString *str = g_string_new("");
-
- while (list) {
- str = g_string_append(str, (gchar *)list->data);
- list = g_list_next(list);
- if (list) {
- str = g_string_append_c(str, separator);
- }
- }
-
- return g_string_free(str, FALSE);
-}
-
-static void config_dump(GAConfig *config)
-{
- GError *error = NULL;
- GKeyFile *keyfile;
- gchar *tmp;
-
- keyfile = g_key_file_new();
- g_assert(keyfile);
-
- g_key_file_set_boolean(keyfile, "general", "daemon", config->daemonize);
- g_key_file_set_string(keyfile, "general", "method", config->method);
- g_key_file_set_string(keyfile, "general", "path", config->channel_path);
- if (config->log_filepath) {
- g_key_file_set_string(keyfile, "general", "logfile",
- config->log_filepath);
- }
- g_key_file_set_string(keyfile, "general", "pidfile", config->pid_filepath);
-#ifdef CONFIG_FSFREEZE
- if (config->fsfreeze_hook) {
- g_key_file_set_string(keyfile, "general", "fsfreeze-hook",
- config->fsfreeze_hook);
- }
-#endif
- g_key_file_set_string(keyfile, "general", "statedir", config->state_dir);
- g_key_file_set_boolean(keyfile, "general", "verbose",
- config->log_level == G_LOG_LEVEL_MASK);
- tmp = list_join(config->blacklist, ',');
- g_key_file_set_string(keyfile, "general", "blacklist", tmp);
- g_free(tmp);
-
- tmp = g_key_file_to_data(keyfile, NULL, &error);
- printf("%s", tmp);
-
- g_free(tmp);
- g_key_file_free(keyfile);
-}
-
-static void config_parse(GAConfig *config, int argc, char **argv)
-{
- const char *sopt = "hVvdm:p:l:f:F::b:s:t:D";
- int opt_ind = 0, ch;
- const struct option lopt[] = {
- { "help", 0, NULL, 'h' },
- { "version", 0, NULL, 'V' },
- { "dump-conf", 0, NULL, 'D' },
- { "logfile", 1, NULL, 'l' },
- { "pidfile", 1, NULL, 'f' },
-#ifdef CONFIG_FSFREEZE
- { "fsfreeze-hook", 2, NULL, 'F' },
-#endif
- { "verbose", 0, NULL, 'v' },
- { "method", 1, NULL, 'm' },
- { "path", 1, NULL, 'p' },
- { "daemonize", 0, NULL, 'd' },
- { "blacklist", 1, NULL, 'b' },
-#ifdef _WIN32
- { "service", 1, NULL, 's' },
-#endif
- { "statedir", 1, NULL, 't' },
- { NULL, 0, NULL, 0 }
- };
-
- while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
- switch (ch) {
- case 'm':
- g_free(config->method);
- config->method = g_strdup(optarg);
- break;
- case 'p':
- g_free(config->channel_path);
- config->channel_path = g_strdup(optarg);
- break;
- case 'l':
- g_free(config->log_filepath);
- config->log_filepath = g_strdup(optarg);
- break;
- case 'f':
- g_free(config->pid_filepath);
- config->pid_filepath = g_strdup(optarg);
- break;
-#ifdef CONFIG_FSFREEZE
- case 'F':
- g_free(config->fsfreeze_hook);
- config->fsfreeze_hook = g_strdup(optarg ?: QGA_FSFREEZE_HOOK_DEFAULT);
- break;
-#endif
- case 't':
- g_free(config->state_dir);
- config->state_dir = g_strdup(optarg);
- break;
- case 'v':
- /* enable all log levels */
- config->log_level = G_LOG_LEVEL_MASK;
- break;
- case 'V':
- printf("QEMU Guest Agent %s\n", QEMU_VERSION);
- exit(EXIT_SUCCESS);
- case 'd':
- config->daemonize = 1;
- break;
- case 'D':
- config->dumpconf = 1;
- break;
- case 'b': {
- if (is_help_option(optarg)) {
- qmp_for_each_command(ga_print_cmd, NULL);
- exit(EXIT_SUCCESS);
- }
- config->blacklist = g_list_concat(config->blacklist,
- split_list(optarg, ","));
- break;
- }
-#ifdef _WIN32
- case 's':
- config->service = optarg;
- if (strcmp(config->service, "install") == 0) {
- if (ga_install_vss_provider()) {
- exit(EXIT_FAILURE);
- }
- if (ga_install_service(config->channel_path,
- config->log_filepath, config->state_dir)) {
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- } else if (strcmp(config->service, "uninstall") == 0) {
- ga_uninstall_vss_provider();
- exit(ga_uninstall_service());
- } else if (strcmp(config->service, "vss-install") == 0) {
- if (ga_install_vss_provider()) {
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- } else if (strcmp(config->service, "vss-uninstall") == 0) {
- ga_uninstall_vss_provider();
- exit(EXIT_SUCCESS);
- } else {
- printf("Unknown service command.\n");
- exit(EXIT_FAILURE);
- }
- break;
-#endif
- case 'h':
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- case '?':
- g_print("Unknown option, try '%s --help' for more information.\n",
- argv[0]);
- exit(EXIT_FAILURE);
- }
- }
-}
-
-static void config_free(GAConfig *config)
-{
- g_free(config->method);
- g_free(config->log_filepath);
- g_free(config->pid_filepath);
- g_free(config->state_dir);
- g_free(config->channel_path);
- g_free(config->bliststr);
-#ifdef CONFIG_FSFREEZE
- g_free(config->fsfreeze_hook);
-#endif
- g_free(config);
-}
-
-static bool check_is_frozen(GAState *s)
-{
-#ifndef _WIN32
- /* check if a previous instance of qemu-ga exited with filesystems' state
- * marked as frozen. this could be a stale value (a non-qemu-ga process
- * or reboot may have since unfrozen them), but better to require an
- * uneeded unfreeze than to risk hanging on start-up
- */
- struct stat st;
- if (stat(s->state_filepath_isfrozen, &st) == -1) {
- /* it's okay if the file doesn't exist, but if we can't access for
- * some other reason, such as permissions, there's a configuration
- * that needs to be addressed. so just bail now before we get into
- * more trouble later
- */
- if (errno != ENOENT) {
- g_critical("unable to access state file at path %s: %s",
- s->state_filepath_isfrozen, strerror(errno));
- return EXIT_FAILURE;
- }
- } else {
- g_warning("previous instance appears to have exited with frozen"
- " filesystems. deferring logging/pidfile creation and"
- " disabling non-fsfreeze-safe commands until"
- " guest-fsfreeze-thaw is issued, or filesystems are"
- " manually unfrozen and the file %s is removed",
- s->state_filepath_isfrozen);
- return true;
- }
-#endif
- return false;
-}
-
-static int run_agent(GAState *s, GAConfig *config)
-{
- ga_state = s;
-
- g_log_set_default_handler(ga_log, s);
- g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
- ga_enable_logging(s);
-
-#ifdef _WIN32
- /* On win32 the state directory is application specific (be it the default
- * or a user override). We got past the command line parsing; let's create
- * the directory (with any intermediate directories). If we run into an
- * error later on, we won't try to clean up the directory, it is considered
- * persistent.
- */
- if (g_mkdir_with_parents(config->state_dir, S_IRWXU) == -1) {
- g_critical("unable to create (an ancestor of) the state directory"
- " '%s': %s", config->state_dir, strerror(errno));
- return EXIT_FAILURE;
- }
-#endif
-
- if (ga_is_frozen(s)) {
- if (config->daemonize) {
- /* delay opening/locking of pidfile till filesystems are unfrozen */
- s->deferred_options.pid_filepath = config->pid_filepath;
- become_daemon(NULL);
- }
- if (config->log_filepath) {
- /* delay opening the log file till filesystems are unfrozen */
- s->deferred_options.log_filepath = config->log_filepath;
- }
- ga_disable_logging(s);
- qmp_for_each_command(ga_disable_non_whitelisted, NULL);
- } else {
- if (config->daemonize) {
- become_daemon(config->pid_filepath);
- }
- if (config->log_filepath) {
- FILE *log_file = ga_open_logfile(config->log_filepath);
- if (!log_file) {
- g_critical("unable to open specified log file: %s",
- strerror(errno));
- return EXIT_FAILURE;
- }
- s->log_file = log_file;
- }
- }
-
- /* load persistent state from disk */
- if (!read_persistent_state(&s->pstate,
- s->pstate_filepath,
- ga_is_frozen(s))) {
- g_critical("failed to load persistent state");
- return EXIT_FAILURE;
- }
-
- config->blacklist = ga_command_blacklist_init(config->blacklist);
- if (config->blacklist) {
- GList *l = config->blacklist;
- s->blacklist = config->blacklist;
- do {
- g_debug("disabling command: %s", (char *)l->data);
- qmp_disable_command(l->data);
- l = g_list_next(l);
- } while (l);
- }
- s->command_state = ga_command_state_new();
- ga_command_state_init(s, s->command_state);
- ga_command_state_init_all(s->command_state);
- json_message_parser_init(&s->parser, process_event);
- ga_state = s;
-#ifndef _WIN32
- if (!register_signal_handlers()) {
- g_critical("failed to register signal handlers");
- return EXIT_FAILURE;
- }
-#endif
-
- s->main_loop = g_main_loop_new(NULL, false);
- if (!channel_init(ga_state, config->method, config->channel_path)) {
- g_critical("failed to initialize guest agent channel");
- return EXIT_FAILURE;
- }
-#ifndef _WIN32
- g_main_loop_run(ga_state->main_loop);
-#else
- if (config->daemonize) {
- SERVICE_TABLE_ENTRY service_table[] = {
- { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
- StartServiceCtrlDispatcher(service_table);
- } else {
- g_main_loop_run(ga_state->main_loop);
- }
-#endif
-
- return EXIT_SUCCESS;
-}
-
-static void free_blacklist_entry(gpointer entry, gpointer unused)
-{
- g_free(entry);
-}
-
-int main(int argc, char **argv)
-{
- int ret = EXIT_SUCCESS;
- GAState *s = g_new0(GAState, 1);
- GAConfig *config = g_new0(GAConfig, 1);
-
- config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
-
- module_call_init(MODULE_INIT_QAPI);
-
- init_dfl_pathnames();
- config_load(config);
- config_parse(config, argc, argv);
-
- if (config->pid_filepath == NULL) {
- config->pid_filepath = g_strdup(dfl_pathnames.pidfile);
- }
-
- if (config->state_dir == NULL) {
- config->state_dir = g_strdup(dfl_pathnames.state_dir);
- }
-
- if (config->method == NULL) {
- config->method = g_strdup("virtio-serial");
- }
-
- if (config->channel_path == NULL) {
- if (strcmp(config->method, "virtio-serial") == 0) {
- /* try the default path for the virtio-serial port */
- config->channel_path = g_strdup(QGA_VIRTIO_PATH_DEFAULT);
- } else if (strcmp(config->method, "isa-serial") == 0) {
- /* try the default path for the serial port - COM1 */
- config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT);
- } else {
- g_critical("must specify a path for this channel");
- ret = EXIT_FAILURE;
- goto end;
- }
- }
-
- s->log_level = config->log_level;
- s->log_file = stderr;
-#ifdef CONFIG_FSFREEZE
- s->fsfreeze_hook = config->fsfreeze_hook;
-#endif
- s->pstate_filepath = g_strdup_printf("%s/qga.state", config->state_dir);
- s->state_filepath_isfrozen = g_strdup_printf("%s/qga.state.isfrozen",
- config->state_dir);
- s->frozen = check_is_frozen(s);
-
- if (config->dumpconf) {
- config_dump(config);
- goto end;
- }
-
- ret = run_agent(s, config);
-
-end:
- if (s->command_state) {
- ga_command_state_cleanup_all(s->command_state);
- }
- if (s->channel) {
- ga_channel_free(s->channel);
- }
- g_list_foreach(config->blacklist, free_blacklist_entry, NULL);
- g_free(s->pstate_filepath);
- g_free(s->state_filepath_isfrozen);
-
- if (config->daemonize) {
- unlink(config->pid_filepath);
- }
-
- config_free(config);
-
- return ret;
-}
diff --git a/qemu/qga/qapi-schema.json b/qemu/qga/qapi-schema.json
deleted file mode 100644
index c21f3084d..000000000
--- a/qemu/qga/qapi-schema.json
+++ /dev/null
@@ -1,1028 +0,0 @@
-# *-*- Mode: Python -*-*
-
-##
-#
-# General note concerning the use of guest agent interfaces:
-#
-# "unsupported" is a higher-level error than the errors that individual
-# commands might document. The caller should always be prepared to receive
-# QERR_UNSUPPORTED, even if the given command doesn't specify it, or doesn't
-# document any failure mode at all.
-#
-##
-
-##
-# @guest-sync-delimited:
-#
-# Echo back a unique integer value, and prepend to response a
-# leading sentinel byte (0xFF) the client can check scan for.
-#
-# This is used by clients talking to the guest agent over the
-# wire to ensure the stream is in sync and doesn't contain stale
-# data from previous client. It must be issued upon initial
-# connection, and after any client-side timeouts (including
-# timeouts on receiving a response to this command).
-#
-# After issuing this request, all guest agent responses should be
-# ignored until the response containing the unique integer value
-# the client passed in is returned. Receival of the 0xFF sentinel
-# byte must be handled as an indication that the client's
-# lexer/tokenizer/parser state should be flushed/reset in
-# preparation for reliably receiving the subsequent response. As
-# an optimization, clients may opt to ignore all data until a
-# sentinel value is receiving to avoid unnecessary processing of
-# stale data.
-#
-# Similarly, clients should also precede this *request*
-# with a 0xFF byte to make sure the guest agent flushes any
-# partially read JSON data from a previous client connection.
-#
-# @id: randomly generated 64-bit integer
-#
-# Returns: The unique integer id passed in by the client
-#
-# Since: 1.1
-##
-{ 'command': 'guest-sync-delimited',
- 'data': { 'id': 'int' },
- 'returns': 'int' }
-
-##
-# @guest-sync:
-#
-# Echo back a unique integer value
-#
-# This is used by clients talking to the guest agent over the
-# wire to ensure the stream is in sync and doesn't contain stale
-# data from previous client. All guest agent responses should be
-# ignored until the provided unique integer value is returned,
-# and it is up to the client to handle stale whole or
-# partially-delivered JSON text in such a way that this response
-# can be obtained.
-#
-# In cases where a partial stale response was previously
-# received by the client, this cannot always be done reliably.
-# One particular scenario being if qemu-ga responses are fed
-# character-by-character into a JSON parser. In these situations,
-# using guest-sync-delimited may be optimal.
-#
-# For clients that fetch responses line by line and convert them
-# to JSON objects, guest-sync should be sufficient, but note that
-# in cases where the channel is dirty some attempts at parsing the
-# response may result in a parser error.
-#
-# Such clients should also precede this command
-# with a 0xFF byte to make sure the guest agent flushes any
-# partially read JSON data from a previous session.
-#
-# @id: randomly generated 64-bit integer
-#
-# Returns: The unique integer id passed in by the client
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-sync',
- 'data': { 'id': 'int' },
- 'returns': 'int' }
-
-##
-# @guest-ping:
-#
-# Ping the guest agent, a non-error return implies success
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-ping' }
-
-##
-# @guest-get-time:
-#
-# Get the information about guest's System Time relative to
-# the Epoch of 1970-01-01 in UTC.
-#
-# Returns: Time in nanoseconds.
-#
-# Since 1.5
-##
-{ 'command': 'guest-get-time',
- 'returns': 'int' }
-
-##
-# @guest-set-time:
-#
-# Set guest time.
-#
-# When a guest is paused or migrated to a file then loaded
-# from that file, the guest OS has no idea that there
-# was a big gap in the time. Depending on how long the
-# gap was, NTP might not be able to resynchronize the
-# guest.
-#
-# This command tries to set guest's System Time to the
-# given value, then sets the Hardware Clock (RTC) to the
-# current System Time. This will make it easier for a guest
-# to resynchronize without waiting for NTP. If no @time is
-# specified, then the time to set is read from RTC. However,
-# this may not be supported on all platforms (i.e. Windows).
-# If that's the case users are advised to always pass a
-# value.
-#
-# @time: #optional time of nanoseconds, relative to the Epoch
-# of 1970-01-01 in UTC.
-#
-# Returns: Nothing on success.
-#
-# Since: 1.5
-##
-{ 'command': 'guest-set-time',
- 'data': { '*time': 'int' } }
-
-##
-# @GuestAgentCommandInfo:
-#
-# Information about guest agent commands.
-#
-# @name: name of the command
-#
-# @enabled: whether command is currently enabled by guest admin
-#
-# @success-response: whether command returns a response on success
-# (since 1.7)
-#
-# Since 1.1.0
-##
-{ 'struct': 'GuestAgentCommandInfo',
- 'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
-
-##
-# @GuestAgentInfo
-#
-# Information about guest agent.
-#
-# @version: guest agent version
-#
-# @supported_commands: Information about guest agent commands
-#
-# Since 0.15.0
-##
-{ 'struct': 'GuestAgentInfo',
- 'data': { 'version': 'str',
- 'supported_commands': ['GuestAgentCommandInfo'] } }
-##
-# @guest-info:
-#
-# Get some information about the guest agent.
-#
-# Returns: @GuestAgentInfo
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-info',
- 'returns': 'GuestAgentInfo' }
-
-##
-# @guest-shutdown:
-#
-# Initiate guest-activated shutdown. Note: this is an asynchronous
-# shutdown request, with no guarantee of successful shutdown.
-#
-# @mode: #optional "halt", "powerdown" (default), or "reboot"
-#
-# This command does NOT return a response on success. Success condition
-# is indicated by the VM exiting with a zero exit status or, when
-# running with --no-shutdown, by issuing the query-status QMP command
-# to confirm the VM status is "shutdown".
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
- 'success-response': false }
-
-##
-# @guest-file-open:
-#
-# Open a file in the guest and retrieve a file handle for it
-#
-# @filepath: Full path to the file in the guest to open.
-#
-# @mode: #optional open mode, as per fopen(), "r" is the default.
-#
-# Returns: Guest file handle on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-open',
- 'data': { 'path': 'str', '*mode': 'str' },
- 'returns': 'int' }
-
-##
-# @guest-file-close:
-#
-# Close an open file in the guest
-#
-# @handle: filehandle returned by guest-file-open
-#
-# Returns: Nothing on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-close',
- 'data': { 'handle': 'int' } }
-
-##
-# @GuestFileRead
-#
-# Result of guest agent file-read operation
-#
-# @count: number of bytes read (note: count is *before*
-# base64-encoding is applied)
-#
-# @buf-b64: base64-encoded bytes read
-#
-# @eof: whether EOF was encountered during read operation.
-#
-# Since: 0.15.0
-##
-{ 'struct': 'GuestFileRead',
- 'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
-
-##
-# @guest-file-read:
-#
-# Read from an open file in the guest. Data will be base64-encoded
-#
-# @handle: filehandle returned by guest-file-open
-#
-# @count: #optional maximum number of bytes to read (default is 4KB)
-#
-# Returns: @GuestFileRead on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-read',
- 'data': { 'handle': 'int', '*count': 'int' },
- 'returns': 'GuestFileRead' }
-
-##
-# @GuestFileWrite
-#
-# Result of guest agent file-write operation
-#
-# @count: number of bytes written (note: count is actual bytes
-# written, after base64-decoding of provided buffer)
-#
-# @eof: whether EOF was encountered during write operation.
-#
-# Since: 0.15.0
-##
-{ 'struct': 'GuestFileWrite',
- 'data': { 'count': 'int', 'eof': 'bool' } }
-
-##
-# @guest-file-write:
-#
-# Write to an open file in the guest.
-#
-# @handle: filehandle returned by guest-file-open
-#
-# @buf-b64: base64-encoded string representing data to be written
-#
-# @count: #optional bytes to write (actual bytes, after base64-decode),
-# default is all content in buf-b64 buffer after base64 decoding
-#
-# Returns: @GuestFileWrite on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-write',
- 'data': { 'handle': 'int', 'buf-b64': 'str', '*count': 'int' },
- 'returns': 'GuestFileWrite' }
-
-
-##
-# @GuestFileSeek
-#
-# Result of guest agent file-seek operation
-#
-# @position: current file position
-#
-# @eof: whether EOF was encountered during file seek
-#
-# Since: 0.15.0
-##
-{ 'struct': 'GuestFileSeek',
- 'data': { 'position': 'int', 'eof': 'bool' } }
-
-##
-# @QGASeek:
-#
-# Symbolic names for use in @guest-file-seek
-#
-# @set: Set to the specified offset (same effect as 'whence':0)
-# @cur: Add offset to the current location (same effect as 'whence':1)
-# @end: Add offset to the end of the file (same effect as 'whence':2)
-#
-# Since: 2.6
-##
-{ 'enum': 'QGASeek', 'data': [ 'set', 'cur', 'end' ] }
-
-##
-# @GuestFileWhence:
-#
-# Controls the meaning of offset to @guest-file-seek.
-#
-# @value: Integral value (0 for set, 1 for cur, 2 for end), available
-# for historical reasons, and might differ from the host's or
-# guest's SEEK_* values (since: 0.15)
-# @name: Symbolic name, and preferred interface
-#
-# Since: 2.6
-##
-{ 'alternate': 'GuestFileWhence',
- 'data': { 'value': 'int', 'name': 'QGASeek' } }
-
-##
-# @guest-file-seek:
-#
-# Seek to a position in the file, as with fseek(), and return the
-# current file position afterward. Also encapsulates ftell()'s
-# functionality, with offset=0 and whence=1.
-#
-# @handle: filehandle returned by guest-file-open
-#
-# @offset: bytes to skip over in the file stream
-#
-# @whence: Symbolic or numeric code for interpreting offset
-#
-# Returns: @GuestFileSeek on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-seek',
- 'data': { 'handle': 'int', 'offset': 'int',
- 'whence': 'GuestFileWhence' },
- 'returns': 'GuestFileSeek' }
-
-##
-# @guest-file-flush:
-#
-# Write file changes bufferred in userspace to disk/kernel buffers
-#
-# @handle: filehandle returned by guest-file-open
-#
-# Returns: Nothing on success.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-file-flush',
- 'data': { 'handle': 'int' } }
-
-##
-# @GuestFsFreezeStatus
-#
-# An enumeration of filesystem freeze states
-#
-# @thawed: filesystems thawed/unfrozen
-#
-# @frozen: all non-network guest filesystems frozen
-#
-# Since: 0.15.0
-##
-{ 'enum': 'GuestFsfreezeStatus',
- 'data': [ 'thawed', 'frozen' ] }
-
-##
-# @guest-fsfreeze-status:
-#
-# Get guest fsfreeze state. error state indicates
-#
-# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined below)
-#
-# Note: This may fail to properly report the current state as a result of
-# some other guest processes having issued an fs freeze/thaw.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-fsfreeze-status',
- 'returns': 'GuestFsfreezeStatus' }
-
-##
-# @guest-fsfreeze-freeze:
-#
-# Sync and freeze all freezable, local guest filesystems
-#
-# Returns: Number of file systems currently frozen. On error, all filesystems
-# will be thawed.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-fsfreeze-freeze',
- 'returns': 'int' }
-
-##
-# @guest-fsfreeze-freeze-list:
-#
-# Sync and freeze specified guest filesystems
-#
-# @mountpoints: #optional an array of mountpoints of filesystems to be frozen.
-# If omitted, every mounted filesystem is frozen.
-#
-# Returns: Number of file systems currently frozen. On error, all filesystems
-# will be thawed.
-#
-# Since: 2.2
-##
-{ 'command': 'guest-fsfreeze-freeze-list',
- 'data': { '*mountpoints': ['str'] },
- 'returns': 'int' }
-
-##
-# @guest-fsfreeze-thaw:
-#
-# Unfreeze all frozen guest filesystems
-#
-# Returns: Number of file systems thawed by this call
-#
-# Note: if return value does not match the previous call to
-# guest-fsfreeze-freeze, this likely means some freezable
-# filesystems were unfrozen before this call, and that the
-# filesystem state may have changed before issuing this
-# command.
-#
-# Since: 0.15.0
-##
-{ 'command': 'guest-fsfreeze-thaw',
- 'returns': 'int' }
-
-##
-# @GuestFilesystemTrimResult
-#
-# @path: path that was trimmed
-# @error: an error message when trim failed
-# @trimmed: bytes trimmed for this path
-# @minimum: reported effective minimum for this path
-#
-# Since: 2.4
-##
-{ 'struct': 'GuestFilesystemTrimResult',
- 'data': {'path': 'str',
- '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} }
-
-##
-# @GuestFilesystemTrimResponse
-#
-# @paths: list of @GuestFilesystemTrimResult per path that was trimmed
-#
-# Since: 2.4
-##
-{ 'struct': 'GuestFilesystemTrimResponse',
- 'data': {'paths': ['GuestFilesystemTrimResult']} }
-
-##
-# @guest-fstrim:
-#
-# Discard (or "trim") blocks which are not in use by the filesystem.
-#
-# @minimum:
-# Minimum contiguous free range to discard, in bytes. Free ranges
-# smaller than this may be ignored (this is a hint and the guest
-# may not respect it). By increasing this value, the fstrim
-# operation will complete more quickly for filesystems with badly
-# fragmented free space, although not all blocks will be discarded.
-# The default value is zero, meaning "discard every free block".
-#
-# Returns: A @GuestFilesystemTrimResponse which contains the
-# status of all trimmed paths. (since 2.4)
-#
-# Since: 1.2
-##
-{ 'command': 'guest-fstrim',
- 'data': { '*minimum': 'int' },
- 'returns': 'GuestFilesystemTrimResponse' }
-
-##
-# @guest-suspend-disk
-#
-# Suspend guest to disk.
-#
-# This command tries to execute the scripts provided by the pm-utils package.
-# If it's not available, the suspend operation will be performed by manually
-# writing to a sysfs file.
-#
-# For the best results it's strongly recommended to have the pm-utils
-# package installed in the guest.
-#
-# This command does NOT return a response on success. There is a high chance
-# the command succeeded if the VM exits with a zero exit status or, when
-# running with --no-shutdown, by issuing the query-status QMP command to
-# to confirm the VM status is "shutdown". However, the VM could also exit
-# (or set its status to "shutdown") due to other reasons.
-#
-# The following errors may be returned:
-# If suspend to disk is not supported, Unsupported
-#
-# Notes: It's strongly recommended to issue the guest-sync command before
-# sending commands when the guest resumes
-#
-# Since: 1.1
-##
-{ 'command': 'guest-suspend-disk', 'success-response': false }
-
-##
-# @guest-suspend-ram
-#
-# Suspend guest to ram.
-#
-# This command tries to execute the scripts provided by the pm-utils package.
-# If it's not available, the suspend operation will be performed by manually
-# writing to a sysfs file.
-#
-# For the best results it's strongly recommended to have the pm-utils
-# package installed in the guest.
-#
-# IMPORTANT: guest-suspend-ram requires QEMU to support the 'system_wakeup'
-# command. Thus, it's *required* to query QEMU for the presence of the
-# 'system_wakeup' command before issuing guest-suspend-ram.
-#
-# This command does NOT return a response on success. There are two options
-# to check for success:
-# 1. Wait for the SUSPEND QMP event from QEMU
-# 2. Issue the query-status QMP command to confirm the VM status is
-# "suspended"
-#
-# The following errors may be returned:
-# If suspend to ram is not supported, Unsupported
-#
-# Notes: It's strongly recommended to issue the guest-sync command before
-# sending commands when the guest resumes
-#
-# Since: 1.1
-##
-{ 'command': 'guest-suspend-ram', 'success-response': false }
-
-##
-# @guest-suspend-hybrid
-#
-# Save guest state to disk and suspend to ram.
-#
-# This command requires the pm-utils package to be installed in the guest.
-#
-# IMPORTANT: guest-suspend-hybrid requires QEMU to support the 'system_wakeup'
-# command. Thus, it's *required* to query QEMU for the presence of the
-# 'system_wakeup' command before issuing guest-suspend-hybrid.
-#
-# This command does NOT return a response on success. There are two options
-# to check for success:
-# 1. Wait for the SUSPEND QMP event from QEMU
-# 2. Issue the query-status QMP command to confirm the VM status is
-# "suspended"
-#
-# The following errors may be returned:
-# If hybrid suspend is not supported, Unsupported
-#
-# Notes: It's strongly recommended to issue the guest-sync command before
-# sending commands when the guest resumes
-#
-# Since: 1.1
-##
-{ 'command': 'guest-suspend-hybrid', 'success-response': false }
-
-##
-# @GuestIpAddressType:
-#
-# An enumeration of supported IP address types
-#
-# @ipv4: IP version 4
-#
-# @ipv6: IP version 6
-#
-# Since: 1.1
-##
-{ 'enum': 'GuestIpAddressType',
- 'data': [ 'ipv4', 'ipv6' ] }
-
-##
-# @GuestIpAddress:
-#
-# @ip-address: IP address
-#
-# @ip-address-type: Type of @ip-address (e.g. ipv4, ipv6)
-#
-# @prefix: Network prefix length of @ip-address
-#
-# Since: 1.1
-##
-{ 'struct': 'GuestIpAddress',
- 'data': {'ip-address': 'str',
- 'ip-address-type': 'GuestIpAddressType',
- 'prefix': 'int'} }
-
-##
-# @GuestNetworkInterface:
-#
-# @name: The name of interface for which info are being delivered
-#
-# @hardware-address: Hardware address of @name
-#
-# @ip-addresses: List of addresses assigned to @name
-#
-# Since: 1.1
-##
-{ 'struct': 'GuestNetworkInterface',
- 'data': {'name': 'str',
- '*hardware-address': 'str',
- '*ip-addresses': ['GuestIpAddress'] } }
-
-##
-# @guest-network-get-interfaces:
-#
-# Get list of guest IP addresses, MAC addresses
-# and netmasks.
-#
-# Returns: List of GuestNetworkInfo on success.
-#
-# Since: 1.1
-##
-{ 'command': 'guest-network-get-interfaces',
- 'returns': ['GuestNetworkInterface'] }
-
-##
-# @GuestLogicalProcessor:
-#
-# @logical-id: Arbitrary guest-specific unique identifier of the VCPU.
-#
-# @online: Whether the VCPU is enabled.
-#
-# @can-offline: #optional Whether offlining the VCPU is possible. This member
-# is always filled in by the guest agent when the structure is
-# returned, and always ignored on input (hence it can be omitted
-# then).
-#
-# Since: 1.5
-##
-{ 'struct': 'GuestLogicalProcessor',
- 'data': {'logical-id': 'int',
- 'online': 'bool',
- '*can-offline': 'bool'} }
-
-##
-# @guest-get-vcpus:
-#
-# Retrieve the list of the guest's logical processors.
-#
-# This is a read-only operation.
-#
-# Returns: The list of all VCPUs the guest knows about. Each VCPU is put on the
-# list exactly once, but their order is unspecified.
-#
-# Since: 1.5
-##
-{ 'command': 'guest-get-vcpus',
- 'returns': ['GuestLogicalProcessor'] }
-
-##
-# @guest-set-vcpus:
-#
-# Attempt to reconfigure (currently: enable/disable) logical processors inside
-# the guest.
-#
-# The input list is processed node by node in order. In each node @logical-id
-# is used to look up the guest VCPU, for which @online specifies the requested
-# state. The set of distinct @logical-id's is only required to be a subset of
-# the guest-supported identifiers. There's no restriction on list length or on
-# repeating the same @logical-id (with possibly different @online field).
-# Preferably the input list should describe a modified subset of
-# @guest-get-vcpus' return value.
-#
-# Returns: The length of the initial sublist that has been successfully
-# processed. The guest agent maximizes this value. Possible cases:
-#
-# 0: if the @vcpus list was empty on input. Guest state
-# has not been changed. Otherwise,
-#
-# Error: processing the first node of @vcpus failed for the
-# reason returned. Guest state has not been changed.
-# Otherwise,
-#
-# < length(@vcpus): more than zero initial nodes have been processed,
-# but not the entire @vcpus list. Guest state has
-# changed accordingly. To retrieve the error
-# (assuming it persists), repeat the call with the
-# successfully processed initial sublist removed.
-# Otherwise,
-#
-# length(@vcpus): call successful.
-#
-# Since: 1.5
-##
-{ 'command': 'guest-set-vcpus',
- 'data': {'vcpus': ['GuestLogicalProcessor'] },
- 'returns': 'int' }
-
-##
-# @GuestDiskBusType
-#
-# An enumeration of bus type of disks
-#
-# @ide: IDE disks
-# @fdc: floppy disks
-# @scsi: SCSI disks
-# @virtio: virtio disks
-# @xen: Xen disks
-# @usb: USB disks
-# @uml: UML disks
-# @sata: SATA disks
-# @sd: SD cards
-# @unknown: Unknown bus type
-# @ieee1394: Win IEEE 1394 bus type
-# @ssa: Win SSA bus type
-# @fibre: Win fiber channel bus type
-# @raid: Win RAID bus type
-# @iscsi: Win iScsi bus type
-# @sas: Win serial-attaches SCSI bus type
-# @mmc: Win multimedia card (MMC) bus type
-# @virtual: Win virtual bus type
-# @file-backed virtual: Win file-backed bus type
-#
-# Since: 2.2; 'Unknown' and all entries below since 2.4
-##
-{ 'enum': 'GuestDiskBusType',
- 'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
- 'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
- 'sas', 'mmc', 'virtual', 'file-backed-virtual' ] }
-
-
-##
-# @GuestPCIAddress:
-#
-# @domain: domain id
-# @bus: bus id
-# @slot: slot id
-# @function: function id
-#
-# Since: 2.2
-##
-{ 'struct': 'GuestPCIAddress',
- 'data': {'domain': 'int', 'bus': 'int',
- 'slot': 'int', 'function': 'int'} }
-
-##
-# @GuestDiskAddress:
-#
-# @pci-controller: controller's PCI address
-# @type: bus type
-# @bus: bus id
-# @target: target id
-# @unit: unit id
-#
-# Since: 2.2
-##
-{ 'struct': 'GuestDiskAddress',
- 'data': {'pci-controller': 'GuestPCIAddress',
- 'bus-type': 'GuestDiskBusType',
- 'bus': 'int', 'target': 'int', 'unit': 'int'} }
-
-##
-# @GuestFilesystemInfo
-#
-# @name: disk name
-# @mountpoint: mount point path
-# @type: file system type string
-# @disk: an array of disk hardware information that the volume lies on,
-# which may be empty if the disk type is not supported
-#
-# Since: 2.2
-##
-{ 'struct': 'GuestFilesystemInfo',
- 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
- 'disk': ['GuestDiskAddress']} }
-
-##
-# @guest-get-fsinfo:
-#
-# Returns: The list of filesystems information mounted in the guest.
-# The returned mountpoints may be specified to
-# @guest-fsfreeze-freeze-list.
-# Network filesystems (such as CIFS and NFS) are not listed.
-#
-# Since: 2.2
-##
-{ 'command': 'guest-get-fsinfo',
- 'returns': ['GuestFilesystemInfo'] }
-
-##
-# @guest-set-user-password
-#
-# @username: the user account whose password to change
-# @password: the new password entry string, base64 encoded
-# @crypted: true if password is already crypt()d, false if raw
-#
-# If the @crypted flag is true, it is the caller's responsibility
-# to ensure the correct crypt() encryption scheme is used. This
-# command does not attempt to interpret or report on the encryption
-# scheme. Refer to the documentation of the guest operating system
-# in question to determine what is supported.
-#
-# Not all guest operating systems will support use of the
-# @crypted flag, as they may require the clear-text password
-#
-# The @password parameter must always be base64 encoded before
-# transmission, even if already crypt()d, to ensure it is 8-bit
-# safe when passed as JSON.
-#
-# Returns: Nothing on success.
-#
-# Since 2.3
-##
-{ 'command': 'guest-set-user-password',
- 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } }
-
-# @GuestMemoryBlock:
-#
-# @phys-index: Arbitrary guest-specific unique identifier of the MEMORY BLOCK.
-#
-# @online: Whether the MEMORY BLOCK is enabled in guest.
-#
-# @can-offline: #optional Whether offlining the MEMORY BLOCK is possible.
-# This member is always filled in by the guest agent when the
-# structure is returned, and always ignored on input (hence it
-# can be omitted then).
-#
-# Since: 2.3
-##
-{ 'struct': 'GuestMemoryBlock',
- 'data': {'phys-index': 'uint64',
- 'online': 'bool',
- '*can-offline': 'bool'} }
-
-##
-# @guest-get-memory-blocks:
-#
-# Retrieve the list of the guest's memory blocks.
-#
-# This is a read-only operation.
-#
-# Returns: The list of all memory blocks the guest knows about.
-# Each memory block is put on the list exactly once, but their order
-# is unspecified.
-#
-# Since: 2.3
-##
-{ 'command': 'guest-get-memory-blocks',
- 'returns': ['GuestMemoryBlock'] }
-
-##
-# @GuestMemoryBlockResponseType
-#
-# An enumeration of memory block operation result.
-#
-# @success: the operation of online/offline memory block is successful.
-# @not-found: can't find the corresponding memoryXXX directory in sysfs.
-# @operation-not-supported: for some old kernels, it does not support
-# online or offline memory block.
-# @operation-failed: the operation of online/offline memory block fails,
-# because of some errors happen.
-#
-# Since: 2.3
-##
-{ 'enum': 'GuestMemoryBlockResponseType',
- 'data': ['success', 'not-found', 'operation-not-supported',
- 'operation-failed'] }
-
-##
-# @GuestMemoryBlockResponse:
-#
-# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock.
-#
-# @response: the result of memory block operation.
-#
-# @error-code: #optional the error number.
-# When memory block operation fails, we assign the value of
-# 'errno' to this member, it indicates what goes wrong.
-# When the operation succeeds, it will be omitted.
-#
-# Since: 2.3
-##
-{ 'struct': 'GuestMemoryBlockResponse',
- 'data': { 'phys-index': 'uint64',
- 'response': 'GuestMemoryBlockResponseType',
- '*error-code': 'int' }}
-
-##
-# @guest-set-memory-blocks:
-#
-# Attempt to reconfigure (currently: enable/disable) state of memory blocks
-# inside the guest.
-#
-# The input list is processed node by node in order. In each node @phys-index
-# is used to look up the guest MEMORY BLOCK, for which @online specifies the
-# requested state. The set of distinct @phys-index's is only required to be a
-# subset of the guest-supported identifiers. There's no restriction on list
-# length or on repeating the same @phys-index (with possibly different @online
-# field).
-# Preferably the input list should describe a modified subset of
-# @guest-get-memory-blocks' return value.
-#
-# Returns: The operation results, it is a list of @GuestMemoryBlockResponse,
-# which is corresponding to the input list.
-#
-# Note: it will return NULL if the @mem-blks list was empty on input,
-# or there is an error, and in this case, guest state will not be
-# changed.
-#
-# Since: 2.3
-##
-{ 'command': 'guest-set-memory-blocks',
- 'data': {'mem-blks': ['GuestMemoryBlock'] },
- 'returns': ['GuestMemoryBlockResponse'] }
-
-# @GuestMemoryBlockInfo:
-#
-# @size: the size (in bytes) of the guest memory blocks,
-# which are the minimal units of memory block online/offline
-# operations (also called Logical Memory Hotplug).
-#
-# Since: 2.3
-##
-{ 'struct': 'GuestMemoryBlockInfo',
- 'data': {'size': 'uint64'} }
-
-##
-# @guest-get-memory-block-info:
-#
-# Get information relating to guest memory blocks.
-#
-# Returns: memory block size in bytes.
-# Returns: @GuestMemoryBlockInfo
-#
-# Since 2.3
-##
-{ 'command': 'guest-get-memory-block-info',
- 'returns': 'GuestMemoryBlockInfo' }
-
-# @GuestExecStatus:
-#
-# @exited: true if process has already terminated.
-# @exitcode: #optional process exit code if it was normally terminated.
-# @signal: #optional signal number (linux) or unhandled exception code
-# (windows) if the process was abnormally terminated.
-# @out-data: #optional base64-encoded stdout of the process
-# @err-data: #optional base64-encoded stderr of the process
-# Note: @out-data and @err-data are present only
-# if 'capture-output' was specified for 'guest-exec'
-# @out-truncated: #optional true if stdout was not fully captured
-# due to size limitation.
-# @err-truncated: #optional true if stderr was not fully captured
-# due to size limitation.
-#
-# Since: 2.5
-##
-{ 'struct': 'GuestExecStatus',
- 'data': { 'exited': 'bool', '*exitcode': 'int', '*signal': 'int',
- '*out-data': 'str', '*err-data': 'str',
- '*out-truncated': 'bool', '*err-truncated': 'bool' }}
-##
-# @guest-exec-status
-#
-# Check status of process associated with PID retrieved via guest-exec.
-# Reap the process and associated metadata if it has exited.
-#
-# @pid: pid returned from guest-exec
-#
-# Returns: GuestExecStatus on success.
-#
-# Since 2.5
-##
-{ 'command': 'guest-exec-status',
- 'data': { 'pid': 'int' },
- 'returns': 'GuestExecStatus' }
-
-##
-# @GuestExec:
-# @pid: pid of child process in guest OS
-#
-#Since: 2.5
-##
-{ 'struct': 'GuestExec',
- 'data': { 'pid': 'int'} }
-
-##
-# @guest-exec:
-#
-# Execute a command in the guest
-#
-# @path: path or executable name to execute
-# @arg: #optional argument list to pass to executable
-# @env: #optional environment variables to pass to executable
-# @input-data: #optional data to be passed to process stdin (base64 encoded)
-# @capture-output: #optional bool flag to enable capture of
-# stdout/stderr of running process. defaults to false.
-#
-# Returns: PID on success.
-#
-# Since: 2.5
-##
-{ 'command': 'guest-exec',
- 'data': { 'path': 'str', '*arg': ['str'], '*env': ['str'],
- '*input-data': 'str', '*capture-output': 'bool' },
- 'returns': 'GuestExec' }
diff --git a/qemu/qga/service-win32.c b/qemu/qga/service-win32.c
deleted file mode 100644
index 72437587b..000000000
--- a/qemu/qga/service-win32.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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 "qemu/osdep.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 = fprintf(stderr, "%s. (Error: %d) %s", text, (int)err, message);
- LocalFree(message);
-
- return n;
-}
-
-/* Windows command line escaping. Based on
- * <http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx> and
- * <http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft%28v=vs.85%29.aspx>.
- *
- * The caller is responsible for initializing @buffer; prior contents are lost.
- */
-static const char *win_escape_arg(const char *to_escape, GString *buffer)
-{
- size_t backslash_count;
- const char *c;
-
- /* open with a double quote */
- g_string_assign(buffer, "\"");
-
- backslash_count = 0;
- for (c = to_escape; *c != '\0'; ++c) {
- switch (*c) {
- case '\\':
- /* The meaning depends on the first non-backslash character coming
- * up.
- */
- ++backslash_count;
- break;
-
- case '"':
- /* We must escape each pending backslash, then escape the double
- * quote. This creates a case of "odd number of backslashes [...]
- * followed by a double quotation mark".
- */
- while (backslash_count) {
- --backslash_count;
- g_string_append(buffer, "\\\\");
- }
- g_string_append(buffer, "\\\"");
- break;
-
- default:
- /* Any pending backslashes are without special meaning, flush them.
- * "Backslashes are interpreted literally, unless they immediately
- * precede a double quotation mark."
- */
- while (backslash_count) {
- --backslash_count;
- g_string_append_c(buffer, '\\');
- }
- g_string_append_c(buffer, *c);
- }
- }
-
- /* We're about to close with a double quote in string delimiter role.
- * Double all pending backslashes, creating a case of "even number of
- * backslashes [...] followed by a double quotation mark".
- */
- while (backslash_count) {
- --backslash_count;
- g_string_append(buffer, "\\\\");
- }
- g_string_append_c(buffer, '"');
-
- return buffer->str;
-}
-
-int ga_install_service(const char *path, const char *logfile,
- const char *state_dir)
-{
- int ret = EXIT_FAILURE;
- SC_HANDLE manager;
- SC_HANDLE service;
- TCHAR module_fname[MAX_PATH];
- GString *esc;
- GString *cmdline;
- SERVICE_DESCRIPTION desc = { (char *)QGA_SERVICE_DESCRIPTION };
-
- if (GetModuleFileName(NULL, module_fname, MAX_PATH) == 0) {
- printf_win_error("No full path to service's executable");
- return EXIT_FAILURE;
- }
-
- esc = g_string_new("");
- cmdline = g_string_new("");
-
- g_string_append_printf(cmdline, "%s -d",
- win_escape_arg(module_fname, esc));
-
- if (path) {
- g_string_append_printf(cmdline, " -p %s", win_escape_arg(path, esc));
- }
- if (logfile) {
- g_string_append_printf(cmdline, " -l %s -v",
- win_escape_arg(logfile, esc));
- }
- if (state_dir) {
- g_string_append_printf(cmdline, " -t %s",
- win_escape_arg(state_dir, esc));
- }
-
- g_debug("service's cmdline: %s", cmdline->str);
-
- manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if (manager == NULL) {
- printf_win_error("No handle to service control manager");
- goto out_strings;
- }
-
- service = CreateService(manager, QGA_SERVICE_NAME, QGA_SERVICE_DISPLAY_NAME,
- SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
- SERVICE_ERROR_NORMAL, cmdline->str, NULL, NULL, NULL, NULL, NULL);
- if (service == NULL) {
- printf_win_error("Failed to install service");
- goto out_manager;
- }
-
- ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &desc);
- fprintf(stderr, "Service was installed successfully.\n");
- ret = EXIT_SUCCESS;
- CloseServiceHandle(service);
-
-out_manager:
- CloseServiceHandle(manager);
-
-out_strings:
- g_string_free(cmdline, TRUE);
- g_string_free(esc, TRUE);
- return ret;
-}
-
-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 {
- fprintf(stderr, "Service was deleted successfully.\n");
- }
-
- CloseServiceHandle(service);
- CloseServiceHandle(manager);
-
- return EXIT_SUCCESS;
-}
diff --git a/qemu/qga/service-win32.h b/qemu/qga/service-win32.h
deleted file mode 100644
index 3b9e87024..000000000
--- a/qemu/qga/service-win32.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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,
- const char *state_dir);
-int ga_uninstall_service(void);
-
-#endif
diff --git a/qemu/qga/vss-win32.c b/qemu/qga/vss-win32.c
deleted file mode 100644
index 9a0e46356..000000000
--- a/qemu/qga/vss-win32.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * QEMU Guest Agent VSS utility functions
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 "qemu/osdep.h"
-#include <windows.h>
-#include "qga/guest-agent-core.h"
-#include "qga/vss-win32.h"
-#include "qga/vss-win32/requester.h"
-
-#define QGA_VSS_DLL "qga-vss.dll"
-
-static HMODULE provider_lib;
-
-/* Call a function in qga-vss.dll with the specified name */
-static HRESULT call_vss_provider_func(const char *func_name)
-{
- FARPROC WINAPI func;
-
- g_assert(provider_lib);
-
- func = GetProcAddress(provider_lib, func_name);
- if (!func) {
- char *msg;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char *)&msg, 0, NULL);
- fprintf(stderr, "failed to load %s from %s: %s",
- func_name, QGA_VSS_DLL, msg);
- LocalFree(msg);
- return E_FAIL;
- }
-
- return func();
-}
-
-/* Check whether this OS version supports VSS providers */
-static bool vss_check_os_version(void)
-{
- OSVERSIONINFO OSver;
-
- OSver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- GetVersionEx(&OSver);
- if ((OSver.dwMajorVersion == 5 && OSver.dwMinorVersion >= 2) ||
- OSver.dwMajorVersion > 5) {
- BOOL wow64 = false;
-#ifndef _WIN64
- /* Provider doesn't work under WOW64 (32bit agent on 64bit OS) */
- if (!IsWow64Process(GetCurrentProcess(), &wow64)) {
- fprintf(stderr, "failed to IsWow64Process (Error: %lx\n)\n",
- GetLastError());
- return false;
- }
- if (wow64) {
- fprintf(stderr, "Warning: Running under WOW64\n");
- }
-#endif
- return !wow64;
- }
- return false;
-}
-
-/* Load qga-vss.dll */
-bool vss_init(bool init_requester)
-{
- if (!vss_check_os_version()) {
- /* Do nothing if OS doesn't support providers. */
- fprintf(stderr, "VSS provider is not supported in this OS version: "
- "fsfreeze is disabled.\n");
- return false;
- }
-
- provider_lib = LoadLibraryA(QGA_VSS_DLL);
- if (!provider_lib) {
- char *msg;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char *)&msg, 0, NULL);
- fprintf(stderr, "failed to load %s: %sfsfreeze is disabled\n",
- QGA_VSS_DLL, msg);
- LocalFree(msg);
- return false;
- }
-
- if (init_requester) {
- HRESULT hr = call_vss_provider_func("requester_init");
- if (FAILED(hr)) {
- fprintf(stderr, "fsfreeze is disabled.\n");
- vss_deinit(false);
- return false;
- }
- }
-
- return true;
-}
-
-/* Unload qga-provider.dll */
-void vss_deinit(bool deinit_requester)
-{
- if (deinit_requester) {
- call_vss_provider_func("requester_deinit");
- }
- FreeLibrary(provider_lib);
- provider_lib = NULL;
-}
-
-bool vss_initialized(void)
-{
- return !!provider_lib;
-}
-
-int ga_install_vss_provider(void)
-{
- HRESULT hr;
-
- if (!vss_init(false)) {
- fprintf(stderr, "Installation of VSS provider is skipped. "
- "fsfreeze will be disabled.\n");
- return 0;
- }
- hr = call_vss_provider_func("COMRegister");
- vss_deinit(false);
-
- return SUCCEEDED(hr) ? 0 : EXIT_FAILURE;
-}
-
-void ga_uninstall_vss_provider(void)
-{
- if (!vss_init(false)) {
- fprintf(stderr, "Removal of VSS provider is skipped.\n");
- return;
- }
- call_vss_provider_func("COMUnregister");
- vss_deinit(false);
-}
-
-/* Call VSS requester and freeze/thaw filesystems and applications */
-void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze)
-{
- const char *func_name = freeze ? "requester_freeze" : "requester_thaw";
- QGAVSSRequesterFunc func;
- ErrorSet errset = {
- .error_setg_win32_wrapper = error_setg_win32_internal,
- .errp = errp,
- };
-
- g_assert(errp); /* requester.cpp requires it */
- func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name);
- if (!func) {
- error_setg_win32(errp, GetLastError(), "failed to load %s from %s",
- func_name, QGA_VSS_DLL);
- return;
- }
-
- func(nr_volume, &errset);
-}
diff --git a/qemu/qga/vss-win32.h b/qemu/qga/vss-win32.h
deleted file mode 100644
index 4d1d15081..000000000
--- a/qemu/qga/vss-win32.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * QEMU Guest Agent VSS utility declarations
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 VSS_WIN32_H
-#define VSS_WIN32_H
-
-
-bool vss_init(bool init_requester);
-void vss_deinit(bool deinit_requester);
-bool vss_initialized(void);
-
-int ga_install_vss_provider(void);
-void ga_uninstall_vss_provider(void);
-
-void qga_vss_fsfreeze(int *nr_volume, Error **errp, bool freeze);
-
-#endif
diff --git a/qemu/qga/vss-win32/Makefile.objs b/qemu/qga/vss-win32/Makefile.objs
deleted file mode 100644
index 7c96c6b28..000000000
--- a/qemu/qga/vss-win32/Makefile.objs
+++ /dev/null
@@ -1,23 +0,0 @@
-# rules to build qga-vss.dll
-
-qga-vss-dll-obj-y += requester.o provider.o install.o
-
-obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y))
-$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all -fstack-protector-strong, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor
-
-$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static
-$(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def
- $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS)," LINK $(TARGET_DIR)$@")
-
-
-# rules to build qga-provider.tlb
-# Currently, only native build is supported because building .tlb
-# (TypeLibrary) from .idl requires WindowsSDK and MIDL (and cl.exe in VC++).
-MIDL=$(WIN_SDK)/Bin/midl
-
-$(obj)/qga-vss.tlb: $(SRC_PATH)/$(obj)/qga-vss.idl
-ifeq ($(WIN_SDK),"")
- $(call quiet-command,cp $(dir $<)qga-vss.tlb $@, " COPY $(TARGET_DIR)$@")
-else
- $(call quiet-command,$(MIDL) -tlb $@ -I $(WIN_SDK)/Include $<," MIDL $(TARGET_DIR)$@")
-endif
diff --git a/qemu/qga/vss-win32/install.cpp b/qemu/qga/vss-win32/install.cpp
deleted file mode 100644
index cd9cdb4a2..000000000
--- a/qemu/qga/vss-win32/install.cpp
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * QEMU Guest Agent win32 VSS Provider installer
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 "qemu/osdep.h"
-
-#include "vss-common.h"
-#include "inc/win2003/vscoordint.h"
-
-#include <comadmin.h>
-#include <wbemidl.h>
-#include <comdef.h>
-#include <comutil.h>
-
-extern HINSTANCE g_hinstDll;
-
-const GUID CLSID_COMAdminCatalog = { 0xF618C514, 0xDFB8, 0x11d1,
- {0xA2, 0xCF, 0x00, 0x80, 0x5F, 0xC7, 0x92, 0x35} };
-const GUID IID_ICOMAdminCatalog2 = { 0x790C6E0B, 0x9194, 0x4cc9,
- {0x94, 0x26, 0xA4, 0x8A, 0x63, 0x18, 0x56, 0x96} };
-const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0,
- {0x89, 0x1f, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
-const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf,
- {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} };
-
-void errmsg(DWORD err, const char *text)
-{
- /*
- * `text' contains function call statement when errmsg is called via chk().
- * To make error message more readable, we cut off the text after '('.
- * If text doesn't contains '(', negative precision is given, which is
- * treated as though it were missing.
- */
- char *msg = NULL, *nul = strchr(text, '(');
- int len = nul ? nul - text : -1;
-
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char *)&msg, 0, NULL);
- fprintf(stderr, "%.*s. (Error: %lx) %s\n", len, text, err, msg);
- LocalFree(msg);
-}
-
-static void errmsg_dialog(DWORD err, const char *text, const char *opt = "")
-{
- char *msg, buf[512];
-
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char *)&msg, 0, NULL);
- snprintf(buf, sizeof(buf), "%s%s. (Error: %lx) %s", text, opt, err, msg);
- MessageBox(NULL, buf, "Error from " QGA_PROVIDER_NAME, MB_OK|MB_ICONERROR);
- LocalFree(msg);
-}
-
-#define _chk(hr, status, msg, err_label) \
- do { \
- hr = (status); \
- if (FAILED(hr)) { \
- errmsg(hr, msg); \
- goto err_label; \
- } \
- } while (0)
-
-#define chk(status) _chk(hr, status, "Failed to " #status, out)
-
-#if !defined(__MINGW64_VERSION_MAJOR) || !defined(__MINGW64_VERSION_MINOR) || \
- __MINGW64_VERSION_MAJOR * 100 + __MINGW64_VERSION_MINOR < 301
-void __stdcall _com_issue_error(HRESULT hr)
-{
- errmsg(hr, "Unexpected error in COM");
-}
-#endif
-
-template<class T>
-HRESULT put_Value(ICatalogObject *pObj, LPCWSTR name, T val)
-{
- return pObj->put_Value(_bstr_t(name), _variant_t(val));
-}
-
-/* Lookup Administrators group name from winmgmt */
-static HRESULT GetAdminName(_bstr_t *name)
-{
- HRESULT hr;
- COMPointer<IWbemLocator> pLoc;
- COMPointer<IWbemServices> pSvc;
- COMPointer<IEnumWbemClassObject> pEnum;
- COMPointer<IWbemClassObject> pWobj;
- ULONG returned;
- _variant_t var;
-
- chk(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
- IID_IWbemLocator, (LPVOID *)pLoc.replace()));
- chk(pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL,
- 0, 0, 0, pSvc.replace()));
- chk(CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
- NULL, RPC_C_AUTHN_LEVEL_CALL,
- RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE));
- chk(pSvc->ExecQuery(_bstr_t(L"WQL"),
- _bstr_t(L"select * from Win32_Account where "
- "SID='S-1-5-32-544' and localAccount=TRUE"),
- WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
- NULL, pEnum.replace()));
- if (!pEnum) {
- hr = E_FAIL;
- errmsg(hr, "Failed to query for Administrators");
- goto out;
- }
- chk(pEnum->Next(WBEM_INFINITE, 1, pWobj.replace(), &returned));
- if (returned == 0) {
- hr = E_FAIL;
- errmsg(hr, "No Administrators found");
- goto out;
- }
-
- chk(pWobj->Get(_bstr_t(L"Name"), 0, &var, 0, 0));
- try {
- *name = var;
- } catch(...) {
- hr = E_FAIL;
- errmsg(hr, "Failed to get name of Administrators");
- goto out;
- }
-
-out:
- return hr;
-}
-
-/* Find and iterate QGA VSS provider in COM+ Application Catalog */
-static HRESULT QGAProviderFind(
- HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
-{
- HRESULT hr;
- COMInitializer initializer;
- COMPointer<IUnknown> pUnknown;
- COMPointer<ICOMAdminCatalog2> pCatalog;
- COMPointer<ICatalogCollection> pColl;
- COMPointer<ICatalogObject> pObj;
- _variant_t var;
- long i, n;
-
- chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
- IID_IUnknown, (void **)pUnknown.replace()));
- chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
- (void **)pCatalog.replace()));
- chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
- (IDispatch **)pColl.replace()));
- chk(pColl->Populate());
-
- chk(pColl->get_Count(&n));
- for (i = n - 1; i >= 0; i--) {
- chk(pColl->get_Item(i, (IDispatch **)pObj.replace()));
- chk(pObj->get_Value(_bstr_t(L"Name"), &var));
- if (var == _variant_t(QGA_PROVIDER_LNAME)) {
- if (FAILED(found(pColl, i, arg))) {
- goto out;
- }
- }
- }
- chk(pColl->SaveChanges(&n));
-
-out:
- return hr;
-}
-
-/* Count QGA VSS provider in COM+ Application Catalog */
-static HRESULT QGAProviderCount(ICatalogCollection *coll, int i, void *arg)
-{
- (*(int *)arg)++;
- return S_OK;
-}
-
-/* Remove QGA VSS provider from COM+ Application Catalog Collection */
-static HRESULT QGAProviderRemove(ICatalogCollection *coll, int i, void *arg)
-{
- HRESULT hr;
-
- fprintf(stderr, "Removing COM+ Application: %s\n", QGA_PROVIDER_NAME);
- chk(coll->Remove(i));
-out:
- return hr;
-}
-
-/* Unregister this module from COM+ Applications Catalog */
-STDAPI COMUnregister(void)
-{
- HRESULT hr;
-
- DllUnregisterServer();
- chk(QGAProviderFind(QGAProviderRemove, NULL));
-out:
- return hr;
-}
-
-/* Register this module to COM+ Applications Catalog */
-STDAPI COMRegister(void)
-{
- HRESULT hr;
- COMInitializer initializer;
- COMPointer<IUnknown> pUnknown;
- COMPointer<ICOMAdminCatalog2> pCatalog;
- COMPointer<ICatalogCollection> pApps, pRoles, pUsersInRole;
- COMPointer<ICatalogObject> pObj;
- long n;
- _bstr_t name;
- _variant_t key;
- CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
- bool unregisterOnFailure = false;
- int count = 0;
-
- if (!g_hinstDll) {
- errmsg(E_FAIL, "Failed to initialize DLL");
- return E_FAIL;
- }
-
- chk(QGAProviderFind(QGAProviderCount, (void *)&count));
- if (count) {
- errmsg(E_ABORT, "QGA VSS Provider is already installed");
- return E_ABORT;
- }
-
- chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
- IID_IUnknown, (void **)pUnknown.replace()));
- chk(pUnknown->QueryInterface(IID_ICOMAdminCatalog2,
- (void **)pCatalog.replace()));
-
- /* Install COM+ Component */
-
- chk(pCatalog->GetCollection(_bstr_t(L"Applications"),
- (IDispatch **)pApps.replace()));
- chk(pApps->Populate());
- chk(pApps->Add((IDispatch **)&pObj));
- chk(put_Value(pObj, L"Name", QGA_PROVIDER_LNAME));
- chk(put_Value(pObj, L"Description", QGA_PROVIDER_LNAME));
- chk(put_Value(pObj, L"ApplicationAccessChecksEnabled", true));
- chk(put_Value(pObj, L"Authentication", short(6)));
- chk(put_Value(pObj, L"AuthenticationCapability", short(2)));
- chk(put_Value(pObj, L"ImpersonationLevel", short(2)));
- chk(pApps->SaveChanges(&n));
-
- /* The app should be deleted if something fails after SaveChanges */
- unregisterOnFailure = true;
-
- chk(pObj->get_Key(&key));
-
- if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
- hr = HRESULT_FROM_WIN32(GetLastError());
- errmsg(hr, "GetModuleFileName failed");
- goto out;
- }
- n = strlen(dllPath);
- if (n < 3) {
- hr = E_FAIL;
- errmsg(hr, "Failed to lookup dll");
- goto out;
- }
- strcpy(tlbPath, dllPath);
- strcpy(tlbPath+n-3, "tlb");
- fprintf(stderr, "Registering " QGA_PROVIDER_NAME ":\n");
- fprintf(stderr, " %s\n", dllPath);
- fprintf(stderr, " %s\n", tlbPath);
- if (!PathFileExists(tlbPath)) {
- hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
- errmsg(hr, "Failed to lookup tlb");
- goto out;
- }
-
- chk(pCatalog->CreateServiceForApplication(
- _bstr_t(QGA_PROVIDER_LNAME), _bstr_t(QGA_PROVIDER_LNAME),
- _bstr_t(L"SERVICE_AUTO_START"), _bstr_t(L"SERVICE_ERROR_NORMAL"),
- _bstr_t(L""), _bstr_t(L".\\localsystem"), _bstr_t(L""), FALSE));
- chk(pCatalog->InstallComponent(_bstr_t(QGA_PROVIDER_LNAME),
- _bstr_t(dllPath), _bstr_t(tlbPath),
- _bstr_t("")));
-
- /* Setup roles of the applicaion */
-
- chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
- (IDispatch **)pRoles.replace()));
- chk(pRoles->Populate());
- chk(pRoles->Add((IDispatch **)pObj.replace()));
- chk(put_Value(pObj, L"Name", L"Administrators"));
- chk(put_Value(pObj, L"Description", L"Administrators group"));
- chk(pRoles->SaveChanges(&n));
- chk(pObj->get_Key(&key));
-
- /* Setup users in the role */
-
- chk(pRoles->GetCollection(_bstr_t(L"UsersInRole"), key,
- (IDispatch **)pUsersInRole.replace()));
- chk(pUsersInRole->Populate());
-
- chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
- chk(GetAdminName(&name));
- chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
-
- chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
- chk(put_Value(pObj, L"User", L"SYSTEM"));
- chk(pUsersInRole->SaveChanges(&n));
-
-out:
- if (unregisterOnFailure && FAILED(hr)) {
- COMUnregister();
- }
-
- return hr;
-}
-
-
-static BOOL CreateRegistryKey(LPCTSTR key, LPCTSTR value, LPCTSTR data)
-{
- HKEY hKey;
- LONG ret;
- DWORD size;
-
- ret = RegCreateKeyEx(HKEY_CLASSES_ROOT, key, 0, NULL,
- REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
- if (ret != ERROR_SUCCESS) {
- goto out;
- }
-
- if (data != NULL) {
- size = strlen(data) + 1;
- } else {
- size = 0;
- }
-
- ret = RegSetValueEx(hKey, value, 0, REG_SZ, (LPBYTE)data, size);
- RegCloseKey(hKey);
-
-out:
- if (ret != ERROR_SUCCESS) {
- /* As we cannot printf within DllRegisterServer(), show a dialog. */
- errmsg_dialog(ret, "Cannot add registry", key);
- return FALSE;
- }
- return TRUE;
-}
-
-/* Register this dll as a VSS provider */
-STDAPI DllRegisterServer(void)
-{
- COMInitializer initializer;
- COMPointer<IVssAdmin> pVssAdmin;
- HRESULT hr = E_FAIL;
- char dllPath[MAX_PATH];
- char key[256];
-
- if (!g_hinstDll) {
- errmsg_dialog(hr, "Module instance is not available");
- goto out;
- }
-
- /* Add this module to registery */
-
- sprintf(key, "CLSID\\%s", g_szClsid);
- if (!CreateRegistryKey(key, NULL, g_szClsid)) {
- goto out;
- }
-
- if (!GetModuleFileName(g_hinstDll, dllPath, sizeof(dllPath))) {
- errmsg_dialog(GetLastError(), "GetModuleFileName failed");
- goto out;
- }
-
- sprintf(key, "CLSID\\%s\\InprocServer32", g_szClsid);
- if (!CreateRegistryKey(key, NULL, dllPath)) {
- goto out;
- }
-
- if (!CreateRegistryKey(key, "ThreadingModel", "Apartment")) {
- goto out;
- }
-
- sprintf(key, "CLSID\\%s\\ProgID", g_szClsid);
- if (!CreateRegistryKey(key, NULL, g_szProgid)) {
- goto out;
- }
-
- if (!CreateRegistryKey(g_szProgid, NULL, QGA_PROVIDER_NAME)) {
- goto out;
- }
-
- sprintf(key, "%s\\CLSID", g_szProgid);
- if (!CreateRegistryKey(key, NULL, g_szClsid)) {
- goto out;
- }
-
- hr = CoCreateInstance(CLSID_VSSCoordinator, NULL, CLSCTX_ALL,
- IID_IVssAdmin, (void **)pVssAdmin.replace());
- if (FAILED(hr)) {
- errmsg_dialog(hr, "CoCreateInstance(VSSCoordinator) failed");
- goto out;
- }
-
- hr = pVssAdmin->RegisterProvider(g_gProviderId, CLSID_QGAVSSProvider,
- const_cast<WCHAR*>(QGA_PROVIDER_LNAME),
- VSS_PROV_SOFTWARE,
- const_cast<WCHAR*>(QGA_PROVIDER_VERSION),
- g_gProviderVersion);
- if (FAILED(hr)) {
- errmsg_dialog(hr, "RegisterProvider failed");
- }
-
-out:
- if (FAILED(hr)) {
- DllUnregisterServer();
- }
-
- return hr;
-}
-
-/* Unregister this VSS hardware provider from the system */
-STDAPI DllUnregisterServer(void)
-{
- TCHAR key[256];
- COMInitializer initializer;
- COMPointer<IVssAdmin> pVssAdmin;
-
- HRESULT hr = CoCreateInstance(CLSID_VSSCoordinator,
- NULL, CLSCTX_ALL, IID_IVssAdmin,
- (void **)pVssAdmin.replace());
- if (SUCCEEDED(hr)) {
- hr = pVssAdmin->UnregisterProvider(g_gProviderId);
- } else {
- errmsg(hr, "CoCreateInstance(VSSCoordinator) failed");
- }
-
- sprintf(key, "CLSID\\%s", g_szClsid);
- SHDeleteKey(HKEY_CLASSES_ROOT, key);
- SHDeleteKey(HKEY_CLASSES_ROOT, g_szProgid);
-
- return S_OK; /* Uninstall should never fail */
-}
-
-
-/* Support function to convert ASCII string into BSTR (used in _bstr_t) */
-namespace _com_util
-{
- BSTR WINAPI ConvertStringToBSTR(const char *ascii) {
- int len = strlen(ascii);
- BSTR bstr = SysAllocStringLen(NULL, len);
-
- if (!bstr) {
- return NULL;
- }
-
- if (mbstowcs(bstr, ascii, len) == (size_t)-1) {
- fprintf(stderr, "Failed to convert string '%s' into BSTR", ascii);
- bstr[0] = 0;
- }
- return bstr;
- }
-}
diff --git a/qemu/qga/vss-win32/provider.cpp b/qemu/qga/vss-win32/provider.cpp
deleted file mode 100644
index d977393e3..000000000
--- a/qemu/qga/vss-win32/provider.cpp
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * QEMU Guest Agent win32 VSS Provider implementations
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 "qemu/osdep.h"
-#include "vss-common.h"
-#include "inc/win2003/vscoordint.h"
-#include "inc/win2003/vsprov.h"
-
-#define VSS_TIMEOUT_MSEC (60*1000)
-
-static long g_nComObjsInUse;
-HINSTANCE g_hinstDll;
-
-/* VSS common GUID's */
-
-const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4,
- {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} };
-const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3,
- {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
-
-const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344,
- {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} };
-const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3,
- {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} };
-const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778,
- {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} };
-const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe,
- {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} };
-
-const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3,
- {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
-
-
-void LockModule(BOOL lock)
-{
- if (lock) {
- InterlockedIncrement(&g_nComObjsInUse);
- } else {
- InterlockedDecrement(&g_nComObjsInUse);
- }
-}
-
-/* Empty enumerator for VssObject */
-
-class CQGAVSSEnumObject : public IVssEnumObject
-{
-public:
- STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- /* IVssEnumObject Methods */
- STDMETHODIMP Next(
- ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched);
- STDMETHODIMP Skip(ULONG celt);
- STDMETHODIMP Reset(void);
- STDMETHODIMP Clone(IVssEnumObject **ppenum);
-
- /* CQGAVSSEnumObject Methods */
- CQGAVSSEnumObject();
- ~CQGAVSSEnumObject();
-
-private:
- long m_nRefCount;
-};
-
-CQGAVSSEnumObject::CQGAVSSEnumObject()
-{
- m_nRefCount = 0;
- LockModule(TRUE);
-}
-
-CQGAVSSEnumObject::~CQGAVSSEnumObject()
-{
- LockModule(FALSE);
-}
-
-STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj)
-{
- if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
- *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this));
- AddRef();
- return S_OK;
- }
- *ppObj = NULL;
- return E_NOINTERFACE;
-}
-
-STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
-{
- return InterlockedIncrement(&m_nRefCount);
-}
-
-STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
-{
- long nRefCount = InterlockedDecrement(&m_nRefCount);
- if (m_nRefCount == 0) {
- delete this;
- }
- return nRefCount;
-}
-
-STDMETHODIMP CQGAVSSEnumObject::Next(
- ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
-{
- *pceltFetched = 0;
- return S_FALSE;
-}
-
-STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
-{
- return S_FALSE;
-}
-
-STDMETHODIMP CQGAVSSEnumObject::Reset(void)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
-{
- return E_NOTIMPL;
-}
-
-
-/* QGAVssProvider */
-
-class CQGAVssProvider :
- public IVssSoftwareSnapshotProvider,
- public IVssProviderCreateSnapshotSet,
- public IVssProviderNotifications
-{
-public:
- STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
-
- /* IVssSoftwareSnapshotProvider Methods */
- STDMETHODIMP SetContext(LONG lContext);
- STDMETHODIMP GetSnapshotProperties(
- VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp);
- STDMETHODIMP Query(
- VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
- VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum);
- STDMETHODIMP DeleteSnapshots(
- VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
- BOOL bForceDelete, LONG *plDeletedSnapshots,
- VSS_ID *pNondeletedSnapshotID);
- STDMETHODIMP BeginPrepareSnapshot(
- VSS_ID SnapshotSetId, VSS_ID SnapshotId,
- VSS_PWSZ pwszVolumeName, LONG lNewContext);
- STDMETHODIMP IsVolumeSupported(
- VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider);
- STDMETHODIMP IsVolumeSnapshotted(
- VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent,
- LONG *plSnapshotCompatibility);
- STDMETHODIMP SetSnapshotProperty(
- VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId,
- VARIANT vProperty);
- STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId);
- STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync);
-
- /* IVssProviderCreateSnapshotSet Methods */
- STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId);
- STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId);
- STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId);
- STDMETHODIMP PostCommitSnapshots(
- VSS_ID SnapshotSetId, LONG lSnapshotsCount);
- STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId);
- STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId);
- STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId);
-
- /* IVssProviderNotifications Methods */
- STDMETHODIMP OnLoad(IUnknown *pCallback);
- STDMETHODIMP OnUnload(BOOL bForceUnload);
-
- /* CQGAVssProvider Methods */
- CQGAVssProvider();
- ~CQGAVssProvider();
-
-private:
- long m_nRefCount;
-};
-
-CQGAVssProvider::CQGAVssProvider()
-{
- m_nRefCount = 0;
- LockModule(TRUE);
-}
-
-CQGAVssProvider::~CQGAVssProvider()
-{
- LockModule(FALSE);
-}
-
-STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj)
-{
- if (riid == IID_IUnknown) {
- *ppObj = static_cast<void*>(this);
- AddRef();
- return S_OK;
- }
- if (riid == IID_IVssSoftwareSnapshotProvider) {
- *ppObj = static_cast<void*>(
- static_cast<IVssSoftwareSnapshotProvider*>(this));
- AddRef();
- return S_OK;
- }
- if (riid == IID_IVssProviderCreateSnapshotSet) {
- *ppObj = static_cast<void*>(
- static_cast<IVssProviderCreateSnapshotSet*>(this));
- AddRef();
- return S_OK;
- }
- if (riid == IID_IVssProviderNotifications) {
- *ppObj = static_cast<void*>(
- static_cast<IVssProviderNotifications*>(this));
- AddRef();
- return S_OK;
- }
- *ppObj = NULL;
- return E_NOINTERFACE;
-}
-
-STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
-{
- return InterlockedIncrement(&m_nRefCount);
-}
-
-STDMETHODIMP_(ULONG) CQGAVssProvider::Release()
-{
- long nRefCount = InterlockedDecrement(&m_nRefCount);
- if (m_nRefCount == 0) {
- delete this;
- }
- return nRefCount;
-}
-
-
-/*
- * IVssSoftwareSnapshotProvider methods
- */
-
-STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
- VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
-{
- return VSS_E_OBJECT_NOT_FOUND;
-}
-
-STDMETHODIMP CQGAVssProvider::Query(
- VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
- VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum)
-{
- try {
- *ppEnum = new CQGAVSSEnumObject;
- } catch (...) {
- return E_OUTOFMEMORY;
- }
- (*ppEnum)->AddRef();
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
- VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
- BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
-{
- *plDeletedSnapshots = 0;
- *pNondeletedSnapshotID = SourceObjectId;
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
- VSS_ID SnapshotSetId, VSS_ID SnapshotId,
- VSS_PWSZ pwszVolumeName, LONG lNewContext)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
- VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
-{
- HANDLE hEventFrozen;
-
- /* Check if a requester is qemu-ga by whether an event is created */
- hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
- if (!hEventFrozen) {
- *pbSupportedByThisProvider = FALSE;
- return S_OK;
- }
- CloseHandle(hEventFrozen);
-
- *pbSupportedByThisProvider = TRUE;
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,
- BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility)
-{
- *pbSnapshotsPresent = FALSE;
- *plSnapshotCompatibility = 0;
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
- VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
-{
- return E_NOTIMPL;
-}
-
-STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
- VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
-{
- return E_NOTIMPL;
-}
-
-
-/*
- * IVssProviderCreateSnapshotSet methods
- */
-
-STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
-{
- HRESULT hr = S_OK;
- HANDLE hEventFrozen, hEventThaw, hEventTimeout;
-
- hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
- if (!hEventFrozen) {
- return E_FAIL;
- }
-
- hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
- if (!hEventThaw) {
- CloseHandle(hEventFrozen);
- return E_FAIL;
- }
-
- hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
- if (!hEventTimeout) {
- CloseHandle(hEventFrozen);
- CloseHandle(hEventThaw);
- return E_FAIL;
- }
-
- /* Send event to qemu-ga to notify filesystem is frozen */
- SetEvent(hEventFrozen);
-
- /* Wait until the snapshot is taken by the host. */
- if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) {
- /* Send event to qemu-ga to notify the provider is timed out */
- SetEvent(hEventTimeout);
- hr = E_ABORT;
- }
-
- CloseHandle(hEventThaw);
- CloseHandle(hEventFrozen);
- CloseHandle(hEventTimeout);
- return hr;
-}
-
-STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
- VSS_ID SnapshotSetId, LONG lSnapshotsCount)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
-{
- return S_OK;
-}
-
-/*
- * IVssProviderNotifications methods
- */
-
-STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
-{
- return S_OK;
-}
-
-STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
-{
- return S_OK;
-}
-
-
-/*
- * CQGAVssProviderFactory class
- */
-
-class CQGAVssProviderFactory : public IClassFactory
-{
-public:
- STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
- STDMETHODIMP CreateInstance(
- IUnknown *pUnknownOuter, REFIID iid, void **ppv);
- STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; }
-
- CQGAVssProviderFactory();
- ~CQGAVssProviderFactory();
-
-private:
- long m_nRefCount;
-};
-
-CQGAVssProviderFactory::CQGAVssProviderFactory()
-{
- m_nRefCount = 0;
- LockModule(TRUE);
-}
-
-CQGAVssProviderFactory::~CQGAVssProviderFactory()
-{
- LockModule(FALSE);
-}
-
-STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)
-{
- if (riid == IID_IUnknown || riid == IID_IClassFactory) {
- *ppv = static_cast<void*>(this);
- AddRef();
- return S_OK;
- }
- *ppv = NULL;
- return E_NOINTERFACE;
-}
-
-STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
-{
- return InterlockedIncrement(&m_nRefCount);
-}
-
-STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
-{
- long nRefCount = InterlockedDecrement(&m_nRefCount);
- if (m_nRefCount == 0) {
- delete this;
- }
- return nRefCount;
-}
-
-STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
- IUnknown *pUnknownOuter, REFIID iid, void **ppv)
-{
- CQGAVssProvider *pObj;
-
- if (pUnknownOuter) {
- return CLASS_E_NOAGGREGATION;
- }
- try {
- pObj = new CQGAVssProvider;
- } catch (...) {
- return E_OUTOFMEMORY;
- }
- HRESULT hr = pObj->QueryInterface(iid, ppv);
- if (FAILED(hr)) {
- delete pObj;
- }
- return hr;
-}
-
-
-/*
- * DLL functions
- */
-
-STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
-{
- CQGAVssProviderFactory *factory;
- try {
- factory = new CQGAVssProviderFactory;
- } catch (...) {
- return E_OUTOFMEMORY;
- }
- factory->AddRef();
- HRESULT hr = factory->QueryInterface(riid, ppv);
- factory->Release();
- return hr;
-}
-
-STDAPI DllCanUnloadNow()
-{
- return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
-}
-
-EXTERN_C
-BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
-{
- if (dwReason == DLL_PROCESS_ATTACH) {
- g_hinstDll = hinstDll;
- DisableThreadLibraryCalls(hinstDll);
- }
- return TRUE;
-}
diff --git a/qemu/qga/vss-win32/qga-vss.def b/qemu/qga/vss-win32/qga-vss.def
deleted file mode 100644
index 927782c31..000000000
--- a/qemu/qga/vss-win32/qga-vss.def
+++ /dev/null
@@ -1,13 +0,0 @@
-LIBRARY "QGA-PROVIDER.DLL"
-
-EXPORTS
- COMRegister PRIVATE
- COMUnregister PRIVATE
- DllCanUnloadNow PRIVATE
- DllGetClassObject PRIVATE
- DllRegisterServer PRIVATE
- DllUnregisterServer PRIVATE
- requester_init PRIVATE
- requester_deinit PRIVATE
- requester_freeze PRIVATE
- requester_thaw PRIVATE
diff --git a/qemu/qga/vss-win32/qga-vss.idl b/qemu/qga/vss-win32/qga-vss.idl
deleted file mode 100644
index 17abca0da..000000000
--- a/qemu/qga/vss-win32/qga-vss.idl
+++ /dev/null
@@ -1,20 +0,0 @@
-import "oaidl.idl";
-import "ocidl.idl";
-
-[
- uuid(103B8142-6CE5-48A7-BDE1-794D3192FCF1),
- version(1.0),
- helpstring("QGAVSSProvider Type Library")
-]
-library QGAVSSHWProviderLib
-{
- importlib("stdole2.tlb");
- [
- uuid(6E6A3492-8D4D-440C-9619-5E5D0CC31CA8),
- helpstring("QGAVSSProvider Class")
- ]
- coclass QGAVSSHWProvider
- {
- [default] interface IUnknown;
- };
-};
diff --git a/qemu/qga/vss-win32/qga-vss.tlb b/qemu/qga/vss-win32/qga-vss.tlb
deleted file mode 100644
index 226452a18..000000000
--- a/qemu/qga/vss-win32/qga-vss.tlb
+++ /dev/null
Binary files differ
diff --git a/qemu/qga/vss-win32/requester.cpp b/qemu/qga/vss-win32/requester.cpp
deleted file mode 100644
index 889052ded..000000000
--- a/qemu/qga/vss-win32/requester.cpp
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * QEMU Guest Agent win32 VSS Requester implementations
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 "qemu/osdep.h"
-#include "vss-common.h"
-#include "requester.h"
-#include "inc/win2003/vswriter.h"
-#include "inc/win2003/vsbackup.h"
-
-/* Max wait time for frozen event (VSS can only hold writes for 10 seconds) */
-#define VSS_TIMEOUT_FREEZE_MSEC 10000
-
-/* Call QueryStatus every 10 ms while waiting for frozen event */
-#define VSS_TIMEOUT_EVENT_MSEC 10
-
-#define err_set(e, err, fmt, ...) \
- ((e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \
- err, fmt, ## __VA_ARGS__))
-/* Bad idea, works only when (e)->errp != NULL: */
-#define err_is_set(e) ((e)->errp && *(e)->errp)
-/* To lift this restriction, error_propagate(), like we do in QEMU code */
-
-/* Handle to VSSAPI.DLL */
-static HMODULE hLib;
-
-/* Functions in VSSAPI.DLL */
-typedef HRESULT(STDAPICALLTYPE * t_CreateVssBackupComponents)(
- OUT IVssBackupComponents**);
-typedef void(APIENTRY * t_VssFreeSnapshotProperties)(IN VSS_SNAPSHOT_PROP*);
-static t_CreateVssBackupComponents pCreateVssBackupComponents;
-static t_VssFreeSnapshotProperties pVssFreeSnapshotProperties;
-
-/* Variables used while applications and filesystes are frozen by VSS */
-static struct QGAVSSContext {
- IVssBackupComponents *pVssbc; /* VSS requester interface */
- IVssAsync *pAsyncSnapshot; /* async info of VSS snapshot operation */
- HANDLE hEventFrozen; /* notify fs/writer freeze from provider */
- HANDLE hEventThaw; /* request provider to thaw */
- HANDLE hEventTimeout; /* notify timeout in provider */
- int cFrozenVols; /* number of frozen volumes */
-} vss_ctx;
-
-STDAPI requester_init(void)
-{
- COMInitializer initializer; /* to call CoInitializeSecurity */
- HRESULT hr = CoInitializeSecurity(
- NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
- RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, NULL);
- if (FAILED(hr)) {
- fprintf(stderr, "failed to CoInitializeSecurity (error %lx)\n", hr);
- return hr;
- }
-
- hLib = LoadLibraryA("VSSAPI.DLL");
- if (!hLib) {
- fprintf(stderr, "failed to load VSSAPI.DLL\n");
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- pCreateVssBackupComponents = (t_CreateVssBackupComponents)
- GetProcAddress(hLib,
-#ifdef _WIN64 /* 64bit environment */
- "?CreateVssBackupComponents@@YAJPEAPEAVIVssBackupComponents@@@Z"
-#else /* 32bit environment */
- "?CreateVssBackupComponents@@YGJPAPAVIVssBackupComponents@@@Z"
-#endif
- );
- if (!pCreateVssBackupComponents) {
- fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- pVssFreeSnapshotProperties = (t_VssFreeSnapshotProperties)
- GetProcAddress(hLib, "VssFreeSnapshotProperties");
- if (!pVssFreeSnapshotProperties) {
- fprintf(stderr, "failed to get proc address from VSSAPI.DLL\n");
- return HRESULT_FROM_WIN32(GetLastError());
- }
-
- return S_OK;
-}
-
-static void requester_cleanup(void)
-{
- if (vss_ctx.hEventFrozen) {
- CloseHandle(vss_ctx.hEventFrozen);
- vss_ctx.hEventFrozen = NULL;
- }
- if (vss_ctx.hEventThaw) {
- CloseHandle(vss_ctx.hEventThaw);
- vss_ctx.hEventThaw = NULL;
- }
- if (vss_ctx.hEventTimeout) {
- CloseHandle(vss_ctx.hEventTimeout);
- vss_ctx.hEventTimeout = NULL;
- }
- if (vss_ctx.pAsyncSnapshot) {
- vss_ctx.pAsyncSnapshot->Release();
- vss_ctx.pAsyncSnapshot = NULL;
- }
- if (vss_ctx.pVssbc) {
- vss_ctx.pVssbc->Release();
- vss_ctx.pVssbc = NULL;
- }
- vss_ctx.cFrozenVols = 0;
-}
-
-STDAPI requester_deinit(void)
-{
- requester_cleanup();
-
- pCreateVssBackupComponents = NULL;
- pVssFreeSnapshotProperties = NULL;
- if (hLib) {
- FreeLibrary(hLib);
- hLib = NULL;
- }
-
- return S_OK;
-}
-
-static HRESULT WaitForAsync(IVssAsync *pAsync)
-{
- HRESULT ret, hr;
-
- do {
- hr = pAsync->Wait();
- if (FAILED(hr)) {
- ret = hr;
- break;
- }
- hr = pAsync->QueryStatus(&ret, NULL);
- if (FAILED(hr)) {
- ret = hr;
- break;
- }
- } while (ret == VSS_S_ASYNC_PENDING);
-
- return ret;
-}
-
-static void AddComponents(ErrorSet *errset)
-{
- unsigned int cWriters, i;
- VSS_ID id, idInstance, idWriter;
- BSTR bstrWriterName = NULL;
- VSS_USAGE_TYPE usage;
- VSS_SOURCE_TYPE source;
- unsigned int cComponents, c1, c2, j;
- COMPointer<IVssExamineWriterMetadata> pMetadata;
- COMPointer<IVssWMComponent> pComponent;
- PVSSCOMPONENTINFO info;
- HRESULT hr;
-
- hr = vss_ctx.pVssbc->GetWriterMetadataCount(&cWriters);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to get writer metadata count");
- goto out;
- }
-
- for (i = 0; i < cWriters; i++) {
- hr = vss_ctx.pVssbc->GetWriterMetadata(i, &id, pMetadata.replace());
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to get writer metadata of %d/%d",
- i, cWriters);
- goto out;
- }
-
- hr = pMetadata->GetIdentity(&idInstance, &idWriter,
- &bstrWriterName, &usage, &source);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to get identity of writer %d/%d",
- i, cWriters);
- goto out;
- }
-
- hr = pMetadata->GetFileCounts(&c1, &c2, &cComponents);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to get file counts of %S",
- bstrWriterName);
- goto out;
- }
-
- for (j = 0; j < cComponents; j++) {
- hr = pMetadata->GetComponent(j, pComponent.replace());
- if (FAILED(hr)) {
- err_set(errset, hr,
- "failed to get component %d/%d of %S",
- j, cComponents, bstrWriterName);
- goto out;
- }
-
- hr = pComponent->GetComponentInfo(&info);
- if (FAILED(hr)) {
- err_set(errset, hr,
- "failed to get component info %d/%d of %S",
- j, cComponents, bstrWriterName);
- goto out;
- }
-
- if (info->bSelectable) {
- hr = vss_ctx.pVssbc->AddComponent(idInstance, idWriter,
- info->type,
- info->bstrLogicalPath,
- info->bstrComponentName);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to add component %S(%S)",
- info->bstrComponentName, bstrWriterName);
- goto out;
- }
- }
- SysFreeString(bstrWriterName);
- bstrWriterName = NULL;
- pComponent->FreeComponentInfo(info);
- info = NULL;
- }
- }
-out:
- if (bstrWriterName) {
- SysFreeString(bstrWriterName);
- }
- if (pComponent && info) {
- pComponent->FreeComponentInfo(info);
- }
-}
-
-void requester_freeze(int *num_vols, ErrorSet *errset)
-{
- COMPointer<IVssAsync> pAsync;
- HANDLE volume;
- HRESULT hr;
- LONG ctx;
- GUID guidSnapshotSet = GUID_NULL;
- SECURITY_DESCRIPTOR sd;
- SECURITY_ATTRIBUTES sa;
- WCHAR short_volume_name[64], *display_name = short_volume_name;
- DWORD wait_status;
- int num_fixed_drives = 0, i;
-
- if (vss_ctx.pVssbc) { /* already frozen */
- *num_vols = 0;
- return;
- }
-
- CoInitialize(NULL);
-
- /* Allow unrestricted access to events */
- InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
- sa.nLength = sizeof(sa);
- sa.lpSecurityDescriptor = &sd;
- sa.bInheritHandle = FALSE;
-
- vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN);
- if (!vss_ctx.hEventFrozen) {
- err_set(errset, GetLastError(), "failed to create event %s",
- EVENT_NAME_FROZEN);
- goto out;
- }
- vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW);
- if (!vss_ctx.hEventThaw) {
- err_set(errset, GetLastError(), "failed to create event %s",
- EVENT_NAME_THAW);
- goto out;
- }
- vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT);
- if (!vss_ctx.hEventTimeout) {
- err_set(errset, GetLastError(), "failed to create event %s",
- EVENT_NAME_TIMEOUT);
- goto out;
- }
-
- assert(pCreateVssBackupComponents != NULL);
- hr = pCreateVssBackupComponents(&vss_ctx.pVssbc);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to create VSS backup components");
- goto out;
- }
-
- hr = vss_ctx.pVssbc->InitializeForBackup();
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to initialize for backup");
- goto out;
- }
-
- hr = vss_ctx.pVssbc->SetBackupState(true, true, VSS_BT_FULL, false);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to set backup state");
- goto out;
- }
-
- /*
- * Currently writable snapshots are not supported.
- * To prevent the final commit (which requires to write to snapshots),
- * ATTR_NO_AUTORECOVERY and ATTR_TRANSPORTABLE are specified here.
- */
- ctx = VSS_CTX_APP_ROLLBACK | VSS_VOLSNAP_ATTR_TRANSPORTABLE |
- VSS_VOLSNAP_ATTR_NO_AUTORECOVERY | VSS_VOLSNAP_ATTR_TXF_RECOVERY;
- hr = vss_ctx.pVssbc->SetContext(ctx);
- if (hr == (HRESULT)VSS_E_UNSUPPORTED_CONTEXT) {
- /* Non-server version of Windows doesn't support ATTR_TRANSPORTABLE */
- ctx &= ~VSS_VOLSNAP_ATTR_TRANSPORTABLE;
- hr = vss_ctx.pVssbc->SetContext(ctx);
- }
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to set backup context");
- goto out;
- }
-
- hr = vss_ctx.pVssbc->GatherWriterMetadata(pAsync.replace());
- if (SUCCEEDED(hr)) {
- hr = WaitForAsync(pAsync);
- }
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to gather writer metadata");
- goto out;
- }
-
- AddComponents(errset);
- if (err_is_set(errset)) {
- goto out;
- }
-
- hr = vss_ctx.pVssbc->StartSnapshotSet(&guidSnapshotSet);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to start snapshot set");
- goto out;
- }
-
- volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name));
- if (volume == INVALID_HANDLE_VALUE) {
- err_set(errset, hr, "failed to find first volume");
- goto out;
- }
- for (;;) {
- if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) {
- VSS_ID pid;
- hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
- g_gProviderId, &pid);
- if (FAILED(hr)) {
- WCHAR volume_path_name[PATH_MAX];
- if (GetVolumePathNamesForVolumeNameW(
- short_volume_name, volume_path_name,
- sizeof(volume_path_name), NULL) && *volume_path_name) {
- display_name = volume_path_name;
- }
- err_set(errset, hr, "failed to add %S to snapshot set",
- display_name);
- FindVolumeClose(volume);
- goto out;
- }
- num_fixed_drives++;
- }
- if (!FindNextVolumeW(volume, short_volume_name,
- sizeof(short_volume_name))) {
- FindVolumeClose(volume);
- break;
- }
- }
-
- if (num_fixed_drives == 0) {
- goto out; /* If there is no fixed drive, just exit. */
- }
-
- hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace());
- if (SUCCEEDED(hr)) {
- hr = WaitForAsync(pAsync);
- }
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to prepare for backup");
- goto out;
- }
-
- hr = vss_ctx.pVssbc->GatherWriterStatus(pAsync.replace());
- if (SUCCEEDED(hr)) {
- hr = WaitForAsync(pAsync);
- }
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to gather writer status");
- goto out;
- }
-
- /*
- * Start VSS quiescing operations.
- * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen
- * after the applications and filesystems are frozen.
- */
- hr = vss_ctx.pVssbc->DoSnapshotSet(&vss_ctx.pAsyncSnapshot);
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to do snapshot set");
- goto out;
- }
-
- /* Need to call QueryStatus several times to make VSS provider progress */
- for (i = 0; i < VSS_TIMEOUT_FREEZE_MSEC/VSS_TIMEOUT_EVENT_MSEC; i++) {
- HRESULT hr2 = vss_ctx.pAsyncSnapshot->QueryStatus(&hr, NULL);
- if (FAILED(hr2)) {
- err_set(errset, hr, "failed to do snapshot set");
- goto out;
- }
- if (hr != VSS_S_ASYNC_PENDING) {
- err_set(errset, E_FAIL,
- "DoSnapshotSet exited without Frozen event");
- goto out;
- }
- wait_status = WaitForSingleObject(vss_ctx.hEventFrozen,
- VSS_TIMEOUT_EVENT_MSEC);
- if (wait_status != WAIT_TIMEOUT) {
- break;
- }
- }
- if (wait_status != WAIT_OBJECT_0) {
- err_set(errset, E_FAIL,
- "couldn't receive Frozen event from VSS provider");
- goto out;
- }
-
- *num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
- return;
-
-out:
- if (vss_ctx.pVssbc) {
- vss_ctx.pVssbc->AbortBackup();
- }
- requester_cleanup();
- CoUninitialize();
-}
-
-
-void requester_thaw(int *num_vols, ErrorSet *errset)
-{
- COMPointer<IVssAsync> pAsync;
-
- if (!vss_ctx.hEventThaw) {
- /*
- * In this case, DoSnapshotSet is aborted or not started,
- * and no volumes must be frozen. We return without an error.
- */
- *num_vols = 0;
- return;
- }
-
- /* Tell the provider that the snapshot is finished. */
- SetEvent(vss_ctx.hEventThaw);
-
- assert(vss_ctx.pVssbc);
- assert(vss_ctx.pAsyncSnapshot);
-
- HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot);
- switch (hr) {
- case VSS_S_ASYNC_FINISHED:
- hr = vss_ctx.pVssbc->BackupComplete(pAsync.replace());
- if (SUCCEEDED(hr)) {
- hr = WaitForAsync(pAsync);
- }
- if (FAILED(hr)) {
- err_set(errset, hr, "failed to complete backup");
- }
- break;
-
- case (HRESULT)VSS_E_OBJECT_NOT_FOUND:
- /*
- * On Windows earlier than 2008 SP2 which does not support
- * VSS_VOLSNAP_ATTR_NO_AUTORECOVERY context, the final commit is not
- * skipped and VSS is aborted by VSS_E_OBJECT_NOT_FOUND. However, as
- * the system had been frozen until fsfreeze-thaw command was issued,
- * we ignore this error.
- */
- vss_ctx.pVssbc->AbortBackup();
- break;
-
- case VSS_E_UNEXPECTED_PROVIDER_ERROR:
- if (WaitForSingleObject(vss_ctx.hEventTimeout, 0) != WAIT_OBJECT_0) {
- err_set(errset, hr, "unexpected error in VSS provider");
- break;
- }
- /* fall through if hEventTimeout is signaled */
-
- case (HRESULT)VSS_E_HOLD_WRITES_TIMEOUT:
- err_set(errset, hr, "couldn't hold writes: "
- "fsfreeze is limited up to 10 seconds");
- break;
-
- default:
- err_set(errset, hr, "failed to do snapshot set");
- }
-
- if (err_is_set(errset)) {
- vss_ctx.pVssbc->AbortBackup();
- }
- *num_vols = vss_ctx.cFrozenVols;
- requester_cleanup();
-
- CoUninitialize();
-}
diff --git a/qemu/qga/vss-win32/requester.h b/qemu/qga/vss-win32/requester.h
deleted file mode 100644
index 2a39d734a..000000000
--- a/qemu/qga/vss-win32/requester.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * QEMU Guest Agent VSS requester declarations
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 VSS_WIN32_REQUESTER_H
-#define VSS_WIN32_REQUESTER_H
-
-#include <basetyps.h> /* STDAPI */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct Error;
-
-/* Callback to set Error; used to avoid linking glib to the DLL */
-typedef void (*ErrorSetFunc)(struct Error **errp,
- const char *src, int line, const char *func,
- int win32_err, const char *fmt, ...)
- GCC_FMT_ATTR(6, 7);
-typedef struct ErrorSet {
- ErrorSetFunc error_setg_win32_wrapper;
- struct Error **errp; /* restriction: must not be null */
-} ErrorSet;
-
-STDAPI requester_init(void);
-STDAPI requester_deinit(void);
-
-typedef void (*QGAVSSRequesterFunc)(int *, ErrorSet *);
-void requester_freeze(int *num_vols, ErrorSet *errset);
-void requester_thaw(int *num_vols, ErrorSet *errset);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/qemu/qga/vss-win32/vss-common.h b/qemu/qga/vss-win32/vss-common.h
deleted file mode 100644
index 91dae0c38..000000000
--- a/qemu/qga/vss-win32/vss-common.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * QEMU Guest Agent win32 VSS common declarations
- *
- * Copyright Hitachi Data Systems Corp. 2013
- *
- * Authors:
- * Tomoki Sekiyama <tomoki.sekiyama@hds.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 VSS_WIN32_H
-#define VSS_WIN32_H
-
-#define __MIDL_user_allocate_free_DEFINED__
-#include <windows.h>
-#include <shlwapi.h>
-
-/* Reduce warnings to include vss.h */
-
-/* Ignore annotations for MS IDE */
-#define __in IN
-#define __out OUT
-#define __RPC_unique_pointer
-#define __RPC_string
-#define __RPC__deref_inout_opt
-#define __RPC__out
-#ifndef __RPC__out_ecount_part
-#define __RPC__out_ecount_part(x, y)
-#endif
-#define _declspec(x)
-#undef uuid
-#define uuid(x)
-
-/* Undef some duplicated error codes redefined in vss.h */
-#undef VSS_E_BAD_STATE
-#undef VSS_E_PROVIDER_NOT_REGISTERED
-#undef VSS_E_PROVIDER_VETO
-#undef VSS_E_OBJECT_NOT_FOUND
-#undef VSS_E_VOLUME_NOT_SUPPORTED
-#undef VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER
-#undef VSS_E_OBJECT_ALREADY_EXISTS
-#undef VSS_E_UNEXPECTED_PROVIDER_ERROR
-#undef VSS_E_INVALID_XML_DOCUMENT
-#undef VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED
-#undef VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED
-
-/*
- * VSS headers must be installed from Microsoft VSS SDK 7.2 available at:
- * http://www.microsoft.com/en-us/download/details.aspx?id=23490
- */
-#include "inc/win2003/vss.h"
-
-/* Macros to convert char definitions to wchar */
-#define _L(a) L##a
-#define L(a) _L(a)
-
-/* Constants for QGA VSS Provider */
-
-#define QGA_PROVIDER_NAME "QEMU Guest Agent VSS Provider"
-#define QGA_PROVIDER_LNAME L(QGA_PROVIDER_NAME)
-#define QGA_PROVIDER_VERSION L(QEMU_VERSION)
-
-#define EVENT_NAME_FROZEN "Global\\QGAVSSEvent-frozen"
-#define EVENT_NAME_THAW "Global\\QGAVSSEvent-thaw"
-#define EVENT_NAME_TIMEOUT "Global\\QGAVSSEvent-timeout"
-
-const GUID g_gProviderId = { 0x3629d4ed, 0xee09, 0x4e0e,
- {0x9a, 0x5c, 0x6d, 0x8b, 0xa2, 0x87, 0x2a, 0xef} };
-const GUID g_gProviderVersion = { 0x11ef8b15, 0xcac6, 0x40d6,
- {0x8d, 0x5c, 0x8f, 0xfc, 0x16, 0x3f, 0x24, 0xca} };
-
-const CLSID CLSID_QGAVSSProvider = { 0x6e6a3492, 0x8d4d, 0x440c,
- {0x96, 0x19, 0x5e, 0x5d, 0x0c, 0xc3, 0x1c, 0xa8} };
-
-const TCHAR g_szClsid[] = TEXT("{6E6A3492-8D4D-440C-9619-5E5D0CC31CA8}");
-const TCHAR g_szProgid[] = TEXT("QGAVSSProvider");
-
-/* Enums undefined in VSS SDK 7.2 but defined in newer Windows SDK */
-enum __VSS_VOLUME_SNAPSHOT_ATTRIBUTES {
- VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002,
- VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000
-};
-
-
-/* COM pointer utility; call ->Release() when it goes out of scope */
-template <class T>
-class COMPointer {
- COMPointer(const COMPointer<T> &p) { } /* no copy */
- T *p;
-public:
- COMPointer &operator=(T *new_p)
- {
- /* Assignment of a new T* (or NULL) causes release of previous p */
- if (p && p != new_p) {
- p->Release();
- }
- p = new_p;
- return *this;
- }
- /* Replace by assignment to the pointer of p */
- T **replace(void)
- {
- *this = NULL;
- return &p;
- }
- /* Make COMPointer be used like T* */
- operator T*() { return p; }
- T *operator->(void) { return p; }
- T &operator*(void) { return *p; }
- operator bool() { return !!p; }
-
- COMPointer(T *p = NULL) : p(p) { }
- ~COMPointer() { *this = NULL; } /* Automatic release */
-};
-
-/*
- * COM initializer; this should declared before COMPointer to uninitialize COM
- * after releasing COM objects.
- */
-class COMInitializer {
-public:
- COMInitializer() { CoInitialize(NULL); }
- ~COMInitializer() { CoUninitialize(); }
-};
-
-#endif