Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2018-10-30-v3-tag' into staging

qemu-ga patch queue for soft-freeze

* support for --retry-path option for recovering from communication
  path failures
* support for serial/device name in guest-get-fsinfo for linux/w32
* support for freezing individual mount points in guest-fsfreeze-*
* fixes for unicode paths on w32, not-present vcpus in guest-get-vcpus,
  buffer overflow in guest-get-fsinfo for w32, and other minor fixes

v3:
* remove redundant check for --static in configure
* correct authorship on "qga-win: add debugging information"

v2:
* set libudev=off in configure for static builds

# gpg: Signature made Wed 31 Oct 2018 14:13:58 GMT
# gpg:                using RSA key 3353C9CEF108B584
# gpg: Good signature from "Michael Roth <flukshun@gmail.com>"
# gpg:                 aka "Michael Roth <mdroth@utexas.edu>"
# gpg:                 aka "Michael Roth <mdroth@linux.vnet.ibm.com>"
# Primary key fingerprint: CEAC C9E1 5534 EBAB B82D  3FA0 3353 C9CE F108 B584

* remotes/mdroth/tags/qga-pull-2018-10-30-v3-tag: (24 commits)
  qga-win: changing --retry-path option behavior
  qga-win: report specific error when failing to open channel
  qga-win: install service with --retry-path set by default
  qga: add --retry-path option for re-initializing channel on failure
  qga: move w32 service handling out of run_agent()
  qga: hang GAConfig/socket_activation off of GAState global
  qga: group agent init/cleanup init separate routines
  qga: fix an off-by-one issue
  qga-win: demystify namespace stripping
  qga-win: return disk device in guest-get-fsinfo
  qga-win: handle multi-disk volumes
  qga-win: refactor disk info
  qga-win: report disk serial number
  qga-win: refactor disk properties (bus)
  qga-win: add debugging information
  build: rename CONFIG_QGA_NTDDDISK to CONFIG_QGA_NTDDSCSI
  qga-win: fsinfo: pci-info: allow partial info
  qga-win: prevent crash when executing fsinfo command
  qga: linux: return disk device in guest-get-fsinfo
  qga: linux: report disk serial number
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2018-11-01 17:26:16 +00:00
13 changed files with 699 additions and 223 deletions
Vendored
+20 -1
View File
@@ -474,6 +474,7 @@ libxml2=""
docker="no"
debug_mutex="no"
libpmem=""
libudev="no"
# cross compilers defaults, can be overridden with --cross-cc-ARCH
cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -870,6 +871,7 @@ Linux)
vhost_vsock="yes"
QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES"
supported_os="yes"
libudev="yes"
;;
esac
@@ -5609,6 +5611,17 @@ if test "$libnfs" != "no" ; then
fi
fi
##########################################
# Do we have libudev
if test "$libudev" != "no" ; then
if $pkg_config libudev && test "$static" != "yes"; then
libudev="yes"
libudev_libs=$($pkg_config --libs libudev)
else
libudev="no"
fi
fi
# Now we've finished running tests it's OK to add -Werror to the compiler flags
if test "$werror" = "yes"; then
QEMU_CFLAGS="-Werror $QEMU_CFLAGS"
@@ -6033,6 +6046,7 @@ echo "VxHS block device $vxhs"
echo "capstone $capstone"
echo "docker $docker"
echo "libpmem support $libpmem"
echo "libudev $libudev"
if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -6128,7 +6142,7 @@ if test "$mingw32" = "yes" ; then
echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak
fi
if test "$guest_agent_ntddscsi" = "yes" ; then
echo "CONFIG_QGA_NTDDDISK=y" >> $config_host_mak
echo "CONFIG_QGA_NTDDSCSI=y" >> $config_host_mak
fi
if test "$guest_agent_msi" = "yes"; then
echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak
@@ -6868,6 +6882,11 @@ if test "$docker" != "no"; then
echo "HAVE_USER_DOCKER=y" >> $config_host_mak
fi
if test "$libudev" != "no"; then
echo "CONFIG_LIBUDEV=y" >> $config_host_mak
echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak
fi
# use included Linux headers
if test "$linux" = "yes" ; then
mkdir -p linux-headers
+1
View File
@@ -1,3 +1,4 @@
commands-posix.o-libs := $(LIBUDEV_LIBS)
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
+2 -1
View File
@@ -302,7 +302,8 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
if (c->handle == INVALID_HANDLE_VALUE) {
g_critical("error opening path %s", newpath);
g_critical("error opening path %s: %s", newpath,
g_win32_error_message(GetLastError()));
return false;
}
+92 -56
View File
@@ -48,6 +48,10 @@ extern char **environ;
#include <net/if.h>
#include <sys/statvfs.h>
#ifdef CONFIG_LIBUDEV
#include <libudev.h>
#endif
#ifdef FIFREEZE
#define CONFIG_FSFREEZE
#endif
@@ -872,6 +876,10 @@ static void build_guest_fsinfo_for_real_device(char const *syspath,
GuestDiskAddressList *list = NULL;
bool has_ata = false, has_host = false, has_tgt = false;
char *p, *q, *driver = NULL;
#ifdef CONFIG_LIBUDEV
struct udev *udev = NULL;
struct udev_device *udevice = NULL;
#endif
p = strstr(syspath, "/devices/pci");
if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
@@ -936,6 +944,26 @@ static void build_guest_fsinfo_for_real_device(char const *syspath,
list = g_malloc0(sizeof(*list));
list->value = disk;
#ifdef CONFIG_LIBUDEV
udev = udev_new();
udevice = udev_device_new_from_syspath(udev, syspath);
if (udev == NULL || udevice == NULL) {
g_debug("failed to query udev");
} else {
const char *devnode, *serial;
devnode = udev_device_get_devnode(udevice);
if (devnode != NULL) {
disk->dev = g_strdup(devnode);
disk->has_dev = true;
}
serial = udev_device_get_property_value(udevice, "ID_SERIAL");
if (serial != NULL && *serial != 0) {
disk->serial = g_strdup(serial);
disk->has_serial = true;
}
}
#endif
if (strcmp(driver, "ata_piix") == 0) {
/* a host per ide bus, target*:0:<unit>:0 */
if (!has_host || !has_tgt) {
@@ -995,14 +1023,19 @@ static void build_guest_fsinfo_for_real_device(char const *syspath,
list->next = fs->disk;
fs->disk = list;
g_free(driver);
return;
goto out;
cleanup:
if (list) {
qapi_free_GuestDiskAddressList(list);
}
out:
g_free(driver);
#ifdef CONFIG_LIBUDEV
udev_unref(udev);
udev_device_unref(udevice);
#endif
return;
}
static void build_guest_fsinfo_for_device(char const *devpath,
@@ -2035,61 +2068,56 @@ static long sysconf_exact(int name, const char *name_str, Error **errp)
* Written members remain unmodified on error.
*/
static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
Error **errp)
char *dirpath, Error **errp)
{
char *dirpath;
int fd;
int res;
int dirfd;
static const char fn[] = "online";
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);
return;
}
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 {
static const char fn[] = "online";
int fd;
int res;
unsigned char status;
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 = 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);
res = close(fd);
g_assert(res == 0);
}
g_free(dirpath);
res = close(dirfd);
g_assert(res == 0);
}
GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
@@ -2107,17 +2135,21 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
while (local_err == NULL && current < sc_max) {
GuestLogicalProcessor *vcpu;
GuestLogicalProcessorList *entry;
int64_t id = current++;
char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
id);
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 (g_file_test(path, G_FILE_TEST_EXISTS)) {
vcpu = g_malloc0(sizeof *vcpu);
vcpu->logical_id = id;
vcpu->has_can_offline = true; /* lolspeak ftw */
transfer_vcpu(vcpu, true, path, &local_err);
entry = g_malloc0(sizeof *entry);
entry->value = vcpu;
*link = entry;
link = &entry->next;
}
g_free(path);
}
if (local_err == NULL) {
@@ -2138,7 +2170,11 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
processed = 0;
while (vcpus != NULL) {
transfer_vcpu(vcpus->value, false, &local_err);
char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
vcpus->value->logical_id);
transfer_vcpu(vcpus->value, false, path, &local_err);
g_free(path);
if (local_err != NULL) {
break;
}
+281 -76
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -78,7 +78,7 @@
Account="LocalSystem"
ErrorControl="ignore"
Interactive="no"
Arguments="-d"
Arguments="-d --retry-path"
>
</ServiceInstall>
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="QEMU-GA" Wait="no" />
+213 -55
View File
@@ -34,6 +34,7 @@
#include "qemu/systemd.h"
#include "qemu-version.h"
#ifdef _WIN32
#include <dbt.h>
#include "qga/service-win32.h"
#include "qga/vss-win32.h"
#endif
@@ -58,6 +59,7 @@
#endif
#define QGA_SENTINEL_BYTE 0xFF
#define QGA_CONF_DEFAULT CONFIG_QEMU_CONFDIR G_DIR_SEPARATOR_S "qemu-ga.conf"
#define QGA_RETRY_INTERVAL 5
static struct {
const char *state_dir;
@@ -69,6 +71,8 @@ typedef struct GAPersistentState {
int64_t fd_counter;
} GAPersistentState;
typedef struct GAConfig GAConfig;
struct GAState {
JSONMessageParser parser;
GMainLoop *main_loop;
@@ -80,6 +84,7 @@ struct GAState {
bool logging_enabled;
#ifdef _WIN32
GAService service;
HANDLE wakeup_event;
#endif
bool delimit_response;
bool frozen;
@@ -94,6 +99,9 @@ struct GAState {
#endif
gchar *pstate_filepath;
GAPersistentState pstate;
GAConfig *config;
int socket_activation;
bool force_exit;
};
struct GAState *ga_state;
@@ -113,8 +121,11 @@ static const char *ga_freeze_whitelist[] = {
#ifdef _WIN32
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
LPVOID ctx);
DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data);
VOID WINAPI service_main(DWORD argc, TCHAR *argv[]);
#endif
static int run_agent(GAState *s);
static void stop_agent(GAState *s, bool requested);
static void
init_dfl_pathnames(void)
@@ -151,7 +162,7 @@ static void quit_handler(int sig)
WaitForSingleObject(hEventTimeout, 0);
CloseHandle(hEventTimeout);
}
qga_vss_fsfreeze(&i, false, &err);
qga_vss_fsfreeze(&i, false, NULL, &err);
if (err) {
g_debug("Error unfreezing filesystems prior to exiting: %s",
error_get_pretty(err));
@@ -163,9 +174,7 @@ static void quit_handler(int sig)
}
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);
}
stop_agent(ga_state, true);
}
#ifndef _WIN32
@@ -250,6 +259,10 @@ QEMU_COPYRIGHT "\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"
" -r, --retry-path attempt re-opening path if it's unavailable or closed\n"
" due to an error which may be recoverable in the future\n"
" (virtio-serial driver re-install, serial device hot\n"
" plug/unplug, etc.)\n"
" -h, --help display this help and exit\n"
"\n"
QEMU_HELP_BOTTOM "\n"
@@ -609,6 +622,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data)
switch (status) {
case G_IO_STATUS_ERROR:
g_warning("error reading channel");
stop_agent(s, false);
return false;
case G_IO_STATUS_NORMAL:
buf[count] = 0;
@@ -666,6 +680,36 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path,
}
#ifdef _WIN32
DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data)
{
DWORD ret = NO_ERROR;
PDEV_BROADCAST_HDR broadcast_header = (PDEV_BROADCAST_HDR)data;
if (broadcast_header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
switch (type) {
/* Device inserted */
case DBT_DEVICEARRIVAL:
/* Start QEMU-ga's service */
if (!SetEvent(ga_state->wakeup_event)) {
ret = GetLastError();
}
break;
/* Device removed */
case DBT_DEVICEQUERYREMOVE:
case DBT_DEVICEREMOVEPENDING:
case DBT_DEVICEREMOVECOMPLETE:
/* Stop QEMU-ga's service */
if (!ResetEvent(ga_state->wakeup_event)) {
ret = GetLastError();
}
break;
default:
ret = ERROR_CALL_NOT_IMPLEMENTED;
}
}
return ret;
}
DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
LPVOID ctx)
{
@@ -677,9 +721,13 @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data,
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
quit_handler(SIGTERM);
SetEvent(ga_state->wakeup_event);
service->status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(service->status_handle, &service->status);
break;
case SERVICE_CONTROL_DEVICEEVENT:
handle_serial_device_events(type, data);
break;
default:
ret = ERROR_CALL_NOT_IMPLEMENTED;
@@ -706,10 +754,24 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[])
service->status.dwServiceSpecificExitCode = NO_ERROR;
service->status.dwCheckPoint = 0;
service->status.dwWaitHint = 0;
DEV_BROADCAST_DEVICEINTERFACE notification_filter;
ZeroMemory(&notification_filter, sizeof(notification_filter));
notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
notification_filter.dbcc_classguid = GUID_VIOSERIAL_PORT;
service->device_notification_handle =
RegisterDeviceNotification(service->status_handle,
&notification_filter, DEVICE_NOTIFY_SERVICE_HANDLE);
if (!service->device_notification_handle) {
g_critical("Failed to register device notification handle!\n");
return;
}
SetServiceStatus(service->status_handle, &service->status);
g_main_loop_run(ga_state->main_loop);
run_agent(ga_state);
UnregisterDeviceNotification(service->device_notification_handle);
service->status.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(service->status_handle, &service->status);
}
@@ -905,7 +967,7 @@ static GList *split_list(const gchar *str, const gchar *delim)
return list;
}
typedef struct GAConfig {
struct GAConfig {
char *channel_path;
char *method;
char *log_filepath;
@@ -922,7 +984,8 @@ typedef struct GAConfig {
int daemonize;
GLogLevelFlags log_level;
int dumpconf;
} GAConfig;
bool retry_path;
};
static void config_load(GAConfig *config)
{
@@ -971,6 +1034,10 @@ static void config_load(GAConfig *config)
/* enable all log levels */
config->log_level = G_LOG_LEVEL_MASK;
}
if (g_key_file_has_key(keyfile, "general", "retry-path", NULL)) {
config->retry_path =
g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr);
}
if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) {
config->bliststr =
g_key_file_get_string(keyfile, "general", "blacklist", &gerr);
@@ -1032,6 +1099,8 @@ static void config_dump(GAConfig *config)
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);
g_key_file_set_boolean(keyfile, "general", "retry-path",
config->retry_path);
tmp = list_join(config->blacklist, ',');
g_key_file_set_string(keyfile, "general", "blacklist", tmp);
g_free(tmp);
@@ -1050,7 +1119,7 @@ static void config_dump(GAConfig *config)
static void config_parse(GAConfig *config, int argc, char **argv)
{
const char *sopt = "hVvdm:p:l:f:F::b:s:t:D";
const char *sopt = "hVvdm:p:l:f:F::b:s:t:Dr";
int opt_ind = 0, ch;
const struct option lopt[] = {
{ "help", 0, NULL, 'h' },
@@ -1070,6 +1139,7 @@ static void config_parse(GAConfig *config, int argc, char **argv)
{ "service", 1, NULL, 's' },
#endif
{ "statedir", 1, NULL, 't' },
{ "retry-path", 0, NULL, 'r' },
{ NULL, 0, NULL, 0 }
};
@@ -1114,6 +1184,9 @@ static void config_parse(GAConfig *config, int argc, char **argv)
case 'D':
config->dumpconf = 1;
break;
case 'r':
config->retry_path = true;
break;
case 'b': {
if (is_help_option(optarg)) {
qmp_for_each_command(&ga_commands, ga_print_cmd, NULL);
@@ -1211,9 +1284,21 @@ static bool check_is_frozen(GAState *s)
return false;
}
static int run_agent(GAState *s, GAConfig *config, int socket_activation)
static GAState *initialize_agent(GAConfig *config, int socket_activation)
{
ga_state = s;
GAState *s = g_new0(GAState, 1);
g_assert(ga_state == NULL);
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);
g_log_set_default_handler(ga_log, s);
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR);
@@ -1229,7 +1314,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
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;
return NULL;
}
#endif
@@ -1254,7 +1339,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
if (!log_file) {
g_critical("unable to open specified log file: %s",
strerror(errno));
return EXIT_FAILURE;
return NULL;
}
s->log_file = log_file;
}
@@ -1265,7 +1350,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
s->pstate_filepath,
ga_is_frozen(s))) {
g_critical("failed to load persistent state");
return EXIT_FAILURE;
return NULL;
}
config->blacklist = ga_command_blacklist_init(config->blacklist);
@@ -1286,36 +1371,116 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
#ifndef _WIN32
if (!register_signal_handlers()) {
g_critical("failed to register signal handlers");
return EXIT_FAILURE;
return NULL;
}
#endif
s->main_loop = g_main_loop_new(NULL, false);
if (!channel_init(ga_state, config->method, config->channel_path,
socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
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);
s->config = config;
s->socket_activation = socket_activation;
#ifdef _WIN32
s->wakeup_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WakeUp"));
if (s->wakeup_event == NULL) {
g_critical("CreateEvent failed");
return NULL;
}
#endif
ga_state = s;
return s;
}
static void cleanup_agent(GAState *s)
{
#ifdef _WIN32
CloseHandle(s->wakeup_event);
#endif
if (s->command_state) {
ga_command_state_cleanup_all(s->command_state);
ga_command_state_free(s->command_state);
json_message_parser_destroy(&s->parser);
}
g_free(s->pstate_filepath);
g_free(s->state_filepath_isfrozen);
if (s->main_loop) {
g_main_loop_unref(s->main_loop);
}
g_free(s);
ga_state = NULL;
}
static int run_agent_once(GAState *s)
{
if (!channel_init(s, s->config->method, s->config->channel_path,
s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) {
g_critical("failed to initialize guest agent channel");
return EXIT_FAILURE;
}
g_main_loop_run(ga_state->main_loop);
if (s->channel) {
ga_channel_free(s->channel);
}
return EXIT_SUCCESS;
}
static void wait_for_channel_availability(GAState *s)
{
g_warning("waiting for channel path...");
#ifndef _WIN32
sleep(QGA_RETRY_INTERVAL);
#else
DWORD dwWaitResult;
dwWaitResult = WaitForSingleObject(s->wakeup_event, INFINITE);
switch (dwWaitResult) {
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
break;
default:
g_critical("WaitForSingleObject failed");
}
#endif
}
static int run_agent(GAState *s)
{
int ret = EXIT_SUCCESS;
s->force_exit = false;
do {
ret = run_agent_once(s);
if (s->config->retry_path && !s->force_exit) {
g_warning("agent stopped unexpectedly, restarting...");
wait_for_channel_availability(s);
}
} while (s->config->retry_path && !s->force_exit);
return ret;
}
static void stop_agent(GAState *s, bool requested)
{
if (!s->force_exit) {
s->force_exit = requested;
}
if (g_main_loop_is_running(s->main_loop)) {
g_main_loop_quit(s->main_loop);
}
}
int main(int argc, char **argv)
{
int ret = EXIT_SUCCESS;
GAState *s = g_new0(GAState, 1);
GAState *s;
GAConfig *config = g_new0(GAConfig, 1);
int socket_activation;
@@ -1383,44 +1548,37 @@ int main(int argc, char **argv)
}
}
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, socket_activation);
s = initialize_agent(config, socket_activation);
if (!s) {
g_critical("error initializing guest agent");
goto end;
}
#ifdef _WIN32
if (config->daemonize) {
SERVICE_TABLE_ENTRY service_table[] = {
{ (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } };
StartServiceCtrlDispatcher(service_table);
} else {
ret = run_agent(s);
}
#else
ret = run_agent(s);
#endif
cleanup_agent(s);
end:
if (s->command_state) {
ga_command_state_cleanup_all(s->command_state);
ga_command_state_free(s->command_state);
json_message_parser_destroy(&s->parser);
}
if (s->channel) {
ga_channel_free(s->channel);
}
g_free(s->pstate_filepath);
g_free(s->state_filepath_isfrozen);
if (config->daemonize) {
unlink(config->pid_filepath);
}
config_free(config);
if (s->main_loop) {
g_main_loop_unref(s->main_loop);
}
g_free(s);
return ret;
}
+4 -1
View File
@@ -834,13 +834,16 @@
# @bus: bus id
# @target: target id
# @unit: unit id
# @serial: serial number (since: 3.1)
# @dev: device node (POSIX) or device UNC (Windows) (since: 3.1)
#
# Since: 2.2
##
{ 'struct': 'GuestDiskAddress',
'data': {'pci-controller': 'GuestPCIAddress',
'bus-type': 'GuestDiskBusType',
'bus': 'int', 'target': 'int', 'unit': 'int'} }
'bus': 'int', 'target': 'int', 'unit': 'int',
'*serial': 'str', '*dev': 'str'} }
##
# @GuestFilesystemInfo:
+4
View File
@@ -20,9 +20,13 @@
#define QGA_SERVICE_NAME "qemu-ga"
#define QGA_SERVICE_DESCRIPTION "Enables integration with QEMU machine emulator and virtualizer."
static const GUID GUID_VIOSERIAL_PORT = { 0x6fde7521, 0x1b65, 0x48ae,
{ 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26 } };
typedef struct GAService {
SERVICE_STATUS status;
SERVICE_STATUS_HANDLE status_handle;
HDEVNOTIFY device_notification_handle;
} GAService;
int ga_install_service(const char *path, const char *logfile,
+3 -2
View File
@@ -147,7 +147,8 @@ void ga_uninstall_vss_provider(void)
}
/* Call VSS requester and freeze/thaw filesystems and applications */
void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp)
void qga_vss_fsfreeze(int *nr_volume, bool freeze,
strList *mountpoints, Error **errp)
{
const char *func_name = freeze ? "requester_freeze" : "requester_thaw";
QGAVSSRequesterFunc func;
@@ -164,5 +165,5 @@ void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp)
return;
}
func(nr_volume, &errset);
func(nr_volume, mountpoints, &errset);
}
+2 -1
View File
@@ -22,6 +22,7 @@ bool vss_initialized(void);
int ga_install_vss_provider(void);
void ga_uninstall_vss_provider(void);
void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp);
void qga_vss_fsfreeze(int *nr_volume, bool freeze,
strList *mountpints, Error **errp);
#endif
+66 -26
View File
@@ -234,7 +234,7 @@ out:
}
}
void requester_freeze(int *num_vols, ErrorSet *errset)
void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset)
{
COMPointer<IVssAsync> pAsync;
HANDLE volume;
@@ -246,6 +246,7 @@ void requester_freeze(int *num_vols, ErrorSet *errset)
WCHAR short_volume_name[64], *display_name = short_volume_name;
DWORD wait_status;
int num_fixed_drives = 0, i;
int num_mount_points = 0;
if (vss_ctx.pVssbc) { /* already frozen */
*num_vols = 0;
@@ -337,39 +338,73 @@ void requester_freeze(int *num_vols, ErrorSet *errset)
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) {
if (mountpoints) {
PWCHAR volume_name_wchar;
for (volList *list = (volList *)mountpoints; list; list = list->next) {
size_t len = strlen(list->value) + 1;
size_t converted = 0;
VSS_ID pid;
hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name,
volume_name_wchar = new wchar_t[len];
mbstowcs_s(&converted, volume_name_wchar, len,
list->value, _TRUNCATE);
hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar,
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);
volume_name_wchar);
delete volume_name_wchar;
goto out;
}
num_fixed_drives++;
num_mount_points++;
delete volume_name_wchar;
}
if (!FindNextVolumeW(volume, short_volume_name,
sizeof(short_volume_name))) {
FindVolumeClose(volume);
break;
if (num_mount_points == 0) {
/* If there is no valid mount points, just exit. */
goto out;
}
}
if (num_fixed_drives == 0) {
goto out; /* If there is no fixed drive, just exit. */
if (!mountpoints) {
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());
@@ -435,7 +470,12 @@ void requester_freeze(int *num_vols, ErrorSet *errset)
goto out;
}
*num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
if (mountpoints) {
*num_vols = vss_ctx.cFrozenVols = num_mount_points;
} else {
*num_vols = vss_ctx.cFrozenVols = num_fixed_drives;
}
return;
out:
@@ -449,7 +489,7 @@ out1:
}
void requester_thaw(int *num_vols, ErrorSet *errset)
void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset)
{
COMPointer<IVssAsync> pAsync;
+10 -3
View File
@@ -34,9 +34,16 @@ typedef struct 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);
typedef struct volList volList;
struct volList {
volList *next;
char *value;
};
typedef void (*QGAVSSRequesterFunc)(int *, void *, ErrorSet *);
void requester_freeze(int *num_vols, void *volList, ErrorSet *errset);
void requester_thaw(int *num_vols, void *volList, ErrorSet *errset);
#ifdef __cplusplus
}