diff --git a/cmd/dumpvms/main.go b/cmd/dumpvms/main.go index 355bed0..77dac1c 100644 --- a/cmd/dumpvms/main.go +++ b/cmd/dumpvms/main.go @@ -20,7 +20,7 @@ func getVms() (string, error) { vmms := hypervctl.VirtualMachineManager{} vms, err := vmms.GetAll() if err != nil { - return "", fmt.Errorf("Could not retrieve virtual machines : %s\n", err.Error()) + return "", fmt.Errorf("Could not retrieve virtual machines: %s\n", err.Error()) } b, err := json.MarshalIndent(vms, "", "\t") if err != nil { diff --git a/pkg/hypervctl/diskdrive_settings.go b/pkg/hypervctl/diskdrive_settings.go index c493b1a..5e66930 100644 --- a/pkg/hypervctl/diskdrive_settings.go +++ b/pkg/hypervctl/diskdrive_settings.go @@ -43,7 +43,7 @@ func (d *SyntheticDiskDriveSettings) DefineVirtualHardDisk(vhdxFile string, befo func createDiskResourceInternal(systemPath string, drivePath string, file string, settings diskAssociation, resourceType string, cb func()) error { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() diff --git a/pkg/hypervctl/error.go b/pkg/hypervctl/error.go index 7fe985d..2f16971 100644 --- a/pkg/hypervctl/error.go +++ b/pkg/hypervctl/error.go @@ -6,6 +6,8 @@ package hypervctl import ( "errors" "fmt" + + "github.com/containers/libhvee/pkg/wmiext" ) // VM State errors @@ -155,3 +157,18 @@ func translateModifyError(code int) error { return &modifyResourceError{code, message} } + +var ( + ErrHyperVNamespaceMissing = errors.New("HyperV namespace not found, is HyperV enabled?") +) + +func translateCommonHyperVWmiError(wmiError error) error { + if werr, ok := wmiError.(*wmiext.WmiError); ok { + switch werr.Code() { + case wmiext.WBEM_E_INVALID_NAMESPACE: + return ErrHyperVNamespaceMissing + } + } + + return wmiError +} diff --git a/pkg/hypervctl/ethernet_port_settings.go b/pkg/hypervctl/ethernet_port_settings.go index f1d8930..294d87e 100644 --- a/pkg/hypervctl/ethernet_port_settings.go +++ b/pkg/hypervctl/ethernet_port_settings.go @@ -68,7 +68,7 @@ func (p *SyntheticEthernetPortSettings) DefineEthernetPortConnection(switchName var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return nil, err } defer service.Close() diff --git a/pkg/hypervctl/resources_settings.go b/pkg/hypervctl/resources_settings.go index c229121..eda4b25 100644 --- a/pkg/hypervctl/resources_settings.go +++ b/pkg/hypervctl/resources_settings.go @@ -53,7 +53,7 @@ func (s *ResourceSettings) Path() string { func createResourceSettingGeneric(settings interface{}, resourceType string) (string, error) { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return "", err } @@ -84,7 +84,7 @@ func createResourceSettingGeneric(settings interface{}, resourceType string) (st func populateDefaults(subType string, settings interface{}) error { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() diff --git a/pkg/hypervctl/scsi_controller.go b/pkg/hypervctl/scsi_controller.go index b9a2e2c..d38bfac 100644 --- a/pkg/hypervctl/scsi_controller.go +++ b/pkg/hypervctl/scsi_controller.go @@ -42,7 +42,7 @@ func (c *ScsiControllerSettings) AddSyntheticDvdDrive(slot uint) (*SyntheticDvdD func (c *ScsiControllerSettings) createSyntheticDriveInternal(slot uint, settings driveAssociation, resourceType string) error { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() diff --git a/pkg/hypervctl/system_settings.go b/pkg/hypervctl/system_settings.go index 527a857..cfb4207 100644 --- a/pkg/hypervctl/system_settings.go +++ b/pkg/hypervctl/system_settings.go @@ -106,7 +106,7 @@ func (s *SystemSettings) AddScsiController() (*ScsiControllerSettings, error) { func (s *SystemSettings) createSystemResourceInternal(settings interface{}, resourceType string, cb func()) error { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() @@ -186,7 +186,7 @@ func addResource(service *wmiext.Service, systemSettingPath string, resourceSett func (s *SystemSettings) GetVM() (*VirtualMachine, error) { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return nil, err } defer service.Close() diff --git a/pkg/hypervctl/system_settings_builder.go b/pkg/hypervctl/system_settings_builder.go index 119ba9e..76b5995 100644 --- a/pkg/hypervctl/system_settings_builder.go +++ b/pkg/hypervctl/system_settings_builder.go @@ -90,7 +90,7 @@ func (builder *SystemSettingsBuilder) Build() (*SystemSettings, error) { return nil, err } - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return nil, err } defer service.Close() diff --git a/pkg/hypervctl/vm.go b/pkg/hypervctl/vm.go index e610990..fa0b612 100644 --- a/pkg/hypervctl/vm.go +++ b/pkg/hypervctl/vm.go @@ -114,7 +114,7 @@ func (vm *VirtualMachine) GetKeyValuePairs() (map[string]string, error) { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return nil, err } @@ -153,7 +153,7 @@ func (vm *VirtualMachine) kvpOperation(op string, key string, value string, ille var vsms, job *wmiext.Instance var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() @@ -226,7 +226,7 @@ func (vm *VirtualMachine) stop(force bool) error { res int32 srv *wmiext.Service ) - if srv, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if srv, err = NewLocalHyperVService(); err != nil { return err } wmiInst, err := srv.FindFirstRelatedInstance(vm.Path(), "Msvm_ShutdownComponent") @@ -303,7 +303,7 @@ func (vm *VirtualMachine) Start() error { func getService(_ *wmiext.Service) (*wmiext.Service, error) { // any reason why when we instantiate a vm, we should NOT just embed a service? - return wmiext.NewLocalService(HyperVNamespace) + return NewLocalHyperVService() } func (vm *VirtualMachine) GetConfig(diskPath string) (*HyperVConfig, error) { @@ -350,7 +350,7 @@ func (vm *VirtualMachine) GetConfig(diskPath string) (*HyperVConfig, error) { // SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this // parameter func (vm *VirtualMachine) GetSummaryInformation(requestedFields SummaryRequestSet) (*SummaryInformation, error) { - service, err := wmiext.NewLocalService(HyperVNamespace) + service, err := NewLocalHyperVService() if err != nil { return nil, err } @@ -469,7 +469,7 @@ func (vm *VirtualMachine) fetchExistingResourceSettings(service *wmiext.Service, } func (vm *VirtualMachine) getMemorySettings(m *MemorySettings) error { - service, err := wmiext.NewLocalService(HyperVNamespace) + service, err := NewLocalHyperVService() if err != nil { return err } @@ -479,7 +479,7 @@ func (vm *VirtualMachine) getMemorySettings(m *MemorySettings) error { // Update processor and/or mem func (vm *VirtualMachine) UpdateProcessorMemSettings(updateProcessor func(*ProcessorSettings), updateMemory func(*MemorySettings)) error { - service, err := wmiext.NewLocalService(HyperVNamespace) + service, err := NewLocalHyperVService() if err != nil { return err } @@ -556,7 +556,7 @@ func (vm *VirtualMachine) remove() (int32, error) { if !Disabled.equal(vm.EnabledState) { return -1, ErrMachineStateInvalid } - if srv, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if srv, err = NewLocalHyperVService(); err != nil { return -1, err } diff --git a/pkg/hypervctl/vmm.go b/pkg/hypervctl/vmm.go index fee021e..489c575 100644 --- a/pkg/hypervctl/vmm.go +++ b/pkg/hypervctl/vmm.go @@ -12,6 +12,7 @@ import ( const ( HyperVNamespace = "root\\virtualization\\v2" VirtualSystemManagementService = "Msvm_VirtualSystemManagementService" + MsvmComputerSystem = "Msvm_ComputerSystem" ) // https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-computersystem @@ -23,12 +24,22 @@ func NewVirtualMachineManager() *VirtualMachineManager { return &VirtualMachineManager{} } +func NewLocalHyperVService() (*wmiext.Service, error) { + service, err := wmiext.NewLocalService(HyperVNamespace) + if err != nil { + return nil, translateCommonHyperVWmiError(err) + } + + return service, nil +} + func (vmm *VirtualMachineManager) GetAll() ([]*VirtualMachine, error) { - const wql = "Select * From Msvm_ComputerSystem Where Description = 'Microsoft Virtual Machine'" + // Fetch through settings to avoid locale sensitive properties + const wql = "Select * From Msvm_VirtualSystemSettingData Where VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'" var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return []*VirtualMachine{}, err } defer service.Close() @@ -38,22 +49,31 @@ func (vmm *VirtualMachineManager) GetAll() ([]*VirtualMachine, error) { return nil, err } defer enum.Close() - var vms []*VirtualMachine + for { - vm := &VirtualMachine{vmm: vmm} - done, err := wmiext.NextObject(enum, vm) + settings, err := enum.Next() if err != nil { return vms, err } - if done { + + // Finished iterating + if settings == nil { break } + + vm, err := vmm.findVMFromSettings(service, settings) + settings.Close() + if err != nil { + return vms, err + } + vms = append(vms, vm) } return vms, nil } + func (vmm *VirtualMachineManager) Exists(name string) (bool, error) { vms, err := vmm.GetAll() if err != nil { @@ -68,14 +88,14 @@ func (vmm *VirtualMachineManager) Exists(name string) (bool, error) { return false, nil } -func (*VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) { - const wql = "Select * From Msvm_ComputerSystem Where Description = 'Microsoft Virtual Machine' And ElementName='%s'" +func (vmm *VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) { + const wql = "Select * From Msvm_VirtualSystemSettingData Where VirtualSystemType = 'Microsoft:Hyper-V:System:Realized' And ElementName='%s'" vm := &VirtualMachine{} var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return vm, err } defer service.Close() @@ -86,22 +106,31 @@ func (*VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) { } defer enum.Close() - done, err := wmiext.NextObject(enum, vm) + settings, err := service.FindFirstInstance(fmt.Sprintf(wql, name)) if err != nil { - return vm, err + return vm, fmt.Errorf("could not find virtual machine %q: %w", name, err) } + defer settings.Close() - if done { - return vm, fmt.Errorf("could not find virtual machine %q", name) + return vmm.findVMFromSettings(service, settings) +} + +func (vmm *VirtualMachineManager) findVMFromSettings(service *wmiext.Service, settings *wmiext.Instance) (*VirtualMachine, error) { + path, err := settings.Path() + if err != nil { + return nil, err } - return vm, nil + vm := &VirtualMachine{vmm: vmm} + err = service.FindFirstRelatedObject(path, MsvmComputerSystem, vm) + + return vm, err } func (*VirtualMachineManager) CreateVhdxFile(path string, maxSize uint64) error { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return err } defer service.Close() @@ -152,7 +181,7 @@ func (vmm *VirtualMachineManager) GetSummaryInformation(requestedFields SummaryR func (vmm *VirtualMachineManager) getSummaryInformation(settingsPath string, requestedFields SummaryRequestSet) ([]SummaryInformation, error) { var service *wmiext.Service var err error - if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil { + if service, err = NewLocalHyperVService(); err != nil { return nil, err } defer service.Close() diff --git a/pkg/wmiext/array.go b/pkg/wmiext/array.go index 2b92dc8..3f05936 100644 --- a/pkg/wmiext/array.go +++ b/pkg/wmiext/array.go @@ -113,7 +113,7 @@ func safeArrayDestroy(safearray *ole.SafeArray) (err error) { ret, _, _ := procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray))) if ret != 0 { - return ole.NewError(ret) + return NewWmiError(ret) } return nil @@ -127,7 +127,7 @@ func safeArrayPutElement(safearray *ole.SafeArray, index int64, element uintptr) element) if ret != 0 { - return ole.NewError(ret) + return NewWmiError(ret) } return nil @@ -141,7 +141,7 @@ func safeArrayGetElement(safearray *ole.SafeArray, index int64, element unsafe.P uintptr(element)) if ret != 0 { - return ole.NewError(ret) + return NewWmiError(ret) } return nil diff --git a/pkg/wmiext/enum.go b/pkg/wmiext/enum.go index 7bb4d8d..2b6fd69 100644 --- a/pkg/wmiext/enum.go +++ b/pkg/wmiext/enum.go @@ -77,7 +77,7 @@ func (e *Enum) Next() (instance *Instance, err error) { uintptr(unsafe.Pointer(&apObjects)), // [out] IWbemClassObject **apObjects, uintptr(unsafe.Pointer(&uReturned))) // [out] ULONG *puReturned) if int(res) < 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } if uReturned < 1 { diff --git a/pkg/wmiext/error.go b/pkg/wmiext/error.go new file mode 100644 index 0000000..9956c58 --- /dev/null +++ b/pkg/wmiext/error.go @@ -0,0 +1,196 @@ +package wmiext + +import ( + "fmt" + "os" + "strings" + "syscall" + "unicode/utf16" + + "golang.org/x/sys/windows" +) + +const ( + WBEM_NO_ERROR = 0 + WBEM_S_NO_ERROR = 0 + WBEM_S_SAME = 0 + WBEM_S_FALSE = 1 + WBEM_S_ALREADY_EXISTS = 0x40001 + WBEM_S_RESET_TO_DEFAULT = 0x40002 + WBEM_S_DIFFERENT = 0x40003 + WBEM_S_TIMEDOUT = 0x40004 + WBEM_S_NO_MORE_DATA = 0x40005 + WBEM_S_OPERATION_CANCELLED = 0x40006 + WBEM_S_PENDING = 0x40007 + WBEM_S_DUPLICATE_OBJECTS = 0x40008 + WBEM_S_ACCESS_DENIED = 0x40009 + WBEM_S_PARTIAL_RESULTS = 0x40010 + WBEM_S_SOURCE_NOT_AVAILABLE = 0x40017 + WBEM_E_FAILED = 0x80041001 + WBEM_E_NOT_FOUND = 0x80041002 + WBEM_E_ACCESS_DENIED = 0x80041003 + WBEM_E_PROVIDER_FAILURE = 0x80041004 + WBEM_E_TYPE_MISMATCH = 0x80041005 + WBEM_E_OUT_OF_MEMORY = 0x80041006 + WBEM_E_INVALID_CONTEXT = 0x80041007 + WBEM_E_INVALID_PARAMETER = 0x80041008 + WBEM_E_NOT_AVAILABLE = 0x80041009 + WBEM_E_CRITICAL_ERROR = 0x8004100a + WBEM_E_INVALID_STREAM = 0x8004100b + WBEM_E_NOT_SUPPORTED = 0x8004100c + WBEM_E_INVALID_SUPERCLASS = 0x8004100d + WBEM_E_INVALID_NAMESPACE = 0x8004100e + WBEM_E_INVALID_OBJECT = 0x8004100f + WBEM_E_INVALID_CLASS = 0x80041010 + WBEM_E_PROVIDER_NOT_FOUND = 0x80041011 + WBEM_E_INVALID_PROVIDER_REGISTRATION = 0x80041012 + WBEM_E_PROVIDER_LOAD_FAILURE = 0x80041013 + WBEM_E_INITIALIZATION_FAILURE = 0x80041014 + WBEM_E_TRANSPORT_FAILURE = 0x80041015 + WBEM_E_INVALID_OPERATION = 0x80041016 + WBEM_E_INVALID_QUERY = 0x80041017 + WBEM_E_INVALID_QUERY_TYPE = 0x80041018 + WBEM_E_ALREADY_EXISTS = 0x80041019 + WBEM_E_OVERRIDE_NOT_ALLOWED = 0x8004101a + WBEM_E_PROPAGATED_QUALIFIER = 0x8004101b + WBEM_E_PROPAGATED_PROPERTY = 0x8004101c + WBEM_E_UNEXPECTED = 0x8004101d + WBEM_E_ILLEGAL_OPERATION = 0x8004101e + WBEM_E_CANNOT_BE_KEY = 0x8004101f + WBEM_E_INCOMPLETE_CLASS = 0x80041020 + WBEM_E_INVALID_SYNTAX = 0x80041021 + WBEM_E_NONDECORATED_OBJECT = 0x80041022 + WBEM_E_READ_ONLY = 0x80041023 + WBEM_E_PROVIDER_NOT_CAPABLE = 0x80041024 + WBEM_E_CLASS_HAS_CHILDREN = 0x80041025 + WBEM_E_CLASS_HAS_INSTANCES = 0x80041026 + WBEM_E_QUERY_NOT_IMPLEMENTED = 0x80041027 + WBEM_E_ILLEGAL_NULL = 0x80041028 + WBEM_E_INVALID_QUALIFIER_TYPE = 0x80041029 + WBEM_E_INVALID_PROPERTY_TYPE = 0x8004102a + WBEM_E_VALUE_OUT_OF_RANGE = 0x8004102b + WBEM_E_CANNOT_BE_SINGLETON = 0x8004102c + WBEM_E_INVALID_CIM_TYPE = 0x8004102d + WBEM_E_INVALID_METHOD = 0x8004102e + WBEM_E_INVALID_METHOD_PARAMETERS = 0x8004102f + WBEM_E_SYSTEM_PROPERTY = 0x80041030 + WBEM_E_INVALID_PROPERTY = 0x80041031 + WBEM_E_CALL_CANCELLED = 0x80041032 + WBEM_E_SHUTTING_DOWN = 0x80041033 + WBEM_E_PROPAGATED_METHOD = 0x80041034 + WBEM_E_UNSUPPORTED_PARAMETER = 0x80041035 + WBEM_E_MISSING_PARAMETER_ID = 0x80041036 + WBEM_E_INVALID_PARAMETER_ID = 0x80041037 + WBEM_E_NONCONSECUTIVE_PARAMETER_IDS = 0x80041038 + WBEM_E_PARAMETER_ID_ON_RETVAL = 0x80041039 + WBEM_E_INVALID_OBJECT_PATH = 0x8004103a + WBEM_E_OUT_OF_DISK_SPACE = 0x8004103b + WBEM_E_BUFFER_TOO_SMALL = 0x8004103c + WBEM_E_UNSUPPORTED_PUT_EXTENSION = 0x8004103d + WBEM_E_UNKNOWN_OBJECT_TYPE = 0x8004103e + WBEM_E_UNKNOWN_PACKET_TYPE = 0x8004103f + WBEM_E_MARSHAL_VERSION_MISMATCH = 0x80041040 + WBEM_E_MARSHAL_INVALID_SIGNATURE = 0x80041041 + WBEM_E_INVALID_QUALIFIER = 0x80041042 + WBEM_E_INVALID_DUPLICATE_PARAMETER = 0x80041043 + WBEM_E_TOO_MUCH_DATA = 0x80041044 + WBEM_E_SERVER_TOO_BUSY = 0x80041045 + WBEM_E_INVALID_FLAVOR = 0x80041046 + WBEM_E_CIRCULAR_REFERENCE = 0x80041047 + WBEM_E_UNSUPPORTED_CLASS_UPDATE = 0x80041048 + WBEM_E_CANNOT_CHANGE_KEY_INHERITANCE = 0x80041049 + WBEM_E_CANNOT_CHANGE_INDEX_INHERITANCE = 0x80041050 + WBEM_E_TOO_MANY_PROPERTIES = 0x80041051 + WBEM_E_UPDATE_TYPE_MISMATCH = 0x80041052 + WBEM_E_UPDATE_OVERRIDE_NOT_ALLOWED = 0x80041053 + WBEM_E_UPDATE_PROPAGATED_METHOD = 0x80041054 + WBEM_E_METHOD_NOT_IMPLEMENTED = 0x80041055 + WBEM_E_METHOD_DISABLED = 0x80041056 + WBEM_E_REFRESHER_BUSY = 0x80041057 + WBEM_E_UNPARSABLE_QUERY = 0x80041058 + WBEM_E_NOT_EVENT_CLASS = 0x80041059 + WBEM_E_MISSING_GROUP_WITHIN = 0x8004105a + WBEM_E_MISSING_AGGREGATION_LIST = 0x8004105b + WBEM_E_PROPERTY_NOT_AN_OBJECT = 0x8004105c + WBEM_E_AGGREGATING_BY_OBJECT = 0x8004105d + WBEM_E_UNINTERPRETABLE_PROVIDER_QUERY = 0x8004105f + WBEM_E_BACKUP_RESTORE_WINMGMT_RUNNING = 0x80041060 + WBEM_E_QUEUE_OVERFLOW = 0x80041061 + WBEM_E_PRIVILEGE_NOT_HELD = 0x80041062 + WBEM_E_INVALID_OPERATOR = 0x80041063 + WBEM_E_LOCAL_CREDENTIALS = 0x80041064 + WBEM_E_CANNOT_BE_ABSTRACT = 0x80041065 + WBEM_E_AMENDED_OBJECT = 0x80041066 + WBEM_E_CLIENT_TOO_SLOW = 0x80041067 + WBEM_E_NULL_SECURITY_DESCRIPTOR = 0x80041068 + WBEM_E_TIMED_OUT = 0x80041069 + WBEM_E_INVALID_ASSOCIATION = 0x8004106a + WBEM_E_AMBIGUOUS_OPERATION = 0x8004106b + WBEM_E_QUOTA_VIOLATION = 0x8004106c + WBEM_E_RESERVED_001 = 0x8004106d + WBEM_E_RESERVED_002 = 0x8004106e + WBEM_E_UNSUPPORTED_LOCALE = 0x8004106f + WBEM_E_HANDLE_OUT_OF_DATE = 0x80041070 + WBEM_E_CONNECTION_FAILED = 0x80041071 + WBEM_E_INVALID_HANDLE_REQUEST = 0x80041072 + WBEM_E_PROPERTY_NAME_TOO_WIDE = 0x80041073 + WBEM_E_CLASS_NAME_TOO_WIDE = 0x80041074 + WBEM_E_METHOD_NAME_TOO_WIDE = 0x80041075 + WBEM_E_QUALIFIER_NAME_TOO_WIDE = 0x80041076 + WBEM_E_RERUN_COMMAND = 0x80041077 + WBEM_E_DATABASE_VER_MISMATCH = 0x80041078 + WBEM_E_VETO_DELETE = 0x80041079 + WBEM_E_VETO_PUT = 0x8004107a + WBEM_E_INVALID_LOCALE = 0x80041080 + WBEM_E_PROVIDER_SUSPENDED = 0x80041081 + WBEM_E_SYNCHRONIZATION_REQUIRED = 0x80041082 + WBEM_E_NO_SCHEMA = 0x80041083 + WBEM_E_PROVIDER_ALREADY_REGISTERED = 0x80041084 + WBEM_E_PROVIDER_NOT_REGISTERED = 0x80041085 + WBEM_E_FATAL_TRANSPORT_ERROR = 0x80041086 + WBEM_E_ENCRYPTED_CONNECTION_REQUIRED = 0x80041087 + WBEM_E_PROVIDER_TIMED_OUT = 0x80041088 + WBEM_E_NO_KEY = 0x80041089 + WBEM_E_PROVIDER_DISABLED = 0x8004108a +) + +var ( + wmiModule syscall.Handle +) + +func init() { + file := os.ExpandEnv("${windir}\\system32\\wbem\\wmiutils.dll") + wmiModule, _ = syscall.LoadLibrary(file) +} + +type WmiError struct { + hres uintptr +} + +func NewWmiError(hres uintptr) *WmiError { + return &WmiError{hres} +} + +func (w *WmiError) String() string { + return w.Error() +} + +func (w *WmiError) Code() uintptr { + return w.hres +} + +func (w *WmiError) Error() string { + // ask windows for the remaining errors + var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | + syscall.FORMAT_MESSAGE_FROM_HMODULE | + syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | + syscall.FORMAT_MESSAGE_IGNORE_INSERTS + + buf := make([]uint16, 300) + n, err := windows.FormatMessage(flags, uintptr(wmiModule), uint32(w.hres), 0, buf, nil) + if err != nil { + return fmt.Sprintf("WMI error [%d]: FormatMessage failed with: %v", w.hres, err) + } + + return fmt.Sprintf("WMI error [%d]: %s", w.hres, strings.TrimRight(string(utf16.Decode(buf[:n])), "\r\n")) +} diff --git a/pkg/wmiext/init.go b/pkg/wmiext/init.go index 5951733..35c5b82 100644 --- a/pkg/wmiext/init.go +++ b/pkg/wmiext/init.go @@ -31,11 +31,6 @@ var ( ) const ( - // WMI HRESULT values - WBEM_S_NO_ERROR = 0 - WBEM_S_FALSE = 1 - WBEM_S_NO_MORE_DATA = 0x40005 - // WMI Generic flags WBEM_FLAG_RETURN_WBEM_COMPLETE = 0x0 WBEM_FLAG_RETURN_IMMEDIATELY = 0x10 @@ -108,6 +103,6 @@ func initSecurity() { uintptr(EOAC_NONE), // [in] DWORD dwCapabilities, uintptr(0)) // [in, optional] void *pReserved3 if int(res) < 0 { - logrus.Errorf("Unable to initialize COM security: %s", ole.NewError(res).Error()) + logrus.Errorf("Unable to initialize COM security: %s", NewWmiError(res).Error()) } } diff --git a/pkg/wmiext/instance.go b/pkg/wmiext/instance.go index fdd18dd..b60d636 100644 --- a/pkg/wmiext/instance.go +++ b/pkg/wmiext/instance.go @@ -151,7 +151,7 @@ func (i *Instance) SpawnInstance() (instance *Instance, err error) { uintptr(0), // [in] long lFlags, uintptr(unsafe.Pointer(&newUnknown))) // [out] IWbemClassObject **ppNewInstance) if res != 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } return newInstance(newUnknown, i.service), nil @@ -168,7 +168,7 @@ func (i *Instance) CloneInstance() (*Instance, error) { uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr uintptr(unsafe.Pointer(&cloned))) // [out] IWbemClassObject **ppCopy) if ret != 0 { - return nil, ole.NewError(ret) + return nil, NewWmiError(ret) } return newInstance(cloned, i.service), nil @@ -260,7 +260,7 @@ func (i *Instance) Put(name string, value interface{}) (err error) { uintptr(unsafe.Pointer(&variant)), // [in] VARIANT *pVal, uintptr(0)) // [in] CIMTYPE Type) if res != 0 { - return ole.NewError(res) + return NewWmiError(res) } _ = variant.Clear() @@ -444,7 +444,7 @@ func (i *Instance) GetAsVariant(name string) (*ole.VARIANT, CIMTYPE_ENUMERATION, uintptr(unsafe.Pointer(&cimType)), // [out, optional] CIMTYPE *pType, uintptr(unsafe.Pointer(&flavor))) // [out, optional] long *plFlavor) if res != 0 { - return nil, 0, 0, ole.NewError(res) + return nil, 0, 0, NewWmiError(res) } return &variant, cimType, flavor, nil @@ -490,7 +490,7 @@ func (i *Instance) NextAsVariant() (bool, string, *ole.VARIANT, CIMTYPE_ENUMERAT uintptr(unsafe.Pointer(&cimType)), // [out, optional] CIMTYPE *pType, uintptr(unsafe.Pointer(&flavor))) // [out, optional] long *plFlavor if int(res) < 0 { - return false, "", nil, cimType, flavor, ole.NewError(res) + return false, "", nil, cimType, flavor, NewWmiError(res) } if res == WBEM_S_NO_MORE_DATA { @@ -556,7 +556,7 @@ func (i *Instance) GetMethodParameters(method string) (*Instance, error) { uintptr(unsafe.Pointer(&inSignature)), // [out] IWbemClassObject **ppInSignature, uintptr(0)) // [out] IWbemClassObject **ppOutSignature) if res != 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } return newInstance(inSignature, i.service), nil @@ -612,7 +612,7 @@ func (i *Instance) BeginEnumeration() error { uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr, uintptr(0)) // [in] long lEnumFlags) // 0 = defaults if result != 0 { - return ole.NewError(result) + return NewWmiError(result) } return nil @@ -626,7 +626,7 @@ func (i *Instance) EndEnumeration() error { i.vTable.EndEnumeration, // IWbemClassObject::EndEnumeration( uintptr(unsafe.Pointer(i.object))) // IWbemClassObject ptr) if res != 0 { - return ole.NewError(res) + return NewWmiError(res) } return nil diff --git a/pkg/wmiext/service.go b/pkg/wmiext/service.go index 8824268..c173e12 100644 --- a/pkg/wmiext/service.go +++ b/pkg/wmiext/service.go @@ -90,7 +90,7 @@ func connectService(namespace string) (*Service, error) { uintptr(unsafe.Pointer(&service))) // [out] IWbemServices **ppNamespace) if res != 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } if err = CoSetProxyBlanket(service); err != nil { @@ -123,7 +123,7 @@ func CoSetProxyBlanket(service *ole.IUnknown) (err error) { uintptr(EOAC_NONE)) // [in] DWORD dwCapabilities) if res != 0 { - return ole.NewError(res) + return NewWmiError(res) } return nil @@ -169,7 +169,7 @@ func (s *Service) ExecQuery(wqlQuery string) (*Enum, error) { uintptr(0), // [in] IWbemContext *pCtx, uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum) if hres != 0 { - return nil, ole.NewError(hres) + return nil, NewWmiError(hres) } if err = CoSetProxyBlanket(pEnum); err != nil { @@ -201,7 +201,7 @@ func (s *Service) GetObject(objectPath string) (instance *Instance, err error) { uintptr(0)) // [out] IWbemCallResult **ppCallResult) if int(res) < 0 { // returns WBEM_E_PROVIDER_NOT_FOUND when no entry found - return nil, ole.NewError(res) + return nil, NewWmiError(res) } return newInstance(pObject, s), nil @@ -240,7 +240,7 @@ func (s *Service) CreateInstanceEnum(className string) (*Enum, error) { uintptr(0), // [in] IWbemContext *pCtx, uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum) if int(res) < 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } if err = CoSetProxyBlanket(pEnum); err != nil { @@ -278,7 +278,7 @@ func (s *Service) ExecMethod(className string, methodName string, inParams *Inst uintptr(unsafe.Pointer(&outParams)), // [out] IWbemClassObject **ppOutParams, uintptr(0)) // [out] IWbemCallResult **ppCallResult) if int(res) < 0 { - return nil, ole.NewError(res) + return nil, NewWmiError(res) } return newInstance(outParams, s), nil