diff options
Diffstat (limited to 'qga')
-rw-r--r-- | qga/commands-win32.c | 152 | ||||
-rw-r--r-- | qga/guest-agent-core.h | 10 | ||||
-rw-r--r-- | qga/main.c | 39 | ||||
-rw-r--r-- | qga/meson.build | 2 | ||||
-rw-r--r-- | qga/qapi-schema.json | 138 | ||||
-rw-r--r-- | qga/vss-win32/install.cpp | 15 |
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); @@ -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); |