aboutsummaryrefslogtreecommitdiff
path: root/qga
diff options
context:
space:
mode:
Diffstat (limited to 'qga')
-rw-r--r--qga/commands-win32.c152
-rw-r--r--qga/guest-agent-core.h10
-rw-r--r--qga/main.c39
-rw-r--r--qga/meson.build2
-rw-r--r--qga/qapi-schema.json138
-rw-r--r--qga/vss-win32/install.cpp15
6 files changed, 275 insertions, 81 deletions
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 749fdf8..8227480 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -27,6 +27,7 @@
#include <lm.h>
#include <wtsapi32.h>
#include <wininet.h>
+#include <pdh.h>
#include "guest-agent-core.h"
#include "vss-win32.h"
@@ -119,6 +120,28 @@ static OpenFlags guest_file_open_modes[] = {
{"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS }
};
+/*
+ * We use an exponentially weighted moving average, just like Unix systems do
+ * https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
+ *
+ * These constants serve as the damping factor and are calculated with
+ * 1 / exp(sampling interval in seconds / window size in seconds)
+ *
+ * This formula comes from linux's include/linux/sched/loadavg.h
+ * https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
+ */
+#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241
+#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501
+#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394
+/*
+ * The time interval in seconds between taking load counts, same as Linux
+ */
+#define LOADAVG_SAMPLING_INTERVAL 5
+
+double load_avg_1m;
+double load_avg_5m;
+double load_avg_15m;
+
#define debug_error(msg) do { \
char *suffix = g_win32_error_message(GetLastError()); \
g_debug("%s: %s", (msg), suffix); \
@@ -826,8 +849,6 @@ static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk,
}
out_free:
g_free(dev_desc);
-
- return;
}
static void get_single_disk_info(int disk_number,
@@ -891,7 +912,6 @@ static void get_single_disk_info(int disk_number,
err_close:
CloseHandle(disk_h);
- return;
}
/* VSS provider works with volumes, thus there is no difference if
@@ -2117,7 +2137,6 @@ static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
rtl_get_version(info);
- return;
}
static char *ga_get_win_name(const OSVERSIONINFOEXW *os_version, bool id)
@@ -2448,3 +2467,128 @@ char *qga_get_host_name(Error **errp)
return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
}
+
+
+static VOID CALLBACK load_avg_callback(PVOID hCounter, BOOLEAN timedOut)
+{
+ PDH_FMT_COUNTERVALUE displayValue;
+ double currentLoad;
+ PDH_STATUS err;
+
+ err = PdhGetFormattedCounterValue(
+ (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue);
+ /* Skip updating the load if we can't get the value successfully */
+ if (err != ERROR_SUCCESS) {
+ slog("PdhGetFormattedCounterValue failed to get load value with 0x%lx",
+ err);
+ return;
+ }
+ currentLoad = displayValue.doubleValue;
+
+ load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_1F);
+ load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_5F);
+ load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F + currentLoad * \
+ (1.0 - LOADAVG_FACTOR_15F);
+}
+
+static BOOL init_load_avg_counter(Error **errp)
+{
+ CONST WCHAR *szCounterPath = L"\\System\\Processor Queue Length";
+ PDH_STATUS status;
+ BOOL ret;
+ HQUERY hQuery;
+ HCOUNTER hCounter;
+ HANDLE event;
+ HANDLE waitHandle;
+
+ status = PdhOpenQueryW(NULL, 0, &hQuery);
+ if (status != ERROR_SUCCESS) {
+ /*
+ * If the function fails, the return value is a system error code or
+ * a PDH error code. error_setg_win32 cant translate PDH error code
+ * properly, so just report it as is.
+ */
+ error_setg_win32(errp, (DWORD)status,
+ "PdhOpenQueryW failed with 0x%lx", status);
+ return FALSE;
+ }
+
+ status = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter);
+ if (status != ERROR_SUCCESS) {
+ error_setg_win32(errp, (DWORD)status,
+ "PdhAddEnglishCounterW failed with 0x%lx. Performance counters may be disabled.",
+ status);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent");
+ if (event == NULL) {
+ error_setg_win32(errp, GetLastError(), "Create LoadUpdateEvent failed");
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ status = PdhCollectQueryDataEx(hQuery, LOADAVG_SAMPLING_INTERVAL, event);
+ if (status != ERROR_SUCCESS) {
+ error_setg_win32(errp, (DWORD)status,
+ "PdhCollectQueryDataEx failed with 0x%lx", status);
+ CloseHandle(event);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ ret = RegisterWaitForSingleObject(
+ &waitHandle,
+ event,
+ (WAITORTIMERCALLBACK)load_avg_callback,
+ (PVOID)hCounter,
+ INFINITE,
+ WT_EXECUTEDEFAULT);
+
+ if (ret == 0) {
+ error_setg_win32(errp, GetLastError(),
+ "RegisterWaitForSingleObject failed");
+ CloseHandle(event);
+ PdhCloseQuery(hQuery);
+ return FALSE;
+ }
+
+ ga_set_load_avg_wait_handle(ga_state, waitHandle);
+ ga_set_load_avg_event(ga_state, event);
+ ga_set_load_avg_pdh_query(ga_state, hQuery);
+
+ return TRUE;
+}
+
+GuestLoadAverage *qmp_guest_get_load(Error **errp)
+{
+ /*
+ * The load average logic calls PerformaceCounterAPI, which can result
+ * in a performance penalty. This avoids running the load average logic
+ * until a management application actually requests it. The load average
+ * will not initially be very accurate, but assuming that any interested
+ * management application will request it repeatedly throughout the lifetime
+ * of the VM, this seems like a good mitigation.
+ */
+ if (ga_get_load_avg_pdh_query(ga_state) == NULL) {
+ /* set initial values */
+ load_avg_1m = 0;
+ load_avg_5m = 0;
+ load_avg_15m = 0;
+
+ if (init_load_avg_counter(errp) == false) {
+ return NULL;
+ }
+ }
+
+ GuestLoadAverage *ret = NULL;
+
+ ret = g_new0(GuestLoadAverage, 1);
+ ret->load1m = load_avg_1m;
+ ret->load5m = load_avg_5m;
+ ret->load15m = load_avg_15m;
+ return ret;
+}
diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h
index a536d07..d9f3922 100644
--- a/qga/guest-agent-core.h
+++ b/qga/guest-agent-core.h
@@ -13,6 +13,10 @@
#ifndef GUEST_AGENT_CORE_H
#define GUEST_AGENT_CORE_H
+#ifdef _WIN32
+#include <pdh.h>
+#endif
+
#include "qapi/qmp-registry.h"
#include "qga-qapi-types.h"
@@ -41,6 +45,12 @@ 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);
+#ifdef _WIN32
+void ga_set_load_avg_event(GAState *s, HANDLE event);
+void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle);
+void ga_set_load_avg_pdh_query(GAState *s, HQUERY query);
+HQUERY ga_get_load_avg_pdh_query(GAState *s);
+#endif
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);
diff --git a/qga/main.c b/qga/main.c
index 72c39b0..6c02f3e 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -33,6 +33,7 @@
#include "qemu-version.h"
#ifdef _WIN32
#include <dbt.h>
+#include <pdh.h>
#include "qga/service-win32.h"
#include "qga/vss-win32.h"
#endif
@@ -105,6 +106,9 @@ struct GAState {
GAService service;
HANDLE wakeup_event;
HANDLE event_log;
+ HANDLE load_avg_wait_handle;
+ HANDLE load_avg_event;
+ HQUERY load_avg_pdh_query;
#endif
bool delimit_response;
bool frozen;
@@ -582,6 +586,25 @@ const char *ga_fsfreeze_hook(GAState *s)
}
#endif
+#ifdef _WIN32
+void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle)
+{
+ s->load_avg_wait_handle = wait_handle;
+}
+void ga_set_load_avg_event(GAState *s, HANDLE event)
+{
+ s->load_avg_event = event;
+}
+void ga_set_load_avg_pdh_query(GAState *s, HQUERY query)
+{
+ s->load_avg_pdh_query = query;
+}
+HQUERY ga_get_load_avg_pdh_query(GAState *s)
+{
+ return s->load_avg_pdh_query;
+}
+#endif
+
static void become_daemon(const char *pidfile)
{
#ifndef _WIN32
@@ -1402,6 +1425,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
g_debug("Guest agent version %s started", QEMU_FULL_VERSION);
#ifdef _WIN32
+ s->load_avg_wait_handle = INVALID_HANDLE_VALUE;
+ s->load_avg_event = INVALID_HANDLE_VALUE;
+ s->load_avg_pdh_query = NULL;
+
s->event_log = RegisterEventSource(NULL, "qemu-ga");
if (!s->event_log) {
g_autofree gchar *errmsg = g_win32_error_message(GetLastError());
@@ -1506,6 +1533,18 @@ static void cleanup_agent(GAState *s)
#ifdef _WIN32
CloseHandle(s->wakeup_event);
CloseHandle(s->event_log);
+
+ if (s->load_avg_wait_handle != INVALID_HANDLE_VALUE) {
+ UnregisterWait(s->load_avg_wait_handle);
+ }
+
+ if (s->load_avg_event != INVALID_HANDLE_VALUE) {
+ CloseHandle(s->load_avg_event);
+ }
+
+ if (s->load_avg_pdh_query) {
+ PdhCloseQuery(s->load_avg_pdh_query);
+ }
#endif
if (s->command_state) {
ga_command_state_cleanup_all(s->command_state);
diff --git a/qga/meson.build b/qga/meson.build
index 587ec4e..89a4a8f 100644
--- a/qga/meson.build
+++ b/qga/meson.build
@@ -95,7 +95,7 @@ gen_tlb = []
qga_libs = []
if host_os == 'windows'
qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32',
- '-lsetupapi', '-lcfgmgr32', '-luserenv']
+ '-lsetupapi', '-lcfgmgr32', '-luserenv', '-lpdh' ]
if have_qga_vss
qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup']
subdir('vss-win32')
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 35ec0e7..8162d88 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -2,10 +2,24 @@
# vim: filetype=python
##
-# = QEMU guest agent protocol commands and structs
+# This manual describes the commands supported by the QEMU Guest
+# Agent Protocol.
#
-# For a concise listing of all commands, events, and types in the QEMU
-# guest agent, please consult the `qapi-qga-index`.
+# For locating a particular item, please see the `qapi-qga-index`.
+#
+# The following notation is used in examples:
+#
+# .. qmp-example::
+#
+# -> ... text sent by client (commands) ...
+# <- ... text sent by server (command responses and events) ...
+#
+# Example text is formatted for readability. However, in real
+# protocol usage, its commonly emitted as a single line.
+#
+# Please refer to the
+# :doc:`QEMU Machine Protocol Specification </interop/qmp-spec>`
+# for the general format of commands, responses, and events.
##
{ 'pragma': { 'doc-required': true } }
@@ -82,11 +96,11 @@
# 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
+# 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
+# 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.
#
@@ -188,8 +202,6 @@
#
# Get some information about the guest agent.
#
-# Returns: @GuestAgentInfo
-#
# Since: 0.15.0
##
{ 'command': 'guest-info',
@@ -205,7 +217,7 @@
#
# 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
+# when running with --no-shutdown, by issuing the `query-status` QMP
# command to confirm the VM status is "shutdown".
#
# Since: 0.15.0
@@ -235,7 +247,7 @@
#
# Close an open file in the guest
#
-# @handle: filehandle returned by guest-file-open
+# @handle: filehandle returned by `guest-file-open`
#
# Since: 0.15.0
##
@@ -266,13 +278,11 @@
# As this command is just for limited, ad-hoc debugging, such as log
# file access, the number of bytes to read is limited to 48 MB.
#
-# @handle: filehandle returned by guest-file-open
+# @handle: filehandle returned by `guest-file-open`
#
# @count: maximum number of bytes to read (default is 4KB, maximum is
# 48MB)
#
-# Returns: @GuestFileRead
-#
# Since: 0.15.0
##
{ 'command': 'guest-file-read',
@@ -299,15 +309,13 @@
#
# Write to an open file in the guest.
#
-# @handle: filehandle returned by guest-file-open
+# @handle: filehandle returned by `guest-file-open`
#
# @buf-b64: base64-encoded string representing data to be written
#
# @count: bytes to write (actual bytes, after base64-decode), default
# is all content in buf-b64 buffer after base64 decoding
#
-# Returns: @GuestFileWrite
-#
# Since: 0.15.0
##
{ 'command': 'guest-file-write',
@@ -332,7 +340,7 @@
##
# @QGASeek:
#
-# Symbolic names for use in @guest-file-seek
+# Symbolic names for use in `guest-file-seek`
#
# @set: Set to the specified offset (same effect as 'whence':0)
#
@@ -347,7 +355,7 @@
##
# @GuestFileWhence:
#
-# Controls the meaning of offset to @guest-file-seek.
+# 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
@@ -367,14 +375,12 @@
# current file position afterward. Also encapsulates ftell()'s
# functionality, with offset=0 and whence=1.
#
-# @handle: filehandle returned by guest-file-open
+# @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
-#
# Since: 0.15.0
##
{ 'command': 'guest-file-seek',
@@ -387,7 +393,7 @@
#
# Write file changes buffered in userspace to disk/kernel buffers
#
-# @handle: filehandle returned by guest-file-open
+# @handle: filehandle returned by `guest-file-open`
#
# Since: 0.15.0
##
@@ -414,9 +420,6 @@
#
# Get guest fsfreeze state.
#
-# 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.
@@ -431,12 +434,12 @@
# @guest-fsfreeze-freeze:
#
# Sync and freeze all freezable, local guest filesystems. If this
-# command succeeded, you may call @guest-fsfreeze-thaw later to
+# command succeeded, you may call `guest-fsfreeze-thaw` later to
# unfreeze.
#
# On error, all filesystems will be thawed. If no filesystems are
-# frozen as a result of this call, then @guest-fsfreeze-status will
-# remain "thawed" and calling @guest-fsfreeze-thaw is not necessary.
+# frozen as a result of this call, then `guest-fsfreeze-status` will
+# remain "thawed" and calling `guest-fsfreeze-thaw` is not necessary.
#
# Returns: Number of file systems currently frozen.
#
@@ -454,7 +457,7 @@
# @guest-fsfreeze-freeze-list:
#
# Sync and freeze specified guest filesystems. See also
-# @guest-fsfreeze-freeze.
+# `guest-fsfreeze-freeze`.
#
# On error, all filesystems will be thawed.
#
@@ -479,7 +482,7 @@
# Returns: Number of file systems thawed by this call
#
# .. note:: If the return value does not match the previous call to
-# guest-fsfreeze-freeze, this likely means some freezable filesystems
+# `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.
#
@@ -510,7 +513,7 @@
##
# @GuestFilesystemTrimResponse:
#
-# @paths: list of @GuestFilesystemTrimResult per path that was trimmed
+# @paths: list of `GuestFilesystemTrimResult` per path that was trimmed
#
# Since: 2.4
##
@@ -531,8 +534,7 @@
# 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)
+# Returns: status of all trimmed paths. (since 2.4)
#
# Since: 1.2
##
@@ -555,7 +557,7 @@
#
# 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
+# 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.
@@ -563,7 +565,7 @@
# Errors:
# - If suspend to disk is not supported, Unsupported
#
-# .. note:: It's strongly recommended to issue the guest-sync command
+# .. note:: It's strongly recommended to issue the `guest-sync` command
# before sending commands when the guest resumes.
#
# Since: 1.1
@@ -583,8 +585,8 @@
# - pm-utils (via pm-hibernate)
# - manual write into sysfs
#
-# IMPORTANT: guest-suspend-ram requires working wakeup support in
-# QEMU. You should check QMP command query-current-machine returns
+# IMPORTANT: `guest-suspend-ram` requires working wakeup support in
+# QEMU. You should check QMP command `query-current-machine` returns
# wakeup-suspend-support: true before issuing this command. Failure
# in doing so can result in a suspended guest that QEMU will not be
# able to awaken, forcing the user to power cycle the guest to bring
@@ -593,14 +595,14 @@
# 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
+# 1. Wait for the `SUSPEND` QMP event from QEMU
+# 2. Issue the `query-status` QMP command to confirm the VM status is
# "suspended"
#
# Errors:
# - If suspend to ram is not supported, Unsupported
#
-# .. note:: It's strongly recommended to issue the guest-sync command
+# .. note:: It's strongly recommended to issue the `guest-sync` command
# before sending commands when the guest resumes.
#
# Since: 1.1
@@ -619,8 +621,8 @@
# - systemd hybrid-sleep
# - pm-utils (via pm-suspend-hybrid)
#
-# IMPORTANT: guest-suspend-hybrid requires working wakeup support in
-# QEMU. You should check QMP command query-current-machine returns
+# IMPORTANT: `guest-suspend-hybrid` requires working wakeup support in
+# QEMU. You should check QMP command `query-current-machine` returns
# wakeup-suspend-support: true before issuing this command. Failure
# in doing so can result in a suspended guest that QEMU will not be
# able to awaken, forcing the user to power cycle the guest to bring
@@ -629,14 +631,14 @@
# 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
+# 1. Wait for the `SUSPEND` QMP event from QEMU
+# 2. Issue the `query-status` QMP command to confirm the VM status is
# "suspended"
#
# Errors:
# - If hybrid suspend is not supported, Unsupported
#
-# .. note:: It's strongly recommended to issue the guest-sync command
+# .. note:: It's strongly recommended to issue the `guest-sync` command
# before sending commands when the guest resumes.
#
# Since: 1.1
@@ -735,8 +737,6 @@
#
# Get list of guest IP addresses, MAC addresses and netmasks.
#
-# Returns: List of GuestNetworkInterface
-#
# Since: 1.1
##
{ 'command': 'guest-network-get-interfaces',
@@ -793,7 +793,7 @@
# 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.
+# `guest-get-vcpus`' return value.
#
# Returns: The length of the initial sublist that has been
# successfully processed. The guest agent maximizes this value.
@@ -1069,7 +1069,7 @@
#
# 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
+# `guest-fsfreeze-freeze-list`. Network filesystems (such as CIFS
# and NFS) are not listed.
#
# Since: 2.2
@@ -1171,7 +1171,7 @@
##
# @GuestMemoryBlockResponse:
#
-# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock.
+# @phys-index: same with the 'phys-index' member of `GuestMemoryBlock`.
#
# @response: the result of memory block operation.
#
@@ -1201,11 +1201,11 @@
# 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
+# 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
+# `GuestMemoryBlockResponse`, which is corresponding to the input
# list.
#
# Note: it will return an empty list if the @mem-blks list was
@@ -1237,8 +1237,6 @@
#
# Get information relating to guest memory blocks.
#
-# Returns: @GuestMemoryBlockInfo
-#
# Since: 2.3
##
{ 'command': 'guest-get-memory-block-info',
@@ -1260,7 +1258,7 @@
#
# @err-data: base64-encoded stderr of the process. Note: @out-data
# and @err-data are present only if 'capture-output' was specified
-# for 'guest-exec'. This field will only be populated after the
+# for `guest-exec`. This field will only be populated after the
# process exits.
#
# @out-truncated: true if stdout was not fully captured due to size
@@ -1279,12 +1277,10 @@
# @guest-exec-status:
#
# Check status of process associated with PID retrieved via
-# guest-exec. Reap the process and associated metadata if it has
+# `guest-exec`. Reap the process and associated metadata if it has
# exited.
#
-# @pid: pid returned from guest-exec
-#
-# Returns: GuestExecStatus
+# @pid: pid returned from `guest-exec`
#
# Since: 2.5
##
@@ -1305,7 +1301,7 @@
##
# @GuestExecCaptureOutputMode:
#
-# An enumeration of guest-exec capture modes.
+# An enumeration of `guest-exec` capture modes.
#
# @none: do not capture any output
#
@@ -1314,7 +1310,7 @@
# @stderr: only capture stderr
#
# @separated: capture both stdout and stderr, but separated into
-# GuestExecStatus out-data and err-data, respectively
+# `GuestExecStatus` out-data and err-data, respectively
#
# @merged: capture both stdout and stderr, but merge together into
# out-data. Not effective on windows guests.
@@ -1328,10 +1324,10 @@
##
# @GuestExecCaptureOutput:
#
-# Controls what guest-exec output gets captures.
+# Controls what `guest-exec` output gets captures.
#
# @flag: captures both stdout and stderr if true. Equivalent to
-# GuestExecCaptureOutputMode::all. (since 2.5)
+# `GuestExecCaptureOutputMode`::all. (since 2.5)
#
# @mode: capture mode; preferred interface
#
@@ -1444,8 +1440,6 @@
#
# Retrieves the timezone information from the guest.
#
-# Returns: A GuestTimezone dictionary.
-#
# Since: 2.10
##
{ 'command': 'guest-get-timezone',
@@ -1519,8 +1513,6 @@
#
# Retrieve guest operating system information
#
-# Returns: @GuestOSInfo
-#
# Since: 2.10
##
{ 'command': 'guest-get-osinfo',
@@ -1590,8 +1582,6 @@
#
# Retrieve information about device drivers in Windows guest
#
-# Returns: @GuestDeviceInfo
-#
# Since: 5.2
##
{ 'command': 'guest-get-devices',
@@ -1619,8 +1609,6 @@
#
# @username: the user account to add the authorized keys
#
-# Returns: @GuestAuthorizedKeys
-#
# Since: 5.2
##
{ 'command': 'guest-ssh-get-authorized-keys',
@@ -1866,7 +1854,7 @@
'load5m': 'number',
'load15m': 'number'
},
- 'if': 'CONFIG_GETLOADAVG'
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] }
}
##
@@ -1874,13 +1862,18 @@
#
# Retrieve CPU process load information
#
+# .. note:: Windows does not have load average API, so QGA emulates it by
+# calculating the average CPU usage in the last 1, 5, 15 minutes
+# similar as Linux does this.
+# Calculation starts from the first time this command is called.
+#
# Returns: load information
#
# Since: 10.0
##
{ 'command': 'guest-get-load',
'returns': 'GuestLoadAverage',
- 'if': 'CONFIG_GETLOADAVG'
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] }
}
##
@@ -1947,6 +1940,7 @@
# @guest-network-get-route:
#
# Retrieve information about route of network.
+#
# Returns: List of route info of guest.
#
# Since: 9.1
diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
index 5cea5bc..7b25d90 100644
--- a/qga/vss-win32/install.cpp
+++ b/qga/vss-win32/install.cpp
@@ -287,9 +287,13 @@ STDAPI COMRegister(void)
chk(QGAProviderFind(QGAProviderCount, (void *)&count));
if (count) {
- errmsg(E_ABORT, "QGA VSS Provider is already installed");
- qga_debug_end;
- return E_ABORT;
+ qga_debug("QGA VSS Provider is already installed. Attempting to unregister first.");
+ hr = COMUnregister();
+ if (FAILED(hr)) {
+ errmsg(hr, "Failed to unregister existing QGA VSS Provider. Aborting installation.");
+ qga_debug_end;
+ return E_ABORT;
+ }
}
chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER,
@@ -385,7 +389,10 @@ out:
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int);
STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int)
{
- COMRegister();
+ HRESULT hr = COMRegister();
+ if (FAILED(hr)) {
+ exit(hr);
+ }
}
STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int);