diff options
Diffstat (limited to 'qemu/qga')
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 Binary files differdeleted file mode 100644 index 226452a18..000000000 --- a/qemu/qga/vss-win32/qga-vss.tlb +++ /dev/null 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 |