On Thu, Sep 24, 2026 at 10:41 PM <[email protected]> wrote:
> > Add a new guest-get-drivers command that reports two independent lists > > of Windows driver information: > > > > - packages: out-of-box driver packages enumerated with the DISM API > > (DismGetDrivers). Each entry includes the published INF name, > > original INF name, version, and provider. > > - services: registered driver services enumerated with the Service > > Control Manager (EnumServicesStatusExW). Each entry includes the > > service name, configured binary path, current SCM status, and > > FileVersion and CompanyName metadata from the driver binary. > > > > Load DismApi.dll dynamically with LoadLibraryExW so that its absence > > does not prevent qemu-ga from starting. Initialize DISM lazily once per > > QGA process and open a separate online DISM session for each command > > invocation, with process cleanup registered through ga_command_state_add. > > > > Normalize driver binary paths from the service database before reading > > version resources, including SystemRoot, environment-variable, NT-prefix, > > and relative path forms. > > > > Link against version.dll (-lversion) for the GetFileVersionInfo and > > VerQueryValue APIs. > > > > Signed-off-by: Elizabeth Ashurov <[email protected]> > > Message-ID: <[email protected]> > > > > diff --git a/qga/commands-win32.c b/qga/commands-win32.c > > index 2bf5581aaa72..0dc94d808e35 100644 > > --- a/qga/commands-win32.c > > +++ b/qga/commands-win32.c > > @@ -28,6 +28,8 @@ > > #include <wtsapi32.h> > > #include <wininet.h> > > #include <pdh.h> > > +#include <dismapi.h> > > +#include <winver.h> > > > > #include "guest-agent-core.h" > > #include "vss-win32.h" > > @@ -1981,12 +1983,15 @@ done: > > g_free(rawpasswddata); > > } > > > > +static void ga_cleanup_dism_api(void); > > + > > /* 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); > > } > > + ga_command_state_add(cs, NULL, ga_cleanup_dism_api); > > } > > > > /* MINGW is missing two fields: IncomingFrames & OutgoingFrames */ > > @@ -2455,6 +2460,684 @@ GuestDeviceInfoList *qmp_guest_get_devices(Error > **errp) > > return head; > > } > > > > +static void error_setg_dism(Error **errp, HRESULT hr, const char *msg) > > +{ > > + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) { > > + error_setg_win32(errp, HRESULT_CODE(hr), "%s", msg); > > + } else { > > + error_setg(errp, "%s (HRESULT 0x%08" PRIx32 ")", msg, > > + (uint32_t)hr); > > + } > > +} > > + > > +static void ga_log_dism_error(HRESULT hr, const char *msg) > > +{ > > + Error *local_err = NULL; > > + > > + error_setg_dism(&local_err, hr, msg); > > + slog("%s", error_get_pretty(local_err)); > > + error_free(local_err); > > +} > > + > > +typedef HRESULT WINAPI QGADismInitializeFunc( > > + DismLogLevel log_level, PCWSTR log_file_path, PCWSTR > scratch_directory); > > +typedef HRESULT WINAPI QGADismShutdownFunc(void); > > +typedef HRESULT WINAPI QGADismOpenSessionFunc( > > + PCWSTR image_path, PCWSTR windows_directory, PCWSTR system_drive, > > + DismSession *session); > > +typedef HRESULT WINAPI QGADismCloseSessionFunc(DismSession session); > > +typedef HRESULT WINAPI QGADismGetDriversFunc( > > + DismSession session, WINBOOL all_drivers, > > + DismDriverPackage **driver_package, unsigned int *driver_count); > > +typedef HRESULT WINAPI QGADismDeleteFunc(void *dism_structure); > > + > > +typedef struct QGADismApi { > > + HMODULE module; > > + QGADismInitializeFunc *initialize; > > + QGADismShutdownFunc *shutdown; > > + QGADismOpenSessionFunc *open_session; > > + QGADismCloseSessionFunc *close_session; > > + QGADismGetDriversFunc *get_drivers; > > + QGADismDeleteFunc *delete; > > +} QGADismApi; > > + > > +static QGADismApi dism_api; > > +static bool dism_initialized; > > + > > +static FARPROC ga_get_dism_function(HMODULE module, const char *name, > > + Error **errp) > > +{ > > + FARPROC function = GetProcAddress(module, name); > > + > > + if (function == NULL) { > > + error_setg_win32(errp, GetLastError(), > > + "failed to resolve %s from DismApi.dll", name); > > + } > > + return function; > > +} > > + > > +static void ga_unload_dism_api(QGADismApi *api) > > +{ > > + if (api->module != NULL && !FreeLibrary(api->module)) { > > + slog("failed to unload DismApi.dll, error=%lu", GetLastError()); > > + } > > + memset(api, 0, sizeof(*api)); > > +} > > + > > +/* > > + * Load DISM from System32 and resolve its entry points at runtime, so a > > + * missing DismApi.dll prevents only this command rather than QGA > startup. > > + */ > > +static bool ga_load_dism_api(QGADismApi *api, Error **errp) > > +{ > > + api->module = LoadLibraryExW(L"DismApi.dll", NULL, > > + LOAD_LIBRARY_SEARCH_SYSTEM32); > > + if (api->module == NULL) { > > + error_setg_win32(errp, GetLastError(), > > + "failed to load DismApi.dll"); > > + return false; > > + } > > + > > + api->initialize = (QGADismInitializeFunc *) > > + ga_get_dism_function(api->module, "DismInitialize", errp); > > + if (api->initialize == NULL) { > > + goto fail; > > + } > > + api->shutdown = (QGADismShutdownFunc *) > > + ga_get_dism_function(api->module, "DismShutdown", errp); > > + if (api->shutdown == NULL) { > > + goto fail; > > + } > > + api->open_session = (QGADismOpenSessionFunc *) > > + ga_get_dism_function(api->module, "DismOpenSession", errp); > > + if (api->open_session == NULL) { > > + goto fail; > > + } > > + api->close_session = (QGADismCloseSessionFunc *) > > + ga_get_dism_function(api->module, "DismCloseSession", errp); > > + if (api->close_session == NULL) { > > + goto fail; > > + } > > + api->get_drivers = (QGADismGetDriversFunc *) > > + ga_get_dism_function(api->module, "DismGetDrivers", errp); > > + if (api->get_drivers == NULL) { > > + goto fail; > > + } > > + api->delete = (QGADismDeleteFunc *) > > + ga_get_dism_function(api->module, "DismDelete", errp); > > + if (api->delete == NULL) { > > + goto fail; > > + } > > + > > + return true; > > + > > +fail: > > + ga_unload_dism_api(api); > > + return false; > > +} > > + > > +static bool ga_ensure_dism_api(Error **errp) > > +{ > > + HRESULT hr; > > + > > + if (dism_initialized) { > > + return true; > > + } > > + > > + if (!ga_load_dism_api(&dism_api, errp)) { > > + return false; > > + } > > + > > + hr = dism_api.initialize(DismLogErrors, NULL, NULL); > > + if (FAILED(hr)) { > > + error_setg_dism(errp, hr, "failed to initialize DISM"); > > + ga_unload_dism_api(&dism_api); > > + return false; > > + } > > + > > + dism_initialized = true; > > + return true; > > +} > > + > > +static void ga_cleanup_dism_api(void) > > +{ > > + HRESULT hr; > > + > > + if (dism_initialized) { > > + hr = dism_api.shutdown(); > > + if (FAILED(hr)) { > > + ga_log_dism_error(hr, "failed to shut down DISM"); > > + } > > + dism_initialized = false; > > + } > > + > > + ga_unload_dism_api(&dism_api); > > +} > > + > > +static char *ga_utf16_to_utf8_required(const WCHAR *str, const char > *name, > > + Error **errp) > > +{ > > + g_autoptr(GError) gerr = NULL; > > + char *ret; > > + > > + if (str == NULL || str[0] == L'\0') { > > + error_setg(errp, "driver %s is missing", name); > > + return NULL; > > + } > > + > > + ret = g_utf16_to_utf8(str, -1, NULL, NULL, &gerr); > > + if (ret == NULL) { > > + error_setg(errp, "failed to convert driver %s to UTF-8: %s", > > + name, gerr->message); > > + } > > + return ret; > > +} > > + > > +static const WCHAR *ga_windows_path_basename(const WCHAR *path) > > +{ > > + const WCHAR *name = path; > > + > > + while (*path != L'\0') { > > + if (*path == L'\\' || *path == L'/') { > > + name = path + 1; > > + } > > + path++; > > + } > > + return name; > > +} > > + > > +static GuestDriverPackageList *ga_get_driver_packages(Error **errp) > > +{ > > + GuestDriverPackageList *head = NULL, **tail = &head; > > + DismSession session = DISM_SESSION_DEFAULT; > > + DismDriverPackage *drivers = NULL; > > + Error *local_err = NULL; > > + HRESULT hr; > > + UINT count = 0; > > + UINT i; > > + > > + if (!ga_ensure_dism_api(&local_err)) { > > + goto out; > > + } > > + > > + hr = dism_api.open_session(DISM_ONLINE_IMAGE, NULL, NULL, &session); > > + if (FAILED(hr)) { > > + error_setg_dism(&local_err, hr, > > + "failed to open online DISM session"); > > + goto out; > > + } > > + > > + /* > > + * FALSE retrieves only out-of-box drivers, meaning drivers that > were > > + * not originally included in the Windows image. See: > > + * > https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismgetdrivers-function > > + */ > > + hr = dism_api.get_drivers(session, FALSE, &drivers, &count); > > + if (FAILED(hr)) { > > + error_setg_dism(&local_err, hr, > > + "failed to enumerate driver packages"); > > + goto out; > > + } > > + > > + for (i = 0; i < count; i++) { > > + g_autoptr(GuestDriverPackage) package = NULL; > > + > > + package = g_new0(GuestDriverPackage, 1); > > + package->name = > ga_utf16_to_utf8_required(drivers[i].PublishedName, > > + "package name", > &local_err); > > + if (package->name == NULL) { > > + goto out; > > + } > > + > > + if (drivers[i].OriginalFileName != NULL && > > + drivers[i].OriginalFileName[0] != L'\0') { > > + const WCHAR *original_name = ga_windows_path_basename( > > + drivers[i].OriginalFileName); > > + > > + if (original_name[0] != L'\0') { > > + package->original_name = g_utf16_to_utf8( > > + original_name, -1, NULL, NULL, NULL); > > + } > > + } > > + > > + package->version = g_strdup_printf("%u.%u.%u.%u", > > + drivers[i].MajorVersion, > > + drivers[i].MinorVersion, > > + drivers[i].Build, > > + drivers[i].Revision); > > + > > + if (drivers[i].ProviderName != NULL && > > + drivers[i].ProviderName[0] != L'\0') { > > + package->vendor = g_utf16_to_utf8(drivers[i].ProviderName, > -1, > > + NULL, NULL, NULL); > > + } > > + > > + QAPI_LIST_APPEND(tail, g_steal_pointer(&package)); > > + } > > + > > +out: > > + if (drivers != NULL) { > > + hr = dism_api.delete(drivers); > > + if (FAILED(hr)) { > > + if (local_err == NULL) { > > + error_setg_dism(&local_err, hr, > > + "failed to release driver package > data"); > > + } else { > > + ga_log_dism_error(hr, > > + "failed to release driver package > data"); > > + } > > + } > > + } > > + if (session != DISM_SESSION_DEFAULT) { > > + hr = dism_api.close_session(session); > > + if (FAILED(hr)) { > > + if (local_err == NULL) { > > + error_setg_dism(&local_err, hr, > > + "failed to close online DISM session"); > > + } else { > > + ga_log_dism_error(hr, > > + "failed to close online DISM > session"); > > + } > > + } > > + } > > + if (local_err != NULL) { > > + qapi_free_GuestDriverPackageList(head); > > + head = NULL; > > + error_propagate(errp, local_err); > > + } > > + return head; > > +} > > + > > +static bool ga_driver_service_status(DWORD state, > > + GuestDriverServiceStatus *status) > > +{ > > + switch (state) { > > + case SERVICE_STOPPED: > > + *status = GUEST_DRIVER_SERVICE_STATUS_STOPPED; > > + return true; > > + case SERVICE_START_PENDING: > > + *status = GUEST_DRIVER_SERVICE_STATUS_START_PENDING; > > + return true; > > + case SERVICE_STOP_PENDING: > > + *status = GUEST_DRIVER_SERVICE_STATUS_STOP_PENDING; > > + return true; > > + case SERVICE_RUNNING: > > + *status = GUEST_DRIVER_SERVICE_STATUS_RUNNING; > > + return true; > > + case SERVICE_CONTINUE_PENDING: > > + *status = GUEST_DRIVER_SERVICE_STATUS_CONTINUE_PENDING; > > + return true; > > + case SERVICE_PAUSE_PENDING: > > + *status = GUEST_DRIVER_SERVICE_STATUS_PAUSE_PENDING; > > + return true; > > + case SERVICE_PAUSED: > > + *status = GUEST_DRIVER_SERVICE_STATUS_PAUSED; > > + return true; > > + default: > > + return false; > > + } > > +} > > + > > +/* Convert an SCM driver path into a path usable by Win32 file APIs. */ > > +static WCHAR *ga_resolve_driver_path(const WCHAR *path) > > +{ > > + WCHAR windows_dir[MAX_PATH + 1]; > > + g_autofree WCHAR *trimmed = NULL; > > + g_autofree WCHAR *expanded = NULL; > > + const WCHAR *start = path; > > + const WCHAR *end; > > + const WCHAR *remainder = NULL; > > + size_t prefix_len; > > + size_t path_len; > > + size_t windows_len; > > + DWORD expanded_len; > > + UINT windows_dir_len; > > + > > + while (*start != L'\0' && iswspace(*start)) { > > + start++; > > + } > > + end = start + wcslen(start); > > + while (end > start && iswspace(end[-1])) { > > + end--; > > + } > > + if (end - start >= 2 && start[0] == L'"' && end[-1] == L'"') { > > + start++; > > + end--; > > + } > > + if (end == start) { > > + return NULL; > > + } > > + > > + path_len = end - start; > > + trimmed = g_new(WCHAR, path_len + 1); > > + memcpy(trimmed, start, path_len * sizeof(WCHAR)); > > + trimmed[path_len] = L'\0'; > > + > > + expanded_len = ExpandEnvironmentStringsW(trimmed, NULL, 0); > > + if (expanded_len == 0) { > > + return NULL; > > + } > > + expanded = g_new(WCHAR, expanded_len); > > + path_len = ExpandEnvironmentStringsW(trimmed, expanded, > expanded_len); > > + if (path_len == 0 || path_len > expanded_len) { > > + return NULL; > > + } > > + > > + prefix_len = wcslen(L"\\SystemRoot"); > > + if (_wcsnicmp(expanded, L"\\SystemRoot", prefix_len) == 0 && > > + (expanded[prefix_len] == L'\0' || > > + expanded[prefix_len] == L'\\' || > > + expanded[prefix_len] == L'/')) { > > + remainder = expanded + prefix_len; > > + } else { > > + prefix_len = wcslen(L"SystemRoot"); > > + if (_wcsnicmp(expanded, L"SystemRoot", prefix_len) == 0 && > > + (expanded[prefix_len] == L'\0' || > > + expanded[prefix_len] == L'\\' || > > + expanded[prefix_len] == L'/')) { > > + remainder = expanded + prefix_len; > > + } > > + } > > + > > + windows_dir_len = GetWindowsDirectoryW(windows_dir, > > + ARRAY_SIZE(windows_dir)); > > + if (windows_dir_len == 0 || windows_dir_len >= > ARRAY_SIZE(windows_dir)) { > > + return NULL; > > + } > > + windows_len = windows_dir_len; > > + > > + if (remainder != NULL) { > > + WCHAR *ret = g_new(WCHAR, windows_len + wcslen(remainder) + 1); > > + > > + memcpy(ret, windows_dir, windows_len * sizeof(WCHAR)); > > + wcscpy(ret + windows_len, remainder); > > + return ret; > > + } > > + > > + if (wcsncmp(expanded, L"\\??\\", 4) == 0) { > > + return g_memdup2(expanded + 4, > > + (wcslen(expanded + 4) + 1) * sizeof(WCHAR)); > > + } > > + > > + if ((iswalpha(expanded[0]) && expanded[1] == L':') || > > + expanded[0] == L'\\') { > > + return g_steal_pointer(&expanded); > > + } > > + > > + path_len = wcslen(expanded); > > + start = expanded; > > + while (*start == L'\\' || *start == L'/') { > > + start++; > > + path_len--; > > + } > > + { > > + WCHAR *ret = g_new(WCHAR, windows_len + path_len + 2); > > + > > + memcpy(ret, windows_dir, windows_len * sizeof(WCHAR)); > > + ret[windows_len] = L'\\'; > > + wcscpy(ret + windows_len + 1, start); > > + return ret; > > + } > > +} > > + > > +typedef struct QGAVersionTranslation { > > + WORD language; > > + WORD code_page; > > +} QGAVersionTranslation; > > + > > +/* Return CompanyName from a localized file-version string table. */ > > +static char *ga_get_file_vendor(const void *version_info) > > +{ > > + QGAVersionTranslation *translations = NULL; > > + static const QGAVersionTranslation fallback = { 0x0409, 0x04b0 }; > > + UINT translations_len = 0; > > + UINT count = 0; > > + UINT i; > > + > > + if (VerQueryValueW(version_info, L"\\VarFileInfo\\Translation", > > + (void **)&translations, &translations_len) && > > + translations != NULL) { > > + count = translations_len / sizeof(*translations); > > + } > > + > > + for (i = 0; i <= count; i++) { > > + const QGAVersionTranslation *translation; > > + WCHAR query[64]; > > + WCHAR *company = NULL; > > + UINT company_len = 0; > > + char *vendor; > > + > > + translation = i < count ? &translations[i] : &fallback; > > + swprintf(query, ARRAY_SIZE(query), > > + L"\\StringFileInfo\\%04x%04x\\CompanyName", > > + translation->language, translation->code_page); > > + if (!VerQueryValueW(version_info, query, (void **)&company, > > + &company_len) || company_len <= 1) { > > + continue; > > + } > > + vendor = g_utf16_to_utf8(company, -1, NULL, NULL, NULL); > > + if (vendor != NULL && vendor[0] != '\0') { > > + return vendor; > > + } > > + g_free(vendor); > > + } > > + return NULL; > > +} > > + > > +/* Add optional FileVersion and CompanyName data from a driver binary. > */ > > +static bool ga_get_driver_file_metadata(const WCHAR *path, > > + GuestDriverService *service, > > + Error **errp) > > +{ > > + g_autofree void *version_info = NULL; > > + VS_FIXEDFILEINFO *fixed_info = NULL; > > + PVOID old_redirection = NULL; > > + DWORD ignored; > > + DWORD version_size; > > + UINT fixed_info_len; > > + bool redirection_disabled; > > + bool version_loaded = false; > > + > > + redirection_disabled = > > + Wow64DisableWow64FsRedirection(&old_redirection); > > + > > + version_size = GetFileVersionInfoSizeW(path, &ignored); > > + if (version_size != 0) { > > + version_info = g_malloc(version_size); > > + version_loaded = GetFileVersionInfoW(path, 0, version_size, > > + version_info); > > + } > > + > > + if (redirection_disabled && > > + !Wow64RevertWow64FsRedirection(old_redirection)) { > > + error_setg_win32(errp, GetLastError(), > > + "failed to restore WOW64 filesystem > redirection " > > + "for driver service '%s'", service->name); > > + return false; > > + } > > + > > + if (!version_loaded) { > > + return true; > > + } > > + > > + if (VerQueryValueW(version_info, L"\\", (void **)&fixed_info, > > + &fixed_info_len) && > > + fixed_info_len >= sizeof(*fixed_info) && > > + fixed_info->dwSignature == VS_FFI_SIGNATURE) { > > + service->version = g_strdup_printf( > > + "%u.%u.%u.%u", > > + HIWORD(fixed_info->dwFileVersionMS), > > + LOWORD(fixed_info->dwFileVersionMS), > > + HIWORD(fixed_info->dwFileVersionLS), > > + LOWORD(fixed_info->dwFileVersionLS)); > > + } > > + > > + service->vendor = ga_get_file_vendor(version_info); > > + return true; > > +} > > + > > +/* > > + * Add optional configuration and binary metadata for one driver > service. > > + * Ordinary per-service lookup failures leave optional fields unset. > > + */ > > +static bool ga_get_driver_service_config(SC_HANDLE manager, > > + const WCHAR *name, > > + GuestDriverService *service, > > + Error **errp) > > +{ > > + SC_HANDLE handle; > > + g_autofree QUERY_SERVICE_CONFIGW *config = NULL; > > + g_autofree WCHAR *resolved_path = NULL; > > + DWORD size = 0; > > + DWORD err; > > + bool success = true; > > + > > + handle = OpenServiceW(manager, name, SERVICE_QUERY_CONFIG); > > + if (handle == NULL) { > > + slog("failed to open configuration for driver service '%s', " > > + "error=%lu", service->name, GetLastError()); > > + return true; > > + } > > + > > + QueryServiceConfigW(handle, NULL, 0, &size); > > + err = GetLastError(); > > + if (err != ERROR_INSUFFICIENT_BUFFER) { > > + slog("failed to get configuration size for driver service '%s', > " > > + "error=%lu", service->name, err); > > + goto out; > > + } > > + > > + config = g_malloc(size); > > + if (!QueryServiceConfigW(handle, config, size, &size)) { > > + slog("failed to get configuration for driver service '%s', " > > + "error=%lu", service->name, GetLastError()); > > + goto out; > > + } > > + > > + if (config->lpBinaryPathName == NULL || > > + config->lpBinaryPathName[0] == L'\0') { > > + goto out; > > + } > > + > > + service->driver_path = g_utf16_to_utf8(config->lpBinaryPathName, -1, > > + NULL, NULL, NULL); > > + > > + resolved_path = ga_resolve_driver_path(config->lpBinaryPathName); > > + if (resolved_path != NULL && > > + !ga_get_driver_file_metadata(resolved_path, service, errp)) { > > + success = false; > > + } > > + > > +out: > > + CloseServiceHandle(handle); > > + return success; > > +} > > + > > +static GuestDriverServiceList *ga_get_driver_services(Error **errp) > > +{ > > + GuestDriverServiceList *head = NULL, **tail = &head; > > + g_autofree BYTE *buffer = NULL; > > + SC_HANDLE manager = NULL; > > + DWORD buffer_size = 256 * 1024; > > + DWORD bytes_needed; > > + DWORD services_returned; > > + DWORD resume = 0; > > + DWORD err; > > + bool complete; > > + bool success = false; > > + > > + manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); > > + if (manager == NULL) { > > + error_setg_win32(errp, GetLastError(), > > + "failed to open service control manager"); > > + goto out; > > + } > > + > > + buffer = g_malloc(buffer_size); > > + do { > > + ENUM_SERVICE_STATUS_PROCESSW *services; > > + DWORD i; > > + > > + bytes_needed = 0; > > + services_returned = 0; > > + complete = EnumServicesStatusExW(manager, SC_ENUM_PROCESS_INFO, > > + SERVICE_DRIVER, > SERVICE_STATE_ALL, > > + buffer, buffer_size, > &bytes_needed, > > + &services_returned, &resume, > NULL); > > + if (!complete) { > > + err = GetLastError(); > > + if (err != ERROR_MORE_DATA) { > > + error_setg_win32(errp, err, > > + "failed to enumerate driver services"); > > + goto out; > > + } > > + if (services_returned == 0) { > > + error_setg(errp, "driver service enumeration made no " > > + "progress (buffer needs %lu bytes)", > bytes_needed); > > + goto out; > > + } > > + } > > + > > + services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer; > > + for (i = 0; i < services_returned; i++) { > > + g_autoptr(GuestDriverService) service = NULL; > > + > > + service = g_new0(GuestDriverService, 1); > > + service->name = ga_utf16_to_utf8_required( > > + services[i].lpServiceName, "service name", errp); > > + if (service->name == NULL) { > > + goto out; > > + } > > + if (!ga_driver_service_status( > > + services[i].ServiceStatusProcess.dwCurrentState, > > + &service->status)) { > > + error_setg(errp, "driver service '%s' has unknown state > %lu", > > + service->name, > > + > services[i].ServiceStatusProcess.dwCurrentState); > > + goto out; > > + } > > An unknown dwCurrentState aborts the entire service enumeration. The states > listed in GuestDriverServiceStatus cover all documented SCM values today, > but a > future Windows version could add new states. > > Consider skipping the service with a slog warning instead, or adding an > "unknown" enum value to GuestDriverServiceStatus. > > > + > > + if (!ga_get_driver_service_config(manager, > > + services[i].lpServiceName, > > + service, errp)) { > > + goto out; > > + } > > + QAPI_LIST_APPEND(tail, g_steal_pointer(&service)); > > + } > > + } while (!complete); > > + success = true; > > + > > +out: > > + if (manager != NULL) { > > + CloseServiceHandle(manager); > > + } > > + if (!success) { > > + qapi_free_GuestDriverServiceList(head); > > + head = NULL; > > + } > > + return head; > > +} > > + > > +GuestDriverInfo *qmp_guest_get_drivers(Error **errp) > > +{ > > + g_autoptr(GuestDriverInfo) info = g_new0(GuestDriverInfo, 1); > > + Error *local_err = NULL; > > + > > + info->packages = ga_get_driver_packages(&local_err); > > + if (local_err) { > > + error_propagate(errp, local_err); > > + return NULL; > > + } > > If DismApi.dll is missing or DismInitialize fails, it returns an error, > even if > listing services could work. That's probably ok, but worth noting. > what if we add an "error" field to the package structure, then we can return services and package with an error about DismInitialize > > > + > > + info->services = ga_get_driver_services(&local_err); > > + if (local_err) { > > + error_propagate(errp, local_err); > > + return NULL; > > + } > > + > > + return g_steal_pointer(&info); > > +} > > + > > char *qga_get_host_name(Error **errp) > > { > > wchar_t tmp[MAX_COMPUTERNAME_LENGTH + 1]; > > diff --git a/qga/meson.build b/qga/meson.build > > index cfa2157efb23..15860d938d4b 100644 > > --- a/qga/meson.build > > +++ b/qga/meson.build > > @@ -94,8 +94,9 @@ qga_ss = qga_ss.apply({}) > > gen_tlb = [] > > qga_libs = [] > > if host_os == 'windows' > > - qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', > '-lwininet', '-liphlpapi', '-lnetapi32', > > - '-lsetupapi', '-lcfgmgr32', '-luserenv', '-lpdh' ] > > + qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', > > + '-lwininet', '-liphlpapi', '-lnetapi32', '-lsetupapi', > > + '-lcfgmgr32', '-luserenv', '-lpdh', '-lversion' ] > > if have_qga_vss > > qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', > '-Wl,--enable-stdcall-fixup'] > > subdir('vss-win32') > > diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json > > index 8cd0ba048b8d..b20cf7135a4c 100644 > > --- a/qga/qapi-schema.json > > +++ b/qga/qapi-schema.json > > @@ -1590,6 +1590,95 @@ > > 'returns': ['GuestDeviceInfo'], > > 'if': 'CONFIG_WIN32' } > > > > +## > > +# @GuestDriverServiceStatus: > > +# > > +# @stopped: the service is stopped > > +# > > +# @start-pending: the service is starting > > +# > > +# @stop-pending: the service is stopping > > +# > > +# @running: the service is running > > +# > > +# @continue-pending: the service is resuming > > +# > > +# @pause-pending: the service is pausing > > +# > > +# @paused: the service is paused > > +# > > +# Since: 11.2 > > +## > > +{ 'enum': 'GuestDriverServiceStatus', > > + 'data': [ 'stopped', 'start-pending', 'stop-pending', 'running', > > + 'continue-pending', 'pause-pending', 'paused' ], > > + 'if': 'CONFIG_WIN32' } > > + > > +## > > +# @GuestDriverPackage: > > +# > > +# An out-of-box driver package installed in the guest. > > +# > > +# @name: published INF name > > +# > > +# @original-name: base name of the original INF file > > +# > > +# @version: four-part version from the package INF DriverVer entry > > +# > > +# @vendor: package provider from the INF metadata > > +# > > +# Since: 11.2 > > +## > > +{ 'struct': 'GuestDriverPackage', > > + 'data': { 'name': 'str', '*original-name': 'str', 'version': 'str', > > + '*vendor': 'str' }, > > + 'if': 'CONFIG_WIN32' } > > + > > +## > > +# @GuestDriverService: > > +# > > +# @name: service name > > +# > > +# @driver-path: driver binary path configured in the service database > > +# > > +# @version: four-part FileVersion from the driver binary > > +# > > +# @vendor: CompanyName from the driver binary > > +# > > +# @status: current service status > > +# > > +# Since: 11.2 > > +## > > +{ 'struct': 'GuestDriverService', > > + 'data': { 'name': 'str', '*driver-path': 'str', '*version': 'str', > > + '*vendor': 'str', 'status': 'GuestDriverServiceStatus' }, > > + 'if': 'CONFIG_WIN32' } > > + > > +## > > +# @GuestDriverInfo: > > +# > > +# @packages: out-of-box driver packages installed in the guest > > +# > > +# @services: registered driver services > > +# > > +# Since: 11.2 > > +## > > +{ 'struct': 'GuestDriverInfo', > > + 'data': { 'packages': ['GuestDriverPackage'], > > + 'services': ['GuestDriverService'] }, > > + 'if': 'CONFIG_WIN32' } > > + > > +## > > +# @guest-get-drivers: > > +# > > +# Retrieve information about Windows driver packages and services. > > +# > > +# Since: 11.2 > > +## > > +{ 'command': 'guest-get-drivers', > > + 'returns': 'GuestDriverInfo', > > + 'if': 'CONFIG_WIN32' } > > + > > ## > > # @GuestAuthorizedKeys: > > # > > otherwise, lgtm > > -- > Marc-André Lureau <[email protected]> > >
