diff --git a/go.mod b/go.mod index 570a910a05..06752f15ff 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,10 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/cavaliergopher/grab/v3 v3.0.1 github.com/cheggaaa/pb/v3 v3.1.5 + github.com/containers/common v0.59.1 github.com/containers/gvisor-tap-vsock v0.7.5 github.com/containers/image/v5 v5.32.2 + github.com/containers/libhvee v0.7.1 github.com/coreos/go-systemd/v22 v22.5.0 github.com/crc-org/admin-helper v0.5.4 github.com/crc-org/machine v0.0.0-20240715101719-0c1bc9eb95f8 @@ -96,7 +98,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-logr/logr v1.4.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.23.0 // indirect github.com/go-openapi/errors v0.22.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect diff --git a/go.sum b/go.sum index 29081bfb8c..793c4bdc8d 100644 --- a/go.sum +++ b/go.sum @@ -43,10 +43,14 @@ github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vc github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containers/common v0.59.1 h1:7VkmJN3YvD0jLFwaUjLHSRJ98JLffydiyOJjYr0dUTo= +github.com/containers/common v0.59.1/go.mod h1:53VicJCZ2AD0O+Br7VVoyrS7viXF4YmwlTIocWUT8XE= github.com/containers/gvisor-tap-vsock v0.7.5 h1:bTy4u3DOmmUPwurL6me2rsgfypAFDhyeJleUcQmBR/E= github.com/containers/gvisor-tap-vsock v0.7.5/go.mod h1:GW9jOqAEEGdaS20XwTYdm6KCYDHIulOE/yEEOabkoE4= github.com/containers/image/v5 v5.32.2 h1:SzNE2Y6sf9b1GJoC8qjCuMBXwQrACFp4p0RK15+4gmQ= github.com/containers/image/v5 v5.32.2/go.mod h1:v1l73VeMugfj/QtKI+jhYbwnwFCFnNGckvbST3rQ5Hk= +github.com/containers/libhvee v0.7.1 h1:dWGF5GLq9DZvXo3P8aDp3cNieL5eCaSell4UmeA/jY4= +github.com/containers/libhvee v0.7.1/go.mod h1:fRKB3AyIqHMvq6xaeYhTpckM2cdoq0oecolyoiuLP7M= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.2.0 h1:X14EgRK3xNFvJEfI5O4Qn4T3E25ANudSOZz/sirVuPM= @@ -118,8 +122,9 @@ github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0 github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= diff --git a/pkg/crc/machine/driver_windows.go b/pkg/crc/machine/driver_windows.go index bff7816955..0a9a6b516b 100644 --- a/pkg/crc/machine/driver_windows.go +++ b/pkg/crc/machine/driver_windows.go @@ -5,28 +5,28 @@ import ( "errors" "github.com/crc-org/crc/v2/pkg/crc/machine/config" - "github.com/crc-org/crc/v2/pkg/crc/machine/hyperv" - machineHyperv "github.com/crc-org/crc/v2/pkg/drivers/hyperv" + "github.com/crc-org/crc/v2/pkg/crc/machine/libhvee" + machineLibhvee "github.com/crc-org/crc/v2/pkg/drivers/libhvee" "github.com/crc-org/crc/v2/pkg/libmachine" "github.com/crc-org/crc/v2/pkg/libmachine/host" ) func newHost(api libmachine.API, machineConfig config.MachineConfig) (*host.Host, error) { - json, err := json.Marshal(hyperv.CreateHost(machineConfig)) + json, err := json.Marshal(libhvee.CreateHost(machineConfig)) if err != nil { return nil, errors.New("Failed to marshal driver options") } return api.NewHost("hyperv", "", json) } -func loadDriverConfig(host *host.Host) (*machineHyperv.Driver, error) { - var hypervDriver machineHyperv.Driver - err := json.Unmarshal(host.RawDriver, &hypervDriver) +func loadDriverConfig(host *host.Host) (*machineLibhvee.Driver, error) { + var libhveeDriver machineLibhvee.Driver + err := json.Unmarshal(host.RawDriver, &libhveeDriver) - return &hypervDriver, err + return &libhveeDriver, err } -func updateDriverConfig(host *host.Host, driver *machineHyperv.Driver) error { +func updateDriverConfig(host *host.Host, driver *machineLibhvee.Driver) error { driverData, err := json.Marshal(driver) if err != nil { return err @@ -34,7 +34,7 @@ func updateDriverConfig(host *host.Host, driver *machineHyperv.Driver) error { return host.UpdateConfig(driverData) } -func updateDriverStruct(host *host.Host, driver *machineHyperv.Driver) error { +func updateDriverStruct(host *host.Host, driver *machineLibhvee.Driver) error { host.Driver = driver return nil } diff --git a/pkg/crc/machine/hyperv/constants_windows.go b/pkg/crc/machine/hyperv/constants_windows.go deleted file mode 100644 index ec04f1b6ac..0000000000 --- a/pkg/crc/machine/hyperv/constants_windows.go +++ /dev/null @@ -1,6 +0,0 @@ -package hyperv - -const ( - // Alternative - AlternativeNetwork = "crc" -) diff --git a/pkg/crc/machine/hyperv/driver_windows.go b/pkg/crc/machine/libhvee/driver_windows.go similarity index 61% rename from pkg/crc/machine/hyperv/driver_windows.go rename to pkg/crc/machine/libhvee/driver_windows.go index 252dfe3b4c..9464b7ac6b 100644 --- a/pkg/crc/machine/hyperv/driver_windows.go +++ b/pkg/crc/machine/libhvee/driver_windows.go @@ -1,4 +1,4 @@ -package hyperv +package libhvee import ( "path/filepath" @@ -6,29 +6,17 @@ import ( "github.com/crc-org/crc/v2/pkg/crc/constants" "github.com/crc-org/crc/v2/pkg/crc/machine/config" - "github.com/crc-org/crc/v2/pkg/crc/network" - "github.com/crc-org/crc/v2/pkg/drivers/hyperv" - winnet "github.com/crc-org/crc/v2/pkg/os/windows/network" + "github.com/crc-org/crc/v2/pkg/drivers/libhvee" "github.com/crc-org/machine/libmachine/drivers" ) -func CreateHost(machineConfig config.MachineConfig) *hyperv.Driver { - hypervDriver := hyperv.NewDriver(machineConfig.Name, constants.MachineBaseDir) +func CreateHost(machineConfig config.MachineConfig) *libhvee.Driver { + libhveeDriver := libhvee.NewDriver(machineConfig.Name, constants.MachineBaseDir) - config.InitVMDriverFromMachineConfig(machineConfig, hypervDriver.VMDriver) + config.InitVMDriverFromMachineConfig(machineConfig, libhveeDriver.VMDriver) - hypervDriver.DisableDynamicMemory = true - - if machineConfig.NetworkMode == network.UserNetworkingMode { - hypervDriver.VirtualSwitch = "" - } else { - // Determine the Virtual Switch to be used - _, switchName := winnet.SelectSwitchByNameOrDefault(AlternativeNetwork) - hypervDriver.VirtualSwitch = switchName - } - - hypervDriver.SharedDirs = configureShareDirs(machineConfig) - return hypervDriver + libhveeDriver.SharedDirs = configureShareDirs(machineConfig) + return libhveeDriver } // converts a path like c:\users\crc to /mnt/c/users/crc diff --git a/pkg/crc/machine/hyperv/driver_windows_test.go b/pkg/crc/machine/libhvee/driver_windows_test.go similarity index 94% rename from pkg/crc/machine/hyperv/driver_windows_test.go rename to pkg/crc/machine/libhvee/driver_windows_test.go index b331204e40..e757d54799 100755 --- a/pkg/crc/machine/hyperv/driver_windows_test.go +++ b/pkg/crc/machine/libhvee/driver_windows_test.go @@ -1,4 +1,4 @@ -package hyperv +package libhvee import ( "testing" diff --git a/pkg/crc/preflight/preflight_checks_windows.go b/pkg/crc/preflight/preflight_checks_windows.go index 18fae462f5..3f61aeccce 100644 --- a/pkg/crc/preflight/preflight_checks_windows.go +++ b/pkg/crc/preflight/preflight_checks_windows.go @@ -8,11 +8,9 @@ import ( "github.com/crc-org/crc/v2/pkg/crc/logging" "github.com/crc-org/crc/v2/pkg/crc/adminhelper" - winnet "github.com/crc-org/crc/v2/pkg/os/windows/network" "github.com/crc-org/crc/v2/pkg/os/windows/powershell" "github.com/crc-org/crc/v2/pkg/crc/constants" - "github.com/crc-org/crc/v2/pkg/crc/machine/hyperv" ) const ( @@ -166,19 +164,6 @@ func fixUserPartOfCrcUsersAndHypervAdminsGroup() error { return errReboot } -func checkIfHyperVVirtualSwitchExists() error { - switchName := hyperv.AlternativeNetwork - - // use winnet instead - exists, foundName := winnet.SelectSwitchByNameOrDefault(switchName) - if exists { - logging.Info("Found Virtual Switch to use: ", foundName) - return nil - } - - return fmt.Errorf("Virtual Switch not found") -} - func checkIfRunningAsNormalUser() error { if !powershell.IsAdmin() { return nil @@ -187,17 +172,6 @@ func checkIfRunningAsNormalUser() error { return fmt.Errorf("crc should be run in a shell without administrator rights") } -func removeDNSServerAddress() error { - resetDNSCommand := `Set-DnsClientServerAddress -InterfaceAlias ("vEthernet (crc)") -ResetServerAddresses` - if exist, defaultSwitch := winnet.GetDefaultSwitchName(); exist { - resetDNSCommand = fmt.Sprintf(`Set-DnsClientServerAddress -InterfaceAlias ("vEthernet (%s)","vEthernet (crc)") -ResetServerAddresses`, defaultSwitch) - } - if _, _, err := powershell.ExecuteAsAdmin("Remove dns entry for default switch", resetDNSCommand); err != nil { - return err - } - return nil -} - func removeCrcVM() (err error) { if _, _, err := powershell.Execute("Get-VM -Name crc"); err != nil { // This means that there is no crc VM exist diff --git a/pkg/crc/preflight/preflight_windows.go b/pkg/crc/preflight/preflight_windows.go index 2d8ea876bd..03e488a0d1 100644 --- a/pkg/crc/preflight/preflight_windows.go +++ b/pkg/crc/preflight/preflight_windows.go @@ -60,21 +60,6 @@ var hypervPreflightChecks = []Check{ labels: labels{Os: Windows}, }, - { - configKeySuffix: "check-hyperv-switch", - checkDescription: "Checking if the Hyper-V virtual switch exists", - check: checkIfHyperVVirtualSwitchExists, - flags: StartUpOnly, - - labels: labels{Os: Windows, NetworkMode: System}, - }, - { - cleanupDescription: "Removing dns server from interface", - cleanup: removeDNSServerAddress, - flags: CleanUpOnly, - - labels: labels{Os: Windows, NetworkMode: System}, - }, } var cleanupCheckRemoveCrcVM = Check{ diff --git a/pkg/crc/preflight/preflight_windows_test.go b/pkg/crc/preflight/preflight_windows_test.go index b0b7834d79..55b1705ca2 100644 --- a/pkg/crc/preflight/preflight_windows_test.go +++ b/pkg/crc/preflight/preflight_windows_test.go @@ -17,8 +17,8 @@ func TestCountConfigurationOptions(t *testing.T) { } func TestCountPreflights(t *testing.T) { - assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 24) - assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 24) + assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 22) + assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 22) assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 23) assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift, false), 23) diff --git a/pkg/crc/services/dns/dns_windows.go b/pkg/crc/services/dns/dns_windows.go index 908ad511c0..e7cdfd779c 100644 --- a/pkg/crc/services/dns/dns_windows.go +++ b/pkg/crc/services/dns/dns_windows.go @@ -2,50 +2,14 @@ package dns import ( "fmt" - "time" "github.com/crc-org/crc/v2/pkg/crc/network" "github.com/crc-org/crc/v2/pkg/crc/services" - winnet "github.com/crc-org/crc/v2/pkg/os/windows/network" - "github.com/crc-org/crc/v2/pkg/os/windows/powershell" - "github.com/crc-org/crc/v2/pkg/os/windows/win32" - crcstrings "github.com/crc-org/crc/v2/pkg/strings" -) - -const ( - // Alternative - AlternativeNetwork = "crc" ) func runPostStartForOS(serviceConfig services.ServicePostStartConfig) error { - if serviceConfig.NetworkMode == network.UserNetworkingMode { - return addOpenShiftHosts(serviceConfig) - } - - _, switchName := winnet.SelectSwitchByNameOrDefault(AlternativeNetwork) - networkInterface := fmt.Sprintf("vEthernet (%s)", switchName) - - setInterfaceNameserverValue(networkInterface, serviceConfig.IP) - - time.Sleep(2 * time.Second) - - if !crcstrings.Contains(getInterfaceNameserverValues(networkInterface), serviceConfig.IP) { - return fmt.Errorf("Nameserver %s not successfully set on interface %s. Perhaps you can try this new network mode: https://github.com/crc-org/crc/wiki/VPN-support--with-an--userland-network-stack", serviceConfig.IP, networkInterface) + if serviceConfig.NetworkMode != network.UserNetworkingMode { + return fmt.Errorf("only user-mode networking is supported on Windows") } - return nil -} - -func getInterfaceNameserverValues(iface string) []string { - getDNSServerCommand := fmt.Sprintf(`(Get-DnsClientServerAddress "%s")[0].ServerAddresses`, iface) - stdOut, _, _ := powershell.Execute(getDNSServerCommand) - - return crcstrings.SplitLines(stdOut) -} - -func setInterfaceNameserverValue(iface string, address string) { - exe := "netsh" - args := fmt.Sprintf(`interface ip set dns "%s" static %s primary`, iface, address) - - // ignore the error as this is useless (prefer not to use nolint here) - _ = win32.ShellExecuteAsAdmin(fmt.Sprintf("add dns server address to interface %s", iface), win32.HwndDesktop, exe, args, "", 0) + return addOpenShiftHosts(serviceConfig) } diff --git a/pkg/drivers/hyperv/hyperv_windows.go b/pkg/drivers/hyperv/hyperv_windows.go deleted file mode 100644 index 40705bf69b..0000000000 --- a/pkg/drivers/hyperv/hyperv_windows.go +++ /dev/null @@ -1,416 +0,0 @@ -package hyperv - -import ( - "encoding/json" - "errors" - "fmt" - "os/exec" - "strconv" - "strings" - "time" - - log "github.com/crc-org/crc/v2/pkg/crc/logging" - crcos "github.com/crc-org/crc/v2/pkg/os" - "github.com/crc-org/crc/v2/pkg/os/windows/powershell" - crcstrings "github.com/crc-org/crc/v2/pkg/strings" - "github.com/crc-org/machine/libmachine/drivers" - "github.com/crc-org/machine/libmachine/state" -) - -type Driver struct { - *drivers.VMDriver - VirtualSwitch string - MacAddress string - DisableDynamicMemory bool -} - -const ( - defaultMemory = 8192 - defaultCPU = 4 - defaultDisableDynamicMemory = false -) - -// NewDriver creates a new Hyper-v driver with default settings. -func NewDriver(hostName, storePath string) *Driver { - return &Driver{ - DisableDynamicMemory: defaultDisableDynamicMemory, - VMDriver: &drivers.VMDriver{ - BaseDriver: &drivers.BaseDriver{ - MachineName: hostName, - StorePath: storePath, - }, - Memory: defaultMemory, - CPU: defaultCPU, - }, - } -} - -func (d *Driver) UpdateConfigRaw(rawConfig []byte) error { - var newDriver Driver - - err := json.Unmarshal(rawConfig, &newDriver) - if err != nil { - return err - } - if newDriver.Memory != d.Memory { - log.Debugf("Updating memory from %d MB to %d MB", d.Memory, newDriver.Memory) - err := cmd("Hyper-V\\Set-VMMemory", - "-VMName", d.MachineName, - "-StartupBytes", toMb(newDriver.Memory)) - if err != nil { - log.Warnf("Failed to update memory to %d MB: %v", newDriver.Memory, err) - return err - } - } - - if newDriver.CPU != d.CPU { - log.Debugf("Updating CPU count from %d to %d", d.CPU, newDriver.CPU) - err := cmd("Hyper-V\\Set-VMProcessor", - d.MachineName, - "-Count", fmt.Sprintf("%d", newDriver.CPU)) - if err != nil { - log.Warnf("Failed to set CPU count to %d", newDriver.CPU) - return err - } - } - if newDriver.DiskCapacity != d.DiskCapacity { - if err := d.resizeDisk(int64(newDriver.DiskCapacity)); err != nil { - log.Warnf("Failed to set disk size to %d", newDriver.DiskCapacity) - return err - } - } - *d = newDriver - return nil -} - -// DriverName returns the name of the driver -func (d *Driver) DriverName() string { - return "hyperv" -} - -func (d *Driver) GetState() (state.State, error) { - stdout, stderr, err := powershell.Execute("Hyper-V\\Get-VM", d.MachineName, "|", "Select-Object", "-ExpandProperty", "State") - if err != nil { - return state.Error, fmt.Errorf("Failed to find the VM status: %v - %s", err, stderr) - } - - resp := crcstrings.FirstLine(stdout) - if resp == "" { - return state.Error, fmt.Errorf("unexpected Hyper-V state %s", stdout) - } - - switch resp { - case "Starting", "Running", "Stopping": - return state.Running, nil - case "Off": - return state.Stopped, nil - default: - return state.Error, fmt.Errorf("unexpected Hyper-V state %s", resp) - } -} - -// PreCreateCheck checks that the machine creation process can be started safely. -func (d *Driver) PreCreateCheck() error { - // Check that powershell was found - if _, err := exec.LookPath("powershell.exe"); err != nil { - return ErrPowerShellNotFound - } - - // Check that hyperv is installed - if err := hypervAvailable(); err != nil { - return err - } - - // Check that the user is an Administrator - isAdmin, err := isAdministrator() - if err != nil { - return err - } - if !isAdmin { - return ErrNotAdministrator - } - - if d.VirtualSwitch == "" { - return nil - } - - // Check that there is a virtual switch already configured - if _, err := d.chooseVirtualSwitch(); err != nil { - return err - } - - return err -} - -func (d *Driver) getDiskPath() string { - return d.ResolveStorePath(fmt.Sprintf("%s.%s", d.MachineName, d.ImageFormat)) -} - -func (d *Driver) resizeDisk(newSize int64) error { - diskPath := d.getDiskPath() - out, err := cmdOut(fmt.Sprintf("@(Get-VHD -Path %s).Size", quote(diskPath))) - if err != nil { - return fmt.Errorf("unable to get current size of crc.vhdx: %w", err) - } - currentSize, err := strconv.ParseInt(strings.TrimSpace(out), 10, 64) - if err != nil { - return fmt.Errorf("unable to convert disk size to int: %w", err) - } - if newSize == currentSize { - log.Debugf("%s is already %d bytes", diskPath, newSize) - return nil - } - if newSize < currentSize { - return fmt.Errorf("current disk image capacity is bigger than the requested size (%d > %d)", currentSize, newSize) - } - - log.Debugf("Resizing disk from %d bytes to %d bytes", currentSize, newSize) - return cmd("Hyper-V\\Resize-VHD", - "-Path", - quote(diskPath), - "-SizeBytes", - fmt.Sprintf("%d", newSize)) -} - -func (d *Driver) Create() error { - if err := crcos.CopyFile(d.ImageSourcePath, d.getDiskPath()); err != nil { - return err - } - - args := []string{ - "Hyper-V\\New-VM", - d.MachineName, - "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")), - "-MemoryStartupBytes", toMb(d.Memory), - } - if d.VirtualSwitch != "" { - virtualSwitch, err := d.chooseVirtualSwitch() - if err != nil { - return err - } - log.Debugf("Using switch %q", virtualSwitch) - args = append(args, "-SwitchName", quote(virtualSwitch)) - } - - log.Debugf("Creating VM...") - if err := cmd(args...); err != nil { - return err - } - - if d.VirtualSwitch == "" { - if err := cmd("Hyper-V\\Remove-VMNetworkAdapter", "-VMName", d.MachineName); err != nil { - return err - } - } - - if d.DisableDynamicMemory { - if err := cmd("Hyper-V\\Set-VMMemory", - "-VMName", d.MachineName, - "-DynamicMemoryEnabled", "$false"); err != nil { - return err - } - } - - if d.CPU > 1 { - if err := cmd("Hyper-V\\Set-VMProcessor", - d.MachineName, - "-Count", fmt.Sprintf("%d", d.CPU)); err != nil { - return err - } - } - - if d.VirtualSwitch != "" && d.MacAddress != "" { - if err := cmd("Hyper-V\\Set-VMNetworkAdapter", - "-VMName", d.MachineName, - "-StaticMacAddress", fmt.Sprintf("\"%s\"", d.MacAddress)); err != nil { - return err - } - } - - // Disables creating checkpoints and Automatic Start - // Shuts down the VM when host shuts down - if err := cmd("Hyper-V\\Set-VM", - "-VMName", d.MachineName, - "-AutomaticStartAction", "Nothing", - "-AutomaticStopAction", "ShutDown", - "-CheckpointType", "Disabled"); err != nil { - return err - } - - if err := cmd("Hyper-V\\Add-VMHardDiskDrive", - "-VMName", d.MachineName, - "-Path", quote(d.getDiskPath())); err != nil { - return err - } - - return d.resizeDisk(int64(d.DiskCapacity)) - -} - -func (d *Driver) chooseVirtualSwitch() (string, error) { - if d.VirtualSwitch == "" { - return "", errors.New("no virtual switch given") - } - - stdout, err := cmdOut("[Console]::OutputEncoding = [Text.Encoding]::UTF8; (Hyper-V\\Get-VMSwitch).Name") - if err != nil { - return "", err - } - - switches := crcstrings.SplitLines(stdout) - - found := false - for _, name := range switches { - if name == d.VirtualSwitch { - found = true - break - } - } - - if !found { - return "", fmt.Errorf("virtual switch %q not found", d.VirtualSwitch) - } - - return d.VirtualSwitch, nil -} - -// waitForIP waits until the host has a valid IP -func (d *Driver) waitForIP() (string, error) { - if d.VirtualSwitch == "" { - return "", errors.New("no virtual switch given") - } - - log.Debugf("Waiting for host to start...") - - for { - ip, _ := d.GetIP() - if ip != "" { - return ip, nil - } - - time.Sleep(1 * time.Second) - } -} - -// waitStopped waits until the host is stopped -func (d *Driver) waitStopped() error { - log.Debugf("Waiting for host to stop...") - - for { - s, err := d.GetState() - if err != nil { - return err - } - - if s != state.Running { - return nil - } - - time.Sleep(1 * time.Second) - } -} - -// Start starts an host -func (d *Driver) Start() error { - if err := cmd("Hyper-V\\Start-VM", d.MachineName); err != nil { - return err - } - - if d.VirtualSwitch == "" { - return nil - } - - ip, err := d.waitForIP() - if err != nil { - return err - } - - d.IPAddress = ip - - return nil -} - -// Stop stops an host -func (d *Driver) Stop() error { - if err := cmd("Hyper-V\\Stop-VM", d.MachineName); err != nil { - return err - } - - if err := d.waitStopped(); err != nil { - return err - } - - d.IPAddress = "" - - return nil -} - -// Remove removes an host -func (d *Driver) Remove() error { - if _, _, err := powershell.Execute(`Hyper-V\Get-VM`, d.MachineName, "-ErrorAction", "SilentlyContinue", "-ErrorVariable", "getVmErrors"); err != nil { - return nil - } - - s, err := d.GetState() - if err != nil { - return err - } - - if s == state.Running { - if err := d.Kill(); err != nil { - return err - } - } - - return cmd("Hyper-V\\Remove-VM", d.MachineName, "-Force") -} - -// Kill force stops an host -func (d *Driver) Kill() error { - if err := cmd("Hyper-V\\Stop-VM", d.MachineName, "-TurnOff"); err != nil { - return err - } - - if err := d.waitStopped(); err != nil { - return err - } - - d.IPAddress = "" - - return nil -} - -func (d *Driver) GetIP() (string, error) { - if d.VirtualSwitch == "" { - return "", errors.New("no virtual switch given") - } - - s, err := d.GetState() - if err != nil { - return "", err - } - if s != state.Running { - return "", drivers.ErrHostIsNotRunning - } - - stdout, err := cmdOut("((", "Hyper-V\\Get-VM", d.MachineName, ").networkadapters[0]).ipaddresses[0]") - if err != nil { - return "", err - } - - resp := crcstrings.FirstLine(stdout) - if resp == "" { - return "", fmt.Errorf("IP not found") - } - - return resp, nil -} - -func (d *Driver) GetSharedDirs() ([]drivers.SharedDir, error) { - for _, dir := range d.SharedDirs { - if !smbShareExists(dir.Tag) { - return []drivers.SharedDir{}, nil - } - } - return d.SharedDirs, nil -} diff --git a/pkg/drivers/libhvee/libhvee_windows.go b/pkg/drivers/libhvee/libhvee_windows.go new file mode 100644 index 0000000000..7028c819a6 --- /dev/null +++ b/pkg/drivers/libhvee/libhvee_windows.go @@ -0,0 +1,305 @@ +package libhvee + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + + log "github.com/crc-org/crc/v2/pkg/crc/logging" + crcos "github.com/crc-org/crc/v2/pkg/os" + "github.com/crc-org/machine/libmachine/drivers" + "github.com/crc-org/machine/libmachine/state" + + "github.com/containers/common/pkg/strongunits" + "github.com/containers/libhvee/pkg/hypervctl" +) + +type Driver struct { + *drivers.VMDriver + DynamicMemory bool +} + +const ( + defaultMemory = 8192 + defaultCPU = 4 + defaultDynamicMemory = false + systemGeneration = "Microsoft:Hyper-V:SubType:2" +) + +// NewDriver creates a new Hyper-v driver with default settings. +func NewDriver(hostName, storePath string) *Driver { + return &Driver{ + VMDriver: &drivers.VMDriver{ + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + Memory: defaultMemory, + CPU: defaultCPU, + }, + DynamicMemory: defaultDynamicMemory, + } +} + +func (d *Driver) UpdateConfigRaw(rawConfig []byte) error { + var update Driver + + err := json.Unmarshal(rawConfig, &update) + if err != nil { + return err + } + + if update.Memory != d.Memory { + log.Debugf("Machine: libhvee -> Updating memory from %d MB to %d MB", d.Memory, update.Memory) + + vm, err := d.getMachine() + if err != nil { + return err + } + + err = vm.UpdateProcessorMemSettings( + func(_ *hypervctl.ProcessorSettings) {}, + func(ms *hypervctl.MemorySettings) { + ms.VirtualQuantity = uint64(update.Memory) + ms.Limit = uint64(update.Memory) + }) + + if err != nil { + return err + } + } + + if update.CPU != d.CPU { + log.Debugf("Machine: libhvee -> Updating CPU count from %d to %d", d.CPU, update.CPU) + + vm, err := d.getMachine() + if err != nil { + return err + } + + err = vm.UpdateProcessorMemSettings( + func(ps *hypervctl.ProcessorSettings) { + ps.VirtualQuantity = uint64(update.CPU) + }, + func(_ *hypervctl.MemorySettings) {}) + + if err != nil { + return err + } + } + if update.DiskCapacity != d.DiskCapacity { + if err := d.resizeDisk(int64(update.DiskCapacity)); err != nil { + log.Warnf("Machine: libhvee -> Failed to set disk size to %d", update.DiskCapacity) + return err + } + } + *d = update + return nil +} + +// DriverName returns the name of the driver +func (d *Driver) DriverName() string { + return "libhvee" +} + +func (d *Driver) GetState() (state.State, error) { + log.Debugf("Machine: libhvee -> state") + vm, err := d.getMachine() + if err != nil { + return state.Error, err + } + + log.Debugf("Machine: libhvee -> state: get") + vmState := vm.State() + switch vmState { + case hypervctl.Enabled: + log.Debugf("Machine: libhvee -> state: running") + return state.Running, nil + case hypervctl.Disabled: + log.Debugf("Machine: libhvee -> state: stopped") + return state.Stopped, nil + } + + log.Debugf("Machine: libhvee -> state: unknown") + return state.Error, fmt.Errorf("unknown state") +} + +// PreCreateCheck checks that the machine creation process can be started safely. +func (d *Driver) PreCreateCheck() error { + // Check that powershell was found + if _, err := exec.LookPath("powershell.exe"); err != nil { + return ErrPowerShellNotFound + } + + // Check that hyperv is installed + if err := hypervAvailable(); err != nil { + return err + } + + // Check that the user is an Administrator + isAdmin, err := isAdministrator() + if err != nil { + return err + } + if !isAdmin { + return ErrNotAdministrator + } + + return nil +} + +func (d *Driver) Create() error { + log.Debugf("Machine: libhvee -> creating: system settings") + systemSettings, err := hypervctl.NewSystemSettingsBuilder(). + PrepareSystemSettings(d.MachineName, nil). + PrepareMemorySettings( + func(ms *hypervctl.MemorySettings) { + ms.DynamicMemoryEnabled = d.DynamicMemory + ms.VirtualQuantity = uint64(d.Memory) + ms.Reservation = 1024 + ms.Limit = uint64(d.Memory) + }). + PrepareProcessorSettings( + func(ps *hypervctl.ProcessorSettings) { + ps.VirtualQuantity = uint64(d.CPU) + }). + Build() + + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> creating: copy disk image") + diskPath := d.getDiskPath() + if err := crcos.CopyFile(d.ImageSourcePath, diskPath); err != nil { + return err + } + + log.Debugf("Machine: libhvee -> creating: hardware setup") + err = hypervctl.NewDriveSettingsBuilder(systemSettings). + AddScsiController(). + AddSyntheticDiskDrive(0). + DefineVirtualHardDisk(diskPath, + func(_ *hypervctl.VirtualHardDiskStorageSettings) {}). + Finish(). + Finish(). + Finish(). + Complete() + + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> creating: done") + + return d.resizeDisk(int64(d.DiskCapacity)) + +} + +func (d *Driver) getMachine() (*hypervctl.VirtualMachine, error) { + vmm := hypervctl.NewVirtualMachineManager() + + log.Debugf("Machine: libhvee -> get machine") + return vmm.GetMachine(d.MachineName) +} + +// Start starts an host +func (d *Driver) Start() error { + vm, err := d.getMachine() + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> start") + return vm.Start() +} + +// Stop stops an host +func (d *Driver) Stop() error { + vm, err := d.getMachine() + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> stop") + return vm.Stop() +} + +// Remove removes an host +func (d *Driver) Remove() error { + s, err := d.GetState() + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> remove running") + if s == state.Running { + if err := d.Kill(); err != nil { + return err + } + } + + vm, err := d.getMachine() + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> remove vm") + err = vm.Remove("") + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> remove disk file") + return os.Remove(d.getDiskPath()) +} + +// Kill force stops an host +func (d *Driver) Kill() error { + vm, err := d.getMachine() + if err != nil { + return err + } + + log.Debugf("Machine: libhvee -> stop with force") + return vm.StopWithForce() +} + +func (d *Driver) GetIP() (string, error) { + return "", drivers.ErrNotSupported +} + +func (d *Driver) GetSharedDirs() ([]drivers.SharedDir, error) { + for _, dir := range d.SharedDirs { + if !smbShareExists(dir.Tag) { + return []drivers.SharedDir{}, nil + } + } + return d.SharedDirs, nil +} + +func (d *Driver) getDiskPath() string { + return d.ResolveStorePath(fmt.Sprintf("%s.%s", d.MachineName, d.ImageFormat)) +} + +func (d *Driver) resizeDisk(newSizeBytes int64) error { + + newSize := strongunits.B(newSizeBytes) + + diskPath := d.getDiskPath() + currentSize, err := hypervctl.GetDiskSize(diskPath) + if err != nil { + return fmt.Errorf("unable to get current size of crc.vhdx: %w", err) + } + if newSize == currentSize.ToBytes() { + log.Debugf("%s is already %d bytes", diskPath, newSize) + return nil + } + if newSize < currentSize.ToBytes() { + return fmt.Errorf("current disk image capacity is bigger than the requested size (%d > %d)", currentSize.ToBytes(), newSize) + } + + log.Debugf("Resizing disk from %d bytes to %d bytes", currentSize.ToBytes(), newSize.ToBytes()) + return hypervctl.ResizeDisk(diskPath, strongunits.ToGiB(newSize)) +} diff --git a/pkg/drivers/hyperv/powershell_windows.go b/pkg/drivers/libhvee/powershell_windows.go similarity index 92% rename from pkg/drivers/hyperv/powershell_windows.go rename to pkg/drivers/libhvee/powershell_windows.go index 7bc1178025..6c8f5c7ab3 100644 --- a/pkg/drivers/hyperv/powershell_windows.go +++ b/pkg/drivers/libhvee/powershell_windows.go @@ -1,4 +1,4 @@ -package hyperv +package libhvee import ( "errors" @@ -76,14 +76,6 @@ func isWindowsAdministrator() (bool, error) { return resp == "True", nil } -func quote(text string) string { - return fmt.Sprintf("'%s'", text) -} - -func toMb(value int) string { - return fmt.Sprintf("%dMB", value) -} - func smbShareExists(name string) bool { if err := cmd(fmt.Sprintf("Get-SmbShare -Name %s", name)); err != nil { return false diff --git a/pkg/libmachine/load_windows.go b/pkg/libmachine/load_windows.go index 12e26fa40c..062379e5d8 100644 --- a/pkg/libmachine/load_windows.go +++ b/pkg/libmachine/load_windows.go @@ -3,12 +3,12 @@ package libmachine import ( "encoding/json" - "github.com/crc-org/crc/v2/pkg/drivers/hyperv" + "github.com/crc-org/crc/v2/pkg/drivers/libhvee" "github.com/crc-org/crc/v2/pkg/libmachine/host" ) func (api *Client) NewHost(_ string, driverPath string, rawDriver []byte) (*host.Host, error) { - driver := hyperv.NewDriver("", "") + driver := libhvee.NewDriver("", "") if err := json.Unmarshal(rawDriver, &driver); err != nil { return nil, err } @@ -29,7 +29,7 @@ func (api *Client) Load(name string) (*host.Host, error) { return nil, err } - driver := hyperv.NewDriver("", "") + driver := libhvee.NewDriver("", "") if err := json.Unmarshal(h.RawDriver, &driver); err != nil { return nil, err } diff --git a/pkg/os/windows/network/vswitch_windows.go b/pkg/os/windows/network/vswitch_windows.go deleted file mode 100644 index bf5cf5786a..0000000000 --- a/pkg/os/windows/network/vswitch_windows.go +++ /dev/null @@ -1,47 +0,0 @@ -package network - -import ( - "fmt" - "strings" - - "github.com/crc-org/crc/v2/pkg/os/windows/powershell" -) - -const hypervDefaultVirtualSwitchID = "c08cb7b8-9b3c-408e-8e30-5e16a3aeb444" - -func SelectSwitchByNameOrDefault(name string) (bool, string) { - // if named exists - if ExistsSwitchByName(name) { - return true, name - } - - // else use Default - return GetDefaultSwitchName() -} - -func ExistsSwitchByName(name string) bool { - getSwitchByNameCmd := fmt.Sprintf("Get-VMSwitch %s | ForEach-Object { $_.Name }", name) - stdOut, stdErr, _ := powershell.Execute(getSwitchByNameCmd) - - // If stdErr contains the command then execution failed - if strings.Contains(stdErr, "Get-VMSwitch") { - return false - } - - if strings.Contains(stdOut, name) { - return true - } - return false -} - -func GetDefaultSwitchName() (bool, string) { - getDefaultSwitchNameCmd := fmt.Sprintf("[Console]::OutputEncoding = [Text.Encoding]::UTF8; Get-VMSwitch -Id %s | ForEach-Object { $_.Name }", hypervDefaultVirtualSwitchID) - stdOut, stdErr, _ := powershell.Execute(getDefaultSwitchNameCmd) - - // If stdErr contains the command then execution failed - if strings.Contains(stdErr, "Get-VMSwitch") { - return false, "" - } - - return true, strings.TrimSpace(stdOut) -} diff --git a/vendor/github.com/containers/common/LICENSE b/vendor/github.com/containers/common/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/github.com/containers/common/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containers/common/pkg/strongunits/config.go b/vendor/github.com/containers/common/pkg/strongunits/config.go new file mode 100644 index 0000000000..35a6b0c3d1 --- /dev/null +++ b/vendor/github.com/containers/common/pkg/strongunits/config.go @@ -0,0 +1,65 @@ +package strongunits + +// supported units + +// B represents bytes +type B uint64 + +// KiB represents KiB +type KiB uint64 + +// MiB represents MiB +type MiB uint64 + +// GiB represents GiB +type GiB uint64 + +const ( + // kibToB is the math convert from bytes to KiB + kibToB = 1 << 10 + // mibToB is the math to convert from bytes to MiB + mibToB = 1 << 20 + // gibToB s the math to convert from bytes to GiB + gibToB = 1 << 30 +) + +// StorageUnits is an interface for converting disk/memory storage +// units amongst each other. +type StorageUnits interface { + ToBytes() B +} + +// ToBytes is a pass-through function for bytes +func (b B) ToBytes() B { + return b +} + +// ToBytes converts KiB to bytes +func (k KiB) ToBytes() B { + return B(k * kibToB) +} + +// ToBytes converts MiB to bytes +func (m MiB) ToBytes() B { + return B(m * mibToB) +} + +// ToBytes converts GiB to bytes +func (g GiB) ToBytes() B { + return B(g * gibToB) +} + +// ToKiB converts any StorageUnit type to KiB +func ToKiB(b StorageUnits) KiB { + return KiB(b.ToBytes() >> 10) +} + +// ToMib converts any StorageUnit type to MiB +func ToMib(b StorageUnits) MiB { + return MiB(b.ToBytes() >> 20) +} + +// ToGiB converts any StorageUnit type to GiB +func ToGiB(b StorageUnits) GiB { + return GiB(b.ToBytes() >> 30) +} diff --git a/vendor/github.com/containers/libhvee/LICENSE b/vendor/github.com/containers/libhvee/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/containers/libhvee/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/diskdrive_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/diskdrive_settings.go new file mode 100644 index 0000000000..5e669302d6 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/diskdrive_settings.go @@ -0,0 +1,72 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "github.com/containers/libhvee/pkg/wmiext" +) + +const SyntheticDiskDriveType = "Microsoft:Hyper-V:Synthetic Disk Drive" + +type SyntheticDiskDriveSettings struct { + ResourceSettings + systemSettings *SystemSettings + controllerSettings *ScsiControllerSettings +} + +type diskAssociation interface { + setParent(parent string) + setHostResource(resource []string) + Path() string +} + +func (d *SyntheticDiskDriveSettings) DefineVirtualHardDisk(vhdxFile string, beforeAdd func(*VirtualHardDiskStorageSettings)) (*VirtualHardDiskStorageSettings, error) { + vhd := &VirtualHardDiskStorageSettings{} + + var cb func() + if beforeAdd != nil { + cb = func() { + beforeAdd(vhd) + } + } + + if err := createDiskResourceInternal(d.systemSettings.Path(), d.Path(), vhdxFile, vhd, VirtualHardDiskType, cb); err != nil { + return nil, err + } + + vhd.driveSettings = d + vhd.systemSettings = d.systemSettings + return vhd, nil +} + +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 = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + if err = populateDefaults(resourceType, settings); err != nil { + return err + } + + settings.setHostResource([]string{file}) + settings.setParent(drivePath) + if cb != nil { + cb() + } + + diskResource, err := createResourceSettingGeneric(settings, resourceType) + if err != nil { + return err + } + + path, err := addResource(service, systemPath, diskResource) + if err != nil { + return err + } + + return service.GetObjectAsObject(path, settings) +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/drive_settings_builder.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/drive_settings_builder.go new file mode 100644 index 0000000000..21d67478c3 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/drive_settings_builder.go @@ -0,0 +1,186 @@ +//go:build windows +// +build windows + +package hypervctl + +type DriveSettingsBuilder struct { + systemSettings *SystemSettings + err error +} + +type ControllerSettingsBuilder struct { + driveSettingsBuilder DriveSettingsBuilder + controllerSettings *ScsiControllerSettings + err error +} + +type SyntheticDiskDriveSettingsBuilder struct { + controllerBuilder *ControllerSettingsBuilder + driveSettings *SyntheticDiskDriveSettings + err error +} + +type SyntheticDvdDriveSettingsBuilder struct { + controllerBuilder *ControllerSettingsBuilder + driveSettings *SyntheticDvdDriveSettings + err error +} + +type VirtualHardDiskStorageSettingsBuilder struct { + driveBuilder *SyntheticDiskDriveSettingsBuilder + diskSettings *VirtualHardDiskStorageSettings + err error +} + +type VirtualDvdDiskStorageSettingsBuilder struct { + driveBuilder *SyntheticDvdDriveSettingsBuilder + diskSettings *VirtualDvdDiskStorageSettings + err error +} + +func NewDriveSettingsBuilder(systemSettings *SystemSettings) *DriveSettingsBuilder { + return &DriveSettingsBuilder{systemSettings: systemSettings} +} + +func (builder *DriveSettingsBuilder) AddScsiController() *ControllerSettingsBuilder { + if builder.err != nil { + return &ControllerSettingsBuilder{driveSettingsBuilder: *builder, err: builder.err} + } + + controllerSettings, err := builder.systemSettings.AddScsiController() + builder.setErr(err) + + return &ControllerSettingsBuilder{ + driveSettingsBuilder: *builder, + controllerSettings: controllerSettings, + err: err, + } +} + +func (builder *ControllerSettingsBuilder) AddSyntheticDiskDrive(slot uint) *SyntheticDiskDriveSettingsBuilder { + if builder.err != nil { + return &SyntheticDiskDriveSettingsBuilder{controllerBuilder: builder, err: builder.err} + } + + driveSettings, err := builder.controllerSettings.AddSyntheticDiskDrive(slot) + builder.setErr(err) + + return &SyntheticDiskDriveSettingsBuilder{ + controllerBuilder: builder, + driveSettings: driveSettings, + err: err, + } +} + +func (builder *ControllerSettingsBuilder) AddSyntheticDvdDrive(slot uint) *SyntheticDvdDriveSettingsBuilder { + if builder.err != nil { + return &SyntheticDvdDriveSettingsBuilder{controllerBuilder: builder, err: builder.err} + } + + driveSettings, err := builder.controllerSettings.AddSyntheticDvdDrive(slot) + builder.setErr(err) + + return &SyntheticDvdDriveSettingsBuilder{ + controllerBuilder: builder, + driveSettings: driveSettings, + err: err, + } +} + +func (builder *SyntheticDiskDriveSettingsBuilder) DefineVirtualHardDisk(vhdxFile string, beforeAdd func(*VirtualHardDiskStorageSettings)) *VirtualHardDiskStorageSettingsBuilder { + if builder.err != nil { + return &VirtualHardDiskStorageSettingsBuilder{driveBuilder: builder, err: builder.err} + } + + diskSettings, err := builder.driveSettings.DefineVirtualHardDisk(vhdxFile, beforeAdd) + builder.setErr(err) + + return &VirtualHardDiskStorageSettingsBuilder{ + driveBuilder: builder, + diskSettings: diskSettings, + err: err, + } +} + +func (builder *SyntheticDvdDriveSettingsBuilder) DefineVirtualDvdDisk(imageFile string) *VirtualDvdDiskStorageSettingsBuilder { + if builder.err != nil { + return &VirtualDvdDiskStorageSettingsBuilder{driveBuilder: builder, err: builder.err} + } + + diskSettings, err := builder.driveSettings.DefineVirtualDvdDisk(imageFile) + builder.setErr(err) + + return &VirtualDvdDiskStorageSettingsBuilder{ + driveBuilder: builder, + diskSettings: diskSettings, + err: err, + } +} + +func (builder *SyntheticDvdDriveSettingsBuilder) setErr(err error) { + builder.err = err + builder.controllerBuilder.setErr(err) +} + +func (builder *SyntheticDiskDriveSettingsBuilder) setErr(err error) { + builder.err = err + builder.controllerBuilder.setErr(err) +} + +func (builder *ControllerSettingsBuilder) setErr(err error) { + builder.err = err + builder.driveSettingsBuilder.setErr(err) +} + +func (builder *DriveSettingsBuilder) setErr(err error) { + builder.err = err +} + +func (builder *ControllerSettingsBuilder) Finish() *DriveSettingsBuilder { + return &builder.driveSettingsBuilder +} + +func (builder *VirtualHardDiskStorageSettingsBuilder) Finish() *SyntheticDiskDriveSettingsBuilder { + return builder.driveBuilder +} + +func (builder *VirtualDvdDiskStorageSettingsBuilder) Finish() *SyntheticDvdDriveSettingsBuilder { + return builder.driveBuilder +} + +func (builder *SyntheticDiskDriveSettingsBuilder) Finish() *ControllerSettingsBuilder { + return builder.controllerBuilder +} + +func (builder *SyntheticDvdDriveSettingsBuilder) Finish() *ControllerSettingsBuilder { + return builder.controllerBuilder +} + +func (builder *VirtualHardDiskStorageSettingsBuilder) Get(s **VirtualHardDiskStorageSettings) *VirtualHardDiskStorageSettingsBuilder { + *s = builder.diskSettings + return builder +} + +func (builder *VirtualDvdDiskStorageSettingsBuilder) Get(s **VirtualDvdDiskStorageSettings) *VirtualDvdDiskStorageSettingsBuilder { + *s = builder.diskSettings + return builder +} + +func (builder *SyntheticDiskDriveSettingsBuilder) Get(s **SyntheticDiskDriveSettings) *SyntheticDiskDriveSettingsBuilder { + *s = builder.driveSettings + return builder +} + +func (builder *SyntheticDvdDriveSettingsBuilder) Get(s **SyntheticDvdDriveSettings) *SyntheticDvdDriveSettingsBuilder { + *s = builder.driveSettings + return builder +} + +func (builder *ControllerSettingsBuilder) Get(s **ScsiControllerSettings) *ControllerSettingsBuilder { + *s = builder.controllerSettings + return builder +} + +func (builder *DriveSettingsBuilder) Complete() error { + return builder.err +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/dvddrive_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/dvddrive_settings.go new file mode 100644 index 0000000000..2f66163a27 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/dvddrive_settings.go @@ -0,0 +1,24 @@ +//go:build windows +// +build windows + +package hypervctl + +const SyntheticDvdDriveType = "Microsoft:Hyper-V:Synthetic DVD Drive" + +type SyntheticDvdDriveSettings struct { + ResourceSettings + systemSettings *SystemSettings + controllerSettings *ScsiControllerSettings +} + +func (d *SyntheticDvdDriveSettings) DefineVirtualDvdDisk(imageFile string) (*VirtualDvdDiskStorageSettings, error) { + vdvd := &VirtualDvdDiskStorageSettings{} + + if err := createDiskResourceInternal(d.systemSettings.Path(), d.Path(), imageFile, vdvd, VirtualDvdDiskType, nil); err != nil { + return nil, err + } + + vdvd.driveSettings = d + vdvd.systemSettings = d.systemSettings + return vdvd, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/error.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/error.go new file mode 100644 index 0000000000..2f169712aa --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/error.go @@ -0,0 +1,174 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "errors" + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +// VM State errors +var ( + ErrMachineAlreadyRunning = errors.New("machine already running") + ErrMachineNotRunning = errors.New("machine not running") + ErrMachineStateInvalid = errors.New("machine in invalid state for action") + ErrMachineStarting = errors.New("machine is currently starting") +) + +// VM Creation errors +var ( + ErrMachineAlreadyExists = errors.New("machine already exists") +) + +type DestroySystemResult int32 + +// VM Destroy Exit Codes +const ( + VMDestroyCompletedwithNoError DestroySystemResult = 0 + VMDestroyNotSupported DestroySystemResult = 1 + VMDestroyFailed DestroySystemResult = 2 + VMDestroyTimeout DestroySystemResult = 3 + VMDestroyInvalidParameter DestroySystemResult = 4 + VMDestroyInvalidState DestroySystemResult = 5 +) + +func (e DestroySystemResult) Reason() string { + switch e { + case VMDestroyNotSupported: + return "not supported" + case VMDestroyFailed: + return "failed" + case VMDestroyTimeout: + return "timeout" + case VMDestroyInvalidParameter: + return "invalid parameter" + case VMDestroyInvalidState: + return "invalid state" + } + return "Unknown" +} + +// Shutdown operation error codes +const ( + ErrShutdownFailed = 32768 + ErrShutdownAccessDenied = 32769 + ErrShutdownNotSupported = 32770 + ErrShutdownStatusUnkown = 32771 + ErrShutdownTimeout = 32772 + ErrShutdownInvalidParameter = 32773 + ErrShutdownSystemInUse = 32774 + ErrShutdownInvalidState = 32775 + ErrShutdownIncorrectData = 32776 + ErrShutdownNotAvailable = 32777 + ErrShutdownOutOfMemory = 32778 + ErrShutdownFileNotFound = 32779 + ErrShutdownNotReady = 32780 + ErrShutdownMachineLocked = 32781 + ErrShutdownInProgress = 32782 +) + +type shutdownCompError struct { + errorCode int + message string +} + +func (s *shutdownCompError) Error() string { + return fmt.Sprintf("%s (%d)", s.message, s.errorCode) +} + +func translateShutdownError(code int) error { + var message string + switch code { + case ErrShutdownFailed: + message = "shutdown failed" + case ErrShutdownAccessDenied: + message = "access was denied" + case ErrShutdownNotSupported: + message = "shutdown not supported by virtual machine" + case ErrShutdownStatusUnkown: + message = "virtual machine status is unknown" + case ErrShutdownTimeout: + message = "timeout starting shutdown" + case ErrShutdownInvalidParameter: + message = "invalid parameter" + case ErrShutdownSystemInUse: + message = "system in use" + case ErrShutdownInvalidState: + message = "virtual machine is in an invalid state for shutdown" + case ErrShutdownIncorrectData: + message = "incorrect data type" + case ErrShutdownNotAvailable: + message = "system is not available" + case ErrShutdownOutOfMemory: + message = "out of memory" + case ErrShutdownFileNotFound: + message = "file not found" + case ErrShutdownMachineLocked: + message = "machine is locked and cannot be shut down without the force option" + case ErrShutdownInProgress: + message = "shutdown is already in progress" + default: + message = "unknown error" + } + + return &shutdownCompError{code, message} +} + +// Modify resource errors +const ( + ErrModifyResourceNotSupported = 1 + ErrModifyResourceFailed = 2 + ErrModifyResourceTimeout = 3 + ErrModifyResourceInvalidParameter = 4 + ErrModifyResourceInvalidState = 5 + ErrModifyResourceIncompatParam = 6 +) + +type modifyResourceError struct { + errorCode int + message string +} + +func (m *modifyResourceError) Error() string { + return fmt.Sprintf("%s (%d)", m.message, m.errorCode) +} + +func translateModifyError(code int) error { + var message string + switch code { + case ErrModifyResourceNotSupported: + message = "virtual machine does not support modification operations" + case ErrModifyResourceFailed: + message = "resource modification failed" + case ErrModifyResourceTimeout: + message = "timeout modifying resource" + case ErrModifyResourceInvalidParameter: + message = "a modify resource operation was passed an invalid parameter" + case ErrModifyResourceInvalidState: + message = "the requested modification could not be applied due to an invalid state" + case ErrModifyResourceIncompatParam: + message = "an incompatible parameter was passed to a modify resource operation" + default: + message = "unknown 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/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_alloc_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_alloc_settings.go new file mode 100644 index 0000000000..9cdb644954 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_alloc_settings.go @@ -0,0 +1,50 @@ +//go:build windows +// +build windows + +package hypervctl + +const EthernetPortAllocationResourceType = "Microsoft:Hyper-V:Ethernet Connection" + +type EthernetPortAllocationSettings struct { + InstanceID string // = "Microsoft:GUID\DeviceSpecificData" + Caption string // = "Ethernet Switch Port Settings" + Description string // = "Ethernet Switch Port Settings" + ElementName string + ResourceType uint16 // = 33 + OtherResourceType string + ResourceSubType string + PoolID string + ConsumerVisibility uint16 // = 3 + HostResource []string + AllocationUnits string + VirtualQuantity uint64 + Reservation uint64 + Limit uint64 + Weight uint32 // = 0 + AutomaticAllocation bool + AutomaticDeallocation bool + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualQuantityUnits string // = "count" + DesiredVLANEndpointMode uint16 + OtherEndpointMode string + EnabledState uint16 + LastKnownSwitchName string + RequiredFeatures []string + RequiredFeatureHints []string + TestReplicaPoolID string + TestReplicaSwitchName string + CompartmentGuid string +} + +func fetchEthernetPortAllocationSettings() (*EthernetPortAllocationSettings, error) { + settings := &EthernetPortAllocationSettings{} + return settings, populateDefaults(EthernetPortAllocationResourceType, settings) +} + +func creatEthernetPortAllocationSettings(settings *EthernetPortAllocationSettings) (string, error) { + return createResourceSettingGeneric(settings, EthernetPortAllocationResourceType) +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_port_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_port_settings.go new file mode 100644 index 0000000000..294d87e5ee --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/ethernet_port_settings.go @@ -0,0 +1,109 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +const SyntheticEthernetPortResourceType = "Microsoft:Hyper-V:Synthetic Ethernet Port" +const DefaultSwitchId = "C08CB7B8-9B3C-408E-8E30-5E16A3AEB444" + +type SyntheticEthernetPortSettings struct { + S__PATH string + InstanceID string + Caption string // = "Virtual Ethernet Port Default Settings" + Description string // = "Describes the default settings for the virtual Ethernet port resources." + ElementName string + ResourceType uint16 // = 10 + OtherResourceType string + ResourceSubType string // = "Microsoft:Hyper-V:Synthetic Ethernet Port" + PoolID string + ConsumerVisibility uint16 // = 3 + HostResource []string + AllocationUnits string // = "count" + VirtualQuantity uint64 // = 1 + Reservation uint64 // = 1 + Limit uint64 // = 1 + Weight uint32 // = 0 + AutomaticAllocation bool // = True + AutomaticDeallocation bool // = True + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualQuantityUnits string // = "count" + DesiredVLANEndpointMode uint16 + OtherEndpointMode string + VirtualSystemIdentifiers []string + DeviceNamingEnabled bool // = FALSE + AllowPacketDirect bool // = FALSE + StaticMacAddress bool // = False + ClusterMonitored bool // = TRUE + + systemSettings *SystemSettings +} + +func (p *SyntheticEthernetPortSettings) Path() string { + return p.S__PATH +} + +func (p *SyntheticEthernetPortSettings) DefineEthernetPortConnection(switchName string) (*EthernetPortAllocationSettings, error) { + const wqlFormat = "select * from Msvm_VirtualEthernetSwitch where %s = '%s'" + + var wqlProperty, wqlValue string + if len(switchName) > 0 { + wqlProperty = "ElementName" + wqlValue = switchName + } else { + wqlProperty = "Name" + wqlValue = DefaultSwitchId + } + + wql := fmt.Sprintf(wqlFormat, wqlProperty, wqlValue) + + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return nil, err + } + defer service.Close() + + switchInst, err := service.FindFirstInstance(wql) + if err != nil { + return nil, err + } + defer switchInst.Close() + switchPath, err := switchInst.Path() + if err != nil { + return nil, err + } + + connectSettings, err := fetchEthernetPortAllocationSettings() + if err != nil { + return nil, err + } + + connectSettings.Parent = p.Path() + connectSettings.HostResource = append(connectSettings.HostResource, switchPath) + + resource, err := creatEthernetPortAllocationSettings(connectSettings) + if err != nil { + return nil, err + } + + path, err := addResource(service, p.systemSettings.Path(), resource) + if err != nil { + return nil, err + } + + if err := service.GetObjectAsObject(path, connectSettings); err != nil { + return nil, err + } + + return connectSettings, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/kvp.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/kvp.go new file mode 100644 index 0000000000..04bed34298 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/kvp.go @@ -0,0 +1,138 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "encoding/xml" + "fmt" + "io" + "strings" + + "github.com/containers/libhvee/pkg/wmiext" +) + +const ( + KvpOperationFailed = 32768 + KvpAccessDenied = 32769 + KvpNotSupported = 32770 + KvpStatusUnknown = 32771 + KvpTimeoutOccurred = 32772 + KvpIllegalArgument = 32773 + KvpSystemInUse = 32774 + KvpInvalidState = 32775 + KvpIncorrectDataType = 32776 + KvpSystemNotAvailable = 32777 + KvpOutOfMemory = 32778 + KvpNotFound = 32779 + + KvpExchangeDataItemName = "Msvm_KvpExchangeDataItem" + MemorySettingDataName = "Msvm_MemorySettingData" +) + +type CimKvpItems struct { + Instances []CimKvpItem `xml:"INSTANCE"` +} + +type CimKvpItem struct { + Properties []CimKvpItemProperty `xml:"PROPERTY"` +} + +type CimKvpItemProperty struct { + Name string `xml:"NAME,attr"` + Value string `xml:"VALUE"` +} + +type KvpError struct { + ErrorCode int + message string +} + +func (k *KvpError) Error() string { + return fmt.Sprintf("%s (%d)", k.message, k.ErrorCode) +} + +func createKvpItem(service *wmiext.Service, key string, value string) (string, error) { + item, err := service.SpawnInstance(KvpExchangeDataItemName) + if err != nil { + return "", err + } + defer item.Close() + + _ = item.Put("Name", key) + _ = item.Put("Data", value) + _ = item.Put("Source", 0) + itemStr := item.GetCimText() + return itemStr, nil +} + +func parseKvpMapXml(kvpXml string) (map[string]string, error) { + // Workaround XML decoder's inability to handle multiple root elements + r := io.MultiReader( + strings.NewReader(""), + strings.NewReader(kvpXml), + strings.NewReader(""), + ) + + var items CimKvpItems + if err := xml.NewDecoder(r).Decode(&items); err != nil { + return nil, err + } + + ret := make(map[string]string) + for _, item := range items.Instances { + var key, value string + for _, prop := range item.Properties { + if strings.EqualFold(prop.Name, "Name") { + key = prop.Value + } else if strings.EqualFold(prop.Name, "Data") { + value = prop.Value + } + } + if len(key) > 0 { + ret[key] = value + } + } + + return ret, nil +} + +func translateKvpError(source error, illegalSuggestion string) error { + j, ok := source.(*wmiext.JobError) + + if !ok { + return source + } + + var message string + switch j.ErrorCode { + case KvpOperationFailed: + message = "Operation failed" + case KvpAccessDenied: + message = "Access denied" + case KvpNotSupported: + message = "Not supported" + case KvpStatusUnknown: + message = "Status is unknown" + case KvpTimeoutOccurred: + message = "Timeout occurred" + case KvpIllegalArgument: + message = "Illegal argument (" + illegalSuggestion + ")" + case KvpSystemInUse: + message = "System is in use" + case KvpInvalidState: + message = "Invalid state for this operation" + case KvpIncorrectDataType: + message = "Incorrect data type" + case KvpSystemNotAvailable: + message = "System is not available" + case KvpOutOfMemory: + message = "Out of memory" + case KvpNotFound: + message = "Not found" + default: + return source + } + + return &KvpError{j.ErrorCode, message} +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/memory_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/memory_settings.go new file mode 100644 index 0000000000..960694eb70 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/memory_settings.go @@ -0,0 +1,56 @@ +//go:build windows +// +build windows + +package hypervctl + +import "fmt" + +const MemoryResourceType = "Microsoft:Hyper-V:Memory" + +type MemorySettings struct { + S__PATH string + InstanceID string + Caption string // = "Memory Default Settings" + Description string // = "Describes the default settings for the memory resources." + ElementName string + ResourceType uint16 // = 4 + OtherResourceType string + ResourceSubType string // = "Microsoft:Hyper-V:Memory" + PoolID string + ConsumerVisibility uint16 + HostResource []string + HugePagesEnabled bool + AllocationUnits string // = "byte * 2^20" + VirtualQuantity uint64 + Reservation uint64 + Limit uint64 + Weight uint32 + AutomaticAllocation bool // = True + AutomaticDeallocation bool // = True + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualQuantityUnits string // = "byte * 2^20" + DynamicMemoryEnabled bool + TargetMemoryBuffer uint32 + IsVirtualized bool // = True + SwapFilesInUse bool + MaxMemoryBlocksPerNumaNode uint64 + SgxSize uint64 + SgxEnabled bool +} + +func createMemorySettings(settings *MemorySettings) (string, error) { + str, err := createResourceSettingGeneric(settings, MemoryResourceType) + if err != nil { + err = fmt.Errorf("could not create memory settings: %w", err) + } + return str, err +} + +func fetchDefaultMemorySettings() (*MemorySettings, error) { + settings := &MemorySettings{} + return settings, populateDefaults(MemoryResourceType, settings) +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/network_settings_builder.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/network_settings_builder.go new file mode 100644 index 0000000000..ed5abd6371 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/network_settings_builder.go @@ -0,0 +1,86 @@ +//go:build windows +// +build windows + +package hypervctl + +type NetworkSettingsBuilder struct { + systemSettings *SystemSettings + err error +} + +type SyntheticEthernetPortSettingsBuilder struct { + networkSettingsBuilder *NetworkSettingsBuilder + portSettings *SyntheticEthernetPortSettings + err error +} + +type EthernetPortAllocationSettingsBuilder struct { + portSettingsBuilder *SyntheticEthernetPortSettingsBuilder + allocSettings *EthernetPortAllocationSettings + err error +} + +func NewNetworkSettingsBuilder(systemSettings *SystemSettings) *NetworkSettingsBuilder { + return &NetworkSettingsBuilder{systemSettings: systemSettings} +} + +func (builder *NetworkSettingsBuilder) AddSyntheticEthernetPort(beforeAdd func(*SyntheticEthernetPortSettings)) *SyntheticEthernetPortSettingsBuilder { + if builder.err != nil { + return &SyntheticEthernetPortSettingsBuilder{networkSettingsBuilder: builder, err: builder.err} + } + + portSettings, err := builder.systemSettings.AddSyntheticEthernetPort(beforeAdd) + builder.setErr(err) + + return &SyntheticEthernetPortSettingsBuilder{ + networkSettingsBuilder: builder, + portSettings: portSettings, + err: err, + } +} + +func (builder *SyntheticEthernetPortSettingsBuilder) AddEthernetPortAllocation(switchName string) *EthernetPortAllocationSettingsBuilder { + if builder.err != nil { + return &EthernetPortAllocationSettingsBuilder{portSettingsBuilder: builder, err: builder.err} + } + + allocSettings, err := builder.portSettings.DefineEthernetPortConnection(switchName) + builder.setErr(err) + + return &EthernetPortAllocationSettingsBuilder{ + portSettingsBuilder: builder, + allocSettings: allocSettings, + err: err, + } +} + +func (builder *SyntheticEthernetPortSettingsBuilder) Finish() *NetworkSettingsBuilder { + return builder.networkSettingsBuilder +} + +func (builder *EthernetPortAllocationSettingsBuilder) Finish() *SyntheticEthernetPortSettingsBuilder { + return builder.portSettingsBuilder +} + +func (builder *NetworkSettingsBuilder) setErr(err error) { + builder.err = err +} + +func (builder *SyntheticEthernetPortSettingsBuilder) setErr(err error) { + builder.err = err + builder.networkSettingsBuilder.setErr(err) +} + +func (builder *EthernetPortAllocationSettingsBuilder) Get(s **EthernetPortAllocationSettings) *EthernetPortAllocationSettingsBuilder { + *s = builder.allocSettings + return builder +} + +func (builder *SyntheticEthernetPortSettingsBuilder) Get(s **SyntheticEthernetPortSettings) *SyntheticEthernetPortSettingsBuilder { + *s = builder.portSettings + return builder +} + +func (builder *NetworkSettingsBuilder) Complete() error { + return builder.err +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/processor_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/processor_settings.go new file mode 100644 index 0000000000..9c0d6d95e8 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/processor_settings.go @@ -0,0 +1,98 @@ +//go:build windows +// +build windows + +package hypervctl + +import "fmt" + +const ProcessorResourceType = "Microsoft:Hyper-V:Processor" + +/* +AllocationUnits : percent / 1000 +AllowACountMCount : True +AutomaticAllocation : True +AutomaticDeallocation : True +Caption : Processor +Connection : +ConsumerVisibility : 3 +CpuGroupId : 00000000-0000-0000-0000-000000000000 +Description : Settings for Microsoft Virtual Processor. +DisableSpeculationControls : False +ElementName : Processor +EnableHostResourceProtection : False +EnableLegacyApicMode : False +EnablePageShattering : 0 +EnablePerfmonIpt : False +EnablePerfmonLbr : False +EnablePerfmonPebs : False +EnablePerfmonPmu : False +ExposeVirtualizationExtensions : False +HideHypervisorPresent : False +HostResource : +HwThreadsPerCore : 0 +InstanceID : Microsoft:B5314955-3924-42BA-ABF9-793993D340A0\b637f346-6a0e-4dec-af52-bd70cb80a21d\0 +Limit : 100000 +LimitCPUID : False +LimitProcessorFeatures : False +MappingBehavior : +MaxNumaNodesPerSocket : 1 +MaxProcessorsPerNumaNode : 4 +OtherResourceType : +Parent : +PoolID : +Reservation : 0 +ResourceSubType : Microsoft:Hyper-V:Processor +ResourceType : 3 +VirtualQuantity : 2 +VirtualQuantityUnits : count +Weight : 100 +*/ + +type ProcessorSettings struct { + S__PATH string + InstanceID string + Caption string // = "Processor" + Description string // = "A logical processor of the hypervisor running on the host computer system." + ElementName string + ResourceType uint16 // = 3 + OtherResourceType string + ResourceSubType string // = "Microsoft:Hyper-V:Processor" + PoolID string + ConsumerVisibility uint16 + HostResource []string + AllocationUnits string // = "percent / 1000" + VirtualQuantity uint64 // = "count" + Reservation uint64 // = 0 + Limit uint64 // = 100000 + Weight uint32 // = 100 + AutomaticAllocation bool // = True + AutomaticDeallocation bool // = True + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualQuantityUnits string // = "count" + LimitCPUID bool + HwThreadsPerCore uint64 + LimitProcessorFeatures bool + MaxProcessorsPerNumaNode uint64 + MaxNumaNodesPerSocket uint64 + EnableHostResourceProtection bool + CpuGroupId string + HideHypervisorPresent bool + ExposeVirtualizationExtensions bool +} + +func fetchDefaultProcessorSettings() (*ProcessorSettings, error) { + settings := &ProcessorSettings{} + return settings, populateDefaults(ProcessorResourceType, settings) +} + +func createProcessorSettings(settings *ProcessorSettings) (string, error) { + str, err := createResourceSettingGeneric(settings, ProcessorResourceType) + if err != nil { + err = fmt.Errorf("could not create processor settings: %w", err) + } + return str, err +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/resources_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/resources_settings.go new file mode 100644 index 0000000000..eda4b25238 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/resources_settings.go @@ -0,0 +1,135 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "errors" + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +type ResourceSettings struct { + S__PATH string + InstanceID string // = "Microsoft:GUID\DeviceSpecificData" + Caption string + Description string + ElementName string + ResourceType uint16 + OtherResourceType string + ResourceSubType string + PoolID string + ConsumerVisibility uint16 + HostResource []string + AllocationUnits string + VirtualQuantity uint64 + Reservation uint64 + Limit uint64 + Weight uint32 + AutomaticAllocation bool + AutomaticDeallocation bool + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualQuantityUnits string // = "count" + VirtualSystemIdentifiers []string // = { "GUID" } +} + +func (s *ResourceSettings) setParent(parent string) { + s.Parent = parent +} + +func (s *ResourceSettings) setAddressOnParent(address string) { + s.AddressOnParent = address +} + +func (s *ResourceSettings) Path() string { + return s.S__PATH +} + +func createResourceSettingGeneric(settings interface{}, resourceType string) (string, error) { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return "", err + } + + ref, err := findResourceDefaults(service, resourceType) + if err != nil { + return "", err + } + + resource, err := service.GetObject(ref) + if err != nil { + return "", err + } + + defer resource.Close() + resource, err = resource.CloneInstance() + if err != nil { + return "", err + } + defer resource.Close() + + if err = resource.PutAll(settings); err != nil { + return "", err + } + + return resource.GetCimText(), nil +} + +func populateDefaults(subType string, settings interface{}) error { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + ref, err := findResourceDefaults(service, subType) + if err != nil { + return err + } + + return service.GetObjectAsObject(ref, settings) +} + +func findResourceDefaults(service *wmiext.Service, subType string) (string, error) { + wql := fmt.Sprintf("SELECT * FROM Msvm_AllocationCapabilities WHERE ResourceSubType = '%s'", subType) + instance, err := service.FindFirstInstance(wql) + if err != nil { + return "", err + } + defer instance.Close() + + path, err := instance.Path() + if err != nil { + return "", err + } + + enum, err := service.ExecQuery(fmt.Sprintf("references of {%s} where ResultClass = Msvm_SettingsDefineCapabilities", path)) + if err != nil { + return "", err + } + defer enum.Close() + + for { + entry, err := enum.Next() + if err != nil { + return "", err + } + if entry == nil { + return "", errors.New("could not find settings definition for resource") + } + + value, vErr := entry.GetAsUint("ValueRole") + ref, pErr := entry.GetAsString("PartComponent") + entry.Close() + if vErr == nil && pErr == nil && value == 0 { + return ref, nil + } + } +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/scsi_controller.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/scsi_controller.go new file mode 100644 index 0000000000..d38bfacb60 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/scsi_controller.go @@ -0,0 +1,69 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +type ScsiControllerSettings struct { + ResourceSettings + systemSettings *SystemSettings +} + +type driveAssociation interface { + setParent(parent string) + setAddressOnParent(address string) +} + +func (c *ScsiControllerSettings) AddSyntheticDiskDrive(slot uint) (*SyntheticDiskDriveSettings, error) { + drive := &SyntheticDiskDriveSettings{} + if err := c.createSyntheticDriveInternal(slot, drive, SyntheticDiskDriveType); err != nil { + return nil, err + } + drive.systemSettings = c.systemSettings + drive.controllerSettings = c + return drive, nil +} + +func (c *ScsiControllerSettings) AddSyntheticDvdDrive(slot uint) (*SyntheticDvdDriveSettings, error) { + drive := &SyntheticDvdDriveSettings{} + if err := c.createSyntheticDriveInternal(slot, drive, SyntheticDvdDriveType); err != nil { + return nil, err + } + drive.systemSettings = c.systemSettings + drive.controllerSettings = c + return drive, nil +} + +func (c *ScsiControllerSettings) createSyntheticDriveInternal(slot uint, settings driveAssociation, resourceType string) error { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + if err = populateDefaults(resourceType, settings); err != nil { + return err + } + + settings.setParent(c.Path()) + settings.setAddressOnParent(fmt.Sprintf("%d", slot)) + + driveResource, err := createResourceSettingGeneric(settings, resourceType) + if err != nil { + return err + } + + path, err := addResource(service, c.systemSettings.Path(), driveResource) + if err != nil { + return err + } + + err = service.GetObjectAsObject(path, settings) + return err +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/storage_alloc_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/storage_alloc_settings.go new file mode 100644 index 0000000000..e6baa54301 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/storage_alloc_settings.go @@ -0,0 +1,61 @@ +//go:build windows +// +build windows + +package hypervctl + +type StorageAllocationSettings struct { + S__PATH string + InstanceID string + Caption string // = "Hard Disk Image Default Settings" + Description string // = "Describes the default settings for the hard disk image resources" + ElementName string + ResourceType uint16 + OtherResourceType string + ResourceSubType string + PoolID string + ConsumerVisibility uint16 + HostResource []string + AllocationUnits string + VirtualQuantity uint64 + Limit uint64 // = 1 + Weight uint32 + StorageQoSPolicyID string + AutomaticAllocation bool + AutomaticDeallocation bool + Parent string + Connection []string + Address string + MappingBehavior uint16 + AddressOnParent string + VirtualResourceBlockSize uint64 + VirtualQuantityUnits string // = "count(fixed size block)" + Access uint16 + HostResourceBlockSize uint64 + Reservation uint64 + HostExtentStartingAddress uint64 + HostExtentName string + HostExtentNameFormat uint16 + OtherHostExtentNameFormat string + HostExtentNameNamespace uint16 + OtherHostExtentNameNamespace string + IOPSLimit uint64 + IOPSReservation uint64 + IOPSAllocationUnits string + PersistentReservationsSupported bool + CachingMode uint16 + SnapshotId string // = "" + IgnoreFlushes bool + WriteHardeningMethod uint16 +} + +func (s *StorageAllocationSettings) setParent(parent string) { + s.Parent = parent +} + +func (s *StorageAllocationSettings) setHostResource(resource []string) { + s.HostResource = resource +} + +func (s *StorageAllocationSettings) Path() string { + return s.S__PATH +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/summary.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/summary.go new file mode 100644 index 0000000000..c8abcef1b0 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/summary.go @@ -0,0 +1,202 @@ +//go:build windows +// +build windows + +package hypervctl + +import "time" + +const ( + SummaryRequestName = 0 + SummaryRequestElementName = 1 + SummaryRequestCreationTime = 2 + SummaryRequestNotes = 3 + SummaryRequestProcessors = 4 + SummaryRequestSmallThumbnail = 5 + SummaryRequestMediumThumbnail = 6 + SummaryRequestLargeThumbnail = 7 + SummaryRequestAllocatedGPU = 8 + SummaryRequestVirtualSwitchNames = 9 + SummaryRequestVersion = 10 + SummaryRequestShielded = 11 + SummaryRequestEnabledState = 100 + SummaryRequestProcessorLoad = 101 + SummaryRequestProcessorLoadHistory = 102 + SummaryRequestMemoryUsage = 103 + SummaryRequestHeartbeat = 104 + SummaryRequestUptime = 105 + SummaryRequestGuestOperatingSystem = 106 + SummaryRequestSnapshots = 107 + SummaryRequestAsynchronousTasks = 108 + SummaryRequestHealthState = 109 + SummaryRequestOperationalStatus = 110 + SummaryRequestStatusDescriptions = 111 + SummaryRequestMemoryAvailable = 112 + SummaryRequestMemoryBuffer = 113 + SummaryRequestReplicationMode = 114 + SummaryRequestReplicationState = 115 + SummaryRequestReplicationHealth = 116 + SummaryRequestApplicationHealth = 117 + SummaryRequestReplicationStateEx = 118 + SummaryRequestReplicationHealthEx = 119 + SummaryRequestSwapFilesInUse = 120 + SummaryRequestIntegrationServicesVersionState = 121 + SummaryRequestReplicationProvider = 122 + SummaryRequestMemorySpansPhysicalNumaNodes = 123 + SummaryRequestIntegrationServicesVersionState2 = 132 + SummaryRequestOtherEnabledState = 132 +) + +type SummaryRequestSet []uint + +var ( + + // SummaryRequestCommon includes a smaller subset of commonly used fields + SummaryRequestCommon = SummaryRequestSet{ + SummaryRequestName, + SummaryRequestElementName, + SummaryRequestCreationTime, + SummaryRequestNotes, + SummaryRequestProcessors, + SummaryRequestEnabledState, + SummaryRequestProcessorLoad, + SummaryRequestMemoryUsage, + SummaryRequestHeartbeat, + SummaryRequestUptime, + SummaryRequestGuestOperatingSystem, + SummaryRequestHealthState, + SummaryRequestOperationalStatus, + SummaryRequestStatusDescriptions, + SummaryRequestMemoryAvailable, + SummaryRequestMemoryBuffer, + SummaryRequestSwapFilesInUse, + } + + // SummaryRequestNearAll includes everything but load history and thumbnails + SummaryRequestNearAll = SummaryRequestSet{ + SummaryRequestName, + SummaryRequestElementName, + SummaryRequestCreationTime, + SummaryRequestNotes, + SummaryRequestProcessors, + SummaryRequestAllocatedGPU, + SummaryRequestVirtualSwitchNames, + SummaryRequestVersion, + SummaryRequestShielded, + SummaryRequestEnabledState, + SummaryRequestProcessorLoad, + SummaryRequestMemoryUsage, + SummaryRequestHeartbeat, + SummaryRequestUptime, + SummaryRequestGuestOperatingSystem, + SummaryRequestSnapshots, + SummaryRequestAsynchronousTasks, + SummaryRequestHealthState, + SummaryRequestOperationalStatus, + SummaryRequestStatusDescriptions, + SummaryRequestMemoryAvailable, + SummaryRequestMemoryBuffer, + SummaryRequestReplicationMode, + SummaryRequestReplicationState, + SummaryRequestReplicationHealth, + SummaryRequestApplicationHealth, + SummaryRequestReplicationStateEx, + SummaryRequestReplicationHealthEx, + SummaryRequestSwapFilesInUse, + SummaryRequestIntegrationServicesVersionState, + SummaryRequestReplicationProvider, + SummaryRequestMemorySpansPhysicalNumaNodes, + SummaryRequestIntegrationServicesVersionState2, + SummaryRequestOtherEnabledState, + } +) + +// SummaryInformation https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-summaryinformation +type SummaryInformation struct { + InstanceID string + AllocatedGPU string + Shielded bool + AsynchronousTasks []ConcreteJob + CreationTime time.Time + ElementName string + EnabledState uint16 + OtherEnabledState string + GuestOperatingSystem string + HealthState uint16 + Heartbeat uint16 + MemoryUsage uint64 + MemoryAvailable int32 + AvailableMemoryBuffer int32 + SwapFilesInUse bool + Name string + Notes string + Version string + NumberOfProcessors uint16 + OperationalStatus []uint16 + ProcessorLoad uint16 + ProcessorLoadHistory []uint16 + Snapshots []SystemSettings + StatusDescriptions []string + ThumbnailImage []uint8 + ThumbnailImageHeight uint16 + ThumbnailImageWidth uint16 + UpTime uint64 + ReplicationState uint16 + ReplicationStateEx []uint16 + ReplicationHealth uint16 + ReplicationHealthEx []uint16 + ReplicationMode uint16 + TestReplicaSystem string // REF to CIM_ComputerSystem + ApplicationHealth uint16 + IntegrationServicesVersionState uint16 + MemorySpansPhysicalNumaNodes bool + ReplicationProviderId []string + EnhancedSessionModeState uint16 + VirtualSwitchNames []string + VirtualSystemSubType string + HostComputerSystemName string +} + +// CIM_ConcreteJob https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-concretejob +type ConcreteJob struct { + InstanceID string + Caption string + Description string + ElementName string + InstallDate time.Time + Name string + OperationalStatus []uint16 // = { 2 } + StatusDescriptions []string // = { "OK" } + Status string + HealthState uint16 // = 5 + CommunicationStatus uint16 + DetailedStatus uint16 + OperatingStatus uint16 + PrimaryStatus uint16 + JobStatus string + TimeSubmitted time.Time + ScheduledStartTime time.Time + StartTime time.Time + ElapsedTime time.Duration + JobRunTimes uint32 + RunMonth uint8 + RunDay int8 + RunDayOfWeek int8 + RunStartInterval time.Time + LocalOrUtcTime uint16 + UntilTime time.Time + Notify string + Owner string + Priority uint32 + PercentComplete uint16 + DeleteOnCompletion bool + ErrorCode uint16 + ErrorDescription string + ErrorSummaryDescription string + RecoveryAction uint16 + OtherRecoveryAction string + JobState uint16 + TimeOfLastStateChange time.Time + TimeBeforeRemoval time.Duration // = + Cancellable bool + JobType uint16 +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings.go new file mode 100644 index 0000000000..cfb4207dc5 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings.go @@ -0,0 +1,207 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "fmt" + "time" + + "github.com/containers/libhvee/pkg/wmiext" +) + +type SystemSettings struct { + S__PATH string + InstanceID string + Caption string // = "Virtual Machine Settings" + Description string + ElementName string + VirtualSystemIdentifier string + VirtualSystemType string + Notes []string + CreationTime time.Time + ConfigurationID string + ConfigurationDataRoot string + ConfigurationFile string + SnapshotDataRoot string + SuspendDataRoot string + SwapFileDataRoot string + LogDataRoot string + AutomaticStartupAction uint16 // non-zero + AutomaticStartupActionDelay time.Duration + AutomaticStartupActionSequenceNumber uint16 + AutomaticShutdownAction uint16 // non-zero + AutomaticRecoveryAction uint16 // non-zero + RecoveryFile string + BIOSGUID string + BIOSSerialNumber string + BaseBoardSerialNumber string + ChassisSerialNumber string + Architecture string + ChassisAssetTag string + BIOSNumLock bool + BootOrder []uint16 + Parent string + UserSnapshotType uint16 // non-zero + IsSaved bool + AdditionalRecoveryInformation string + AllowFullSCSICommandSet bool + DebugChannelId uint32 + DebugPortEnabled uint16 + DebugPort uint32 + Version string + IncrementalBackupEnabled bool + VirtualNumaEnabled bool + AllowReducedFcRedundancy bool // = False + VirtualSystemSubType string + BootSourceOrder []string + PauseAfterBootFailure bool + NetworkBootPreferredProtocol uint16 // non-zero + GuestControlledCacheTypes bool + AutomaticSnapshotsEnabled bool + IsAutomaticSnapshot bool + GuestStateFile string + GuestStateDataRoot string + LockOnDisconnect bool + ParentPackage string + AutomaticCriticalErrorActionTimeout time.Duration + AutomaticCriticalErrorAction uint16 + ConsoleMode uint16 + SecureBootEnabled bool + SecureBootTemplateId string + LowMmioGapSize uint64 + HighMmioGapSize uint64 + EnhancedSessionTransportType uint16 +} + +func DefaultSystemSettings() *SystemSettings { + return &SystemSettings{ + // setup all non-zero settings + AutomaticStartupAction: 2, // no auto-start + AutomaticShutdownAction: 4, // shutdown + AutomaticRecoveryAction: 3, // restart + UserSnapshotType: 2, // no snapshotting + NetworkBootPreferredProtocol: 4096, // ipv4 for pxe + VirtualSystemSubType: "Microsoft:Hyper-V:SubType:2", + } + +} + +func (s *SystemSettings) Path() string { + return s.S__PATH +} + +func (s *SystemSettings) AddScsiController() (*ScsiControllerSettings, error) { + const scsiControllerType = "Microsoft:Hyper-V:Synthetic SCSI Controller" + controller := &ScsiControllerSettings{} + + if err := s.createSystemResourceInternal(controller, scsiControllerType, nil); err != nil { + return nil, err + } + + controller.systemSettings = s + return controller, nil +} + +func (s *SystemSettings) createSystemResourceInternal(settings interface{}, resourceType string, cb func()) error { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + if err = populateDefaults(resourceType, settings); err != nil { + return err + } + + if cb != nil { + cb() + } + + resourceStr, err := createResourceSettingGeneric(settings, resourceType) + if err != nil { + return err + } + + path, err := addResource(service, s.Path(), resourceStr) + if err != nil { + return err + } + + err = service.GetObjectAsObject(path, settings) + return err +} + +func (s *SystemSettings) AddSyntheticEthernetPort(beforeAdd func(*SyntheticEthernetPortSettings)) (*SyntheticEthernetPortSettings, error) { + const networkAdapterType = SyntheticEthernetPortResourceType + port := &SyntheticEthernetPortSettings{} + + var cb func() + if beforeAdd != nil { + cb = func() { + beforeAdd(port) + } + } + + if err := s.createSystemResourceInternal(port, networkAdapterType, cb); err != nil { + return nil, err + } + + port.systemSettings = s + return port, nil +} + +func addResource(service *wmiext.Service, systemSettingPath string, resourceSettings string) (string, error) { + vsms, err := service.GetSingletonInstance(VirtualSystemManagementService) + if err != nil { + return "", err + } + defer vsms.Close() + + var res int32 + var resultingSettings []string + var job *wmiext.Instance + err = vsms.BeginInvoke("AddResourceSettings"). + In("AffectedConfiguration", systemSettingPath). + In("ResourceSettings", []string{resourceSettings}). + Execute(). + Out("Job", &job). + Out("ResultingResourceSettings", &resultingSettings). + Out("ReturnValue", &res).End() + + if err != nil { + return "", fmt.Errorf("AddResourceSettings failed: %w", err) + } + + err = waitVMResult(res, service, job, "failed to add resource", nil) + + if len(resultingSettings) > 0 { + return resultingSettings[0], err + } + + return "", err +} + +func (s *SystemSettings) GetVM() (*VirtualMachine, error) { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return nil, err + } + defer service.Close() + + inst, err := service.FindFirstRelatedInstance(s.Path(), "Msvm_ComputerSystem") + if err != nil { + return nil, err + } + defer inst.Close() + + vm := &VirtualMachine{} + + if err = inst.GetAll(vm); err != nil { + return nil, err + } + + return vm, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings_builder.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings_builder.go new file mode 100644 index 0000000000..76b5995367 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/system_settings_builder.go @@ -0,0 +1,161 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +type SystemSettingsBuilder struct { + systemSettings *SystemSettings + processorSettings *ProcessorSettings + memorySettings *MemorySettings + err error +} + +func NewSystemSettingsBuilder() *SystemSettingsBuilder { + return &SystemSettingsBuilder{} +} + +func (builder *SystemSettingsBuilder) PrepareSystemSettings(name string, beforeAdd func(*SystemSettings)) *SystemSettingsBuilder { + if builder.err != nil { + return builder + } + + if builder.systemSettings == nil { + settings := DefaultSystemSettings() + settings.ElementName = name + builder.systemSettings = settings + } + + if beforeAdd != nil { + beforeAdd(builder.systemSettings) + } + + return builder +} + +func (builder *SystemSettingsBuilder) PrepareProcessorSettings(beforeAdd func(*ProcessorSettings)) *SystemSettingsBuilder { + if builder.err != nil { + return builder + } + + if builder.processorSettings == nil { + settings, err := fetchDefaultProcessorSettings() + if err != nil { + builder.err = err + return builder + } + builder.processorSettings = settings + } + + if beforeAdd != nil { + beforeAdd(builder.processorSettings) + } + + return builder +} + +func (builder *SystemSettingsBuilder) PrepareMemorySettings(beforeAdd func(*MemorySettings)) *SystemSettingsBuilder { + if builder.err != nil { + return builder + } + + if builder.memorySettings == nil { + settings, err := fetchDefaultMemorySettings() + if err != nil { + builder.err = err + return builder + } + builder.memorySettings = settings + } + + if beforeAdd != nil { + beforeAdd(builder.memorySettings) + } + + return builder +} + +func (builder *SystemSettingsBuilder) Build() (*SystemSettings, error) { + var service *wmiext.Service + var err error + + if builder.PrepareSystemSettings("unnamed-vm", nil). + PrepareProcessorSettings(nil). + PrepareMemorySettings(nil).err != nil { + return nil, err + } + + if service, err = NewLocalHyperVService(); err != nil { + return nil, err + } + defer service.Close() + + systemSettingsInst, err := service.SpawnInstance("Msvm_VirtualSystemSettingData") + if err != nil { + return nil, err + } + defer systemSettingsInst.Close() + + err = systemSettingsInst.PutAll(builder.systemSettings) + if err != nil { + return nil, err + } + + memoryStr, err := createMemorySettings(builder.memorySettings) + if err != nil { + return nil, err + } + + processorStr, err := createProcessorSettings(builder.processorSettings) + if err != nil { + return nil, err + } + + vsms, err := service.GetSingletonInstance(VirtualSystemManagementService) + if err != nil { + return nil, err + } + defer vsms.Close() + + systemStr := systemSettingsInst.GetCimText() + + var job *wmiext.Instance + var res int32 + var resultingSystem string + err = vsms.BeginInvoke("DefineSystem"). + In("SystemSettings", systemStr). + In("ResourceSettings", []string{memoryStr, processorStr}). + Execute(). + Out("Job", &job). + Out("ResultingSystem", &resultingSystem). + Out("ReturnValue", &res).End() + + if err != nil { + return nil, fmt.Errorf("failed to define system: %w", err) + } + + err = waitVMResult(res, service, job, "failed to define system", nil) + if err != nil { + return nil, err + } + + newSettings, err := service.FindFirstRelatedInstance(resultingSystem, "Msvm_VirtualSystemSettingData") + if err != nil { + return nil, err + } + path, err := newSettings.Path() + if err != nil { + return nil, err + } + + if err = service.GetObjectAsObject(path, builder.systemSettings); err != nil { + return nil, err + } + + return builder.systemSettings, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vdvd_storage_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vdvd_storage_settings.go new file mode 100644 index 0000000000..ad5eceabe2 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vdvd_storage_settings.go @@ -0,0 +1,13 @@ +//go:build windows +// +build windows + +package hypervctl + +const VirtualDvdDiskType = "Microsoft:Hyper-V:Virtual CD/DVD Disk" + +type VirtualDvdDiskStorageSettings struct { + StorageAllocationSettings + + systemSettings *SystemSettings + driveSettings *SyntheticDvdDriveSettings +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd.go new file mode 100644 index 0000000000..c7310be952 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd.go @@ -0,0 +1,108 @@ +package hypervctl + +import ( + "encoding/xml" + "fmt" + "strconv" + "strings" + + "github.com/containers/common/pkg/strongunits" + "github.com/containers/libhvee/pkg/wmiext" +) + +// ResizeDisk takes a diskPath and strongly typed new size and uses powershell +// to change its size. There is no error protection for trying to size a disk +// smaller than the current size. +func ResizeDisk(diskPath string, newSize strongunits.GiB) error { + var ( + service *wmiext.Service + err error + job *wmiext.Instance + ret int32 + ) + + if service, err = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + imms, err := service.GetSingletonInstance("Msvm_ImageManagementService") + if err != nil { + return err + } + defer imms.Close() + + err = imms.BeginInvoke("ResizeVirtualHardDisk"). + In("Path", diskPath). + In("MaxInternalSize", int64(newSize.ToBytes())). + Execute(). + Out("Job", &job). + Out("ReturnValue", &ret). + End() + + if err != nil { + return fmt.Errorf("failed to resize disk: %w", err) + } + return waitVMResult(ret, service, job, "failed to resize disk", nil) +} + +func GetDiskSize(diskPath string) (strongunits.B, error) { + var ( + service *wmiext.Service + err error + job *wmiext.Instance + ret int32 + results string + ) + + if service, err = NewLocalHyperVService(); err != nil { + return 0, err + } + defer service.Close() + imms, err := service.GetSingletonInstance("Msvm_ImageManagementService") + if err != nil { + return 0, err + } + defer imms.Close() + + inv := imms.BeginInvoke("GetVirtualHardDiskSettingData"). + In("Path", diskPath). + Execute(). + Out("Job", &job). + Out("ReturnValue", &ret) + + if err := inv.Error(); err != nil { + return 0, fmt.Errorf("failed to get setting data for disk %s: %q", diskPath, err) + } + + if err := waitVMResult(ret, service, job, "failure waiting on result from disk settings", nil); err != nil { + return 0, err + } + + err = inv.Out("SettingData", &results).End() + if err != nil { + return 0, fmt.Errorf("failed to retrieve setting object payload for disk: %q", err) + } + + type CimSingleInstance struct { + XMLName xml.Name `xml:"INSTANCE"` + Properties []CimKvpItemProperty `xml:"PROPERTY"` + } + + diskSettings := CimSingleInstance{} + if err := xml.Unmarshal([]byte(results), &diskSettings); err != nil { + return 0, fmt.Errorf("unable to parse disk settings xml: %q", err) + } + + for _, prop := range diskSettings.Properties { + if strings.EqualFold(prop.Name, "MaxInternalSize") { + size, err := strconv.ParseUint(prop.Value, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse size in disk settings") + } + return strongunits.B(size), nil + } + } + + return 0, fmt.Errorf("disk settings was missing a size value") +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_settings.go new file mode 100644 index 0000000000..e6bd8b4bff --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_settings.go @@ -0,0 +1,28 @@ +//go:build windows +// +build windows + +package hypervctl + +import "time" + +type VirtualHardDiskSettings struct { + S__PATH string + InstanceID string + Caption string // = "Virtual Hard Disk Setting Data" + Description string // = "Setting Data for a Virtual Hard Disk" + ElementName string + Type uint16 + Format uint16 + Path string + ParentPath string + ParentTimestamp time.Time + ParentIdentifier string + MaxInternalSize uint64 + BlockSize uint32 + LogicalSectorSize uint32 + PhysicalSectorSize uint32 + VirtualDiskId string + DataAlignment uint64 + PmemAddressAbstractionType uint16 + IsPmemCompatible bool +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_storage_settings.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_storage_settings.go new file mode 100644 index 0000000000..0384408cec --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vhd_storage_settings.go @@ -0,0 +1,13 @@ +//go:build windows +// +build windows + +package hypervctl + +const VirtualHardDiskType = "Microsoft:Hyper-V:Virtual Hard Disk" + +type VirtualHardDiskStorageSettings struct { + StorageAllocationSettings + + systemSettings *SystemSettings + driveSettings *SyntheticDiskDriveSettings +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vm.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vm.go new file mode 100644 index 0000000000..7fcae23aff --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vm.go @@ -0,0 +1,629 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "bytes" + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/containers/libhvee/pkg/kvp/ginsu" + "github.com/containers/libhvee/pkg/wmiext" +) + +// delete this when close to being done +var ( + ErrNotImplemented = errors.New("function not implemented") +) + +type VirtualMachine struct { + S__PATH string `json:"-"` + S__CLASS string `json:"-"` + InstanceID string + Caption string + Description string + ElementName string + InstallDate time.Time + OperationalStatus []uint16 + StatusDescriptions []string + Status string + HealthState uint16 + CommunicationStatus uint16 + DetailedStatus uint16 + OperatingStatus uint16 + PrimaryStatus uint16 + EnabledState uint16 + OtherEnabledState string + RequestedState uint16 + EnabledDefault uint16 + TimeOfLastStateChange string + AvailableRequestedStates []uint16 + TransitioningToState uint16 + CreationClassName string + Name string + PrimaryOwnerName string + PrimaryOwnerContact string + Roles []string + NameFormat string + OtherIdentifyingInfo []string + IdentifyingDescriptions []string + Dedicated []uint16 + OtherDedicatedDescriptions []string + ResetCapability uint16 + PowerManagementCapabilities []uint16 + OnTimeInMilliseconds uint64 + ProcessID uint32 + TimeOfLastConfigurationChange string + NumberOfNumaNodes uint16 + ReplicationState uint16 + ReplicationHealth uint16 + ReplicationMode uint16 + FailedOverReplicationType uint16 + LastReplicationType uint16 + LastApplicationConsistentReplicationTime string + LastReplicationTime time.Time + LastSuccessfulBackupTime string + EnhancedSessionModeState uint16 + vmm *VirtualMachineManager +} + +func (vm *VirtualMachine) Path() string { + return vm.S__PATH +} +func (vm *VirtualMachine) SplitAndAddIgnition(keyPrefix string, ignRdr *bytes.Reader) error { + parts, err := ginsu.Dice(ignRdr) + if err != nil { + return err + } + for idx, val := range parts { + key := fmt.Sprintf("%s%d", keyPrefix, idx) + if err := vm.AddKeyValuePair(key, val); err != nil { + return err + } + } + return nil +} + +func (vm *VirtualMachine) AddKeyValuePair(key string, value string) error { + return vm.kvpOperation("AddKvpItems", key, value, false, "key already exists?") +} + +func (vm *VirtualMachine) ModifyKeyValuePair(key string, value string) error { + return vm.kvpOperation("ModifyKvpItems", key, value, false, "key invalid?") +} + +func (vm *VirtualMachine) PutKeyValuePair(key string, value string) error { + err := vm.AddKeyValuePair(key, value) + kvpError, ok := err.(*KvpError) + if !ok || kvpError.ErrorCode != KvpIllegalArgument { + return err + } + + return vm.ModifyKeyValuePair(key, value) +} + +func (vm *VirtualMachine) RemoveKeyValuePair(key string) error { + return vm.kvpOperation("RemoveKvpItems", key, "", false, "key invalid?") +} + +func (vm *VirtualMachine) RemoveKeyValuePairNoWait(key string) error { + return vm.kvpOperation("RemoveKvpItems", key, "", true, "key invalid?") +} + +func (vm *VirtualMachine) GetKeyValuePairs() (map[string]string, error) { + var service *wmiext.Service + var err error + + if service, err = NewLocalHyperVService(); err != nil { + return nil, err + } + + defer service.Close() + + i, err := service.FindFirstRelatedInstance(vm.Path(), "Msvm_KvpExchangeComponent") + if err != nil { + return nil, err + } + + defer i.Close() + + var path string + path, err = i.GetAsString("__PATH") + if err != nil { + return nil, err + + } + + i, err = service.FindFirstRelatedInstance(path, "Msvm_KvpExchangeComponentSettingData") + if err != nil { + return nil, err + } + defer i.Close() + + s, err := i.GetAsString("HostExchangeItems") + if err != nil { + return nil, err + } + + return parseKvpMapXml(s) +} + +func (vm *VirtualMachine) kvpOperation(op string, key string, value string, nowait bool, illegalSuggestion string) error { + var service *wmiext.Service + var vsms, job *wmiext.Instance + var ret int32 + var err error + + if service, err = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + vsms, err = service.GetSingletonInstance(VirtualSystemManagementService) + if err != nil { + return err + } + defer vsms.Close() + + itemStr, err := createKvpItem(service, key, value) + if err != nil { + return err + } + + execution := vsms.BeginInvoke(op). + In("TargetSystem", vm.Path()). + In("DataItems", []string{itemStr}). + Execute(). + Out("ReturnValue", &ret). + Out("Job", &job) + + if err := execution.End(); err != nil { + return fmt.Errorf("%s execution failed: %w", op, err) + } + + defer job.Close() + if ret == 0 || (nowait && ret == 4096) { + return nil + } + + if ret == 4096 { + err = wmiext.WaitJob(service, job) + } else { + err = &wmiext.JobError{ErrorCode: int(ret)} + } + + return translateKvpError(err, illegalSuggestion) +} + +func waitVMResult(res int32, service *wmiext.Service, job *wmiext.Instance, errorMsg string, translate func(int) error) error { + var err error + + switch res { + case 0: + return nil + case 4096: + err = wmiext.WaitJob(service, job) + defer job.Close() + default: + if translate != nil { + return translate(int(res)) + } + + return fmt.Errorf("%s (result code %d)", errorMsg, res) + } + + if err != nil { + desc, _ := job.GetAsString("ErrorDescription") + desc = strings.Replace(desc, "\n", " ", -1) + return fmt.Errorf("%s: %w (%s)", errorMsg, err, desc) + } + + return err +} + +func (vm *VirtualMachine) StopWithForce() error { + return vm.stop(true) +} + +func (vm *VirtualMachine) Stop() error { + return vm.stop(false) +} + +func (vm *VirtualMachine) stop(force bool) error { + if !Enabled.equal(vm.EnabledState) { + return ErrMachineNotRunning + } + var ( + err error + res int32 + srv *wmiext.Service + ) + if srv, err = NewLocalHyperVService(); err != nil { + return err + } + wmiInst, err := srv.FindFirstRelatedInstance(vm.Path(), "Msvm_ShutdownComponent") + if err != nil { + return err + } + // https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-shutdowncomponent-initiateshutdown + err = wmiInst.BeginInvoke("InitiateShutdown"). + In("Reason", "User requested"). + In("Force", force). + Execute(). + Out("ReturnValue", &res).End() + if err != nil { + return err + } + + if res != 0 { + return translateShutdownError(int(res)) + } + + // Wait for vm to actually *be* down + for i := 0; i < 200; i++ { + refreshVM, err := vm.vmm.GetMachine(vm.ElementName) + if err != nil { + return err + } + if refreshVM.State() == Disabled { + break + } + time.Sleep(50 * time.Millisecond) + } + return nil +} + +func (vm *VirtualMachine) Start() error { + var ( + srv *wmiext.Service + err error + job *wmiext.Instance + res int32 + ) + + if s := vm.EnabledState; !Disabled.equal(s) { + if Enabled.equal(s) { + return ErrMachineAlreadyRunning + } else if Starting.equal(s) { + return ErrMachineAlreadyRunning + } + return errors.New("machine not in a state to start") + } + + if srv, err = getService(srv); err != nil { + return err + } + defer srv.Close() + + instance, err := srv.GetObject(vm.Path()) + if err != nil { + return err + } + defer instance.Close() + + // https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/cim-concretejob-requeststatechange + if err := instance.BeginInvoke("RequestStateChange"). + In("RequestedState", uint16(start)). + In("TimeoutPeriod", &time.Time{}). + Execute(). + Out("Job", &job). + Out("ReturnValue", &res).End(); err != nil { + return err + } + return waitVMResult(res, srv, job, "failed to start vm", nil) +} + +func getService(_ *wmiext.Service) (*wmiext.Service, error) { + // any reason why when we instantiate a vm, we should NOT just embed a service? + return NewLocalHyperVService() +} + +func (vm *VirtualMachine) GetConfig(diskPath string) (*HyperVConfig, error) { + var ( + diskSize uint64 + ) + summary, err := vm.GetSummaryInformation(SummaryRequestCommon) + if err != nil { + return nil, err + } + + // Grabbing actual disk size + diskPathInfo, err := os.Stat(diskPath) + if err != nil { + return nil, err + } + diskSize = uint64(diskPathInfo.Size()) + mem := MemorySettings{} + if err := vm.getMemorySettings(&mem); err != nil { + return nil, err + } + + config := HyperVConfig{ + Hardware: HardwareConfig{ + // TODO we could implement a getProcessorSettings like we did for memory + CPUs: summary.NumberOfProcessors, + DiskPath: diskPath, + DiskSize: diskSize, + Memory: mem.Limit, + }, + Status: Statuses{ + Created: vm.InstallDate, + LastUp: time.Time{}, + Running: Enabled.equal(vm.EnabledState), + Starting: vm.IsStarting(), + State: EnabledState(vm.EnabledState), + }, + } + return &config, nil +} + +// GetSummaryInformation returns the live VM summary information for this virtual machine. +// The requestedFields parameter controls which fields of summary information are populated. +// SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this +// parameter +func (vm *VirtualMachine) GetSummaryInformation(requestedFields SummaryRequestSet) (*SummaryInformation, error) { + service, err := NewLocalHyperVService() + if err != nil { + return nil, err + } + defer service.Close() + + instance, err := vm.fetchSystemSettingsInstance(service) + if err != nil { + return nil, err + } + defer instance.Close() + + path, err := instance.Path() + if err != nil { + return nil, err + } + + result, err := vm.vmm.getSummaryInformation(path, requestedFields) + if err != nil { + return nil, err + } + + if len(result) < 1 { + return nil, errors.New("summary information search returned an empty result set") + } + + return &result[0], nil +} + +// NewVirtualMachine creates a new vm in hyperv +// decided to not return a *VirtualMachine here because of how Podman is +// likely to use this. this could be easily added if desirable +func (vmm *VirtualMachineManager) NewVirtualMachine(name string, config *HardwareConfig) error { + exists, err := vmm.Exists(name) + if err != nil { + return err + } + if exists { + return ErrMachineAlreadyExists + } + + // TODO I gotta believe there are naming restrictions for vms in hyperv? + // TODO If something fails during creation, do we rip things down or follow precedent from other machines? user deletes things + + systemSettings, err := NewSystemSettingsBuilder(). + PrepareSystemSettings(name, nil). + PrepareMemorySettings(func(ms *MemorySettings) { + //ms.DynamicMemoryEnabled = false + //ms.VirtualQuantity = 8192 // Startup memory + //ms.Reservation = config.Memory // min + + // The API seems to require both of these even + // when not using dynamic memory + ms.Limit = config.Memory + ms.VirtualQuantity = config.Memory + }). + PrepareProcessorSettings(func(ps *ProcessorSettings) { + ps.VirtualQuantity = uint64(config.CPUs) // 4 cores + }). + Build() + if err != nil { + return err + } + + if err := NewDriveSettingsBuilder(systemSettings). + AddScsiController(). + AddSyntheticDiskDrive(0). + DefineVirtualHardDisk(config.DiskPath, func(vhdss *VirtualHardDiskStorageSettings) { + // set extra params like + // vhdss.IOPSLimit = 5000 + }). + Finish(). // disk + Finish(). // drive + //AddSyntheticDvdDrive(1). + //DefineVirtualDvdDisk(isoFile). + //Finish(). // disk + //Finish(). // drive + Finish(). // controller + Complete(); err != nil { + return err + } + // Add default network connection + if config.Network { + if err := NewNetworkSettingsBuilder(systemSettings). + AddSyntheticEthernetPort(nil). + AddEthernetPortAllocation(""). // "" = connect to default switch + Finish(). // allocation + Finish(). // port + Complete(); err != nil { + return err + } + } + return nil +} + +func (vm *VirtualMachine) fetchSystemSettingsInstance(service *wmiext.Service) (*wmiext.Instance, error) { + // When a settings snapshot is taken there are multiple associations, use only the realized/active version + return service.FindFirstRelatedInstanceThrough(vm.Path(), "Msvm_VirtualSystemSettingData", "Msvm_SettingsDefineState") +} + +func (vm *VirtualMachine) fetchExistingResourceSettings(service *wmiext.Service, resourceType string, resourceSettings interface{}) error { + const errFmt = "could not fetch resource settings (%s): %w" + // When a settings snapshot is taken there are multiple associations, use only the realized/active version + instance, err := vm.fetchSystemSettingsInstance(service) + if err != nil { + return fmt.Errorf(errFmt, resourceType, err) + } + defer instance.Close() + + path, err := instance.Path() + if err != nil { + return fmt.Errorf(errFmt, resourceType, err) + + } + + return service.FindFirstRelatedObject(path, resourceType, resourceSettings) +} + +func (vm *VirtualMachine) getMemorySettings(m *MemorySettings) error { + service, err := NewLocalHyperVService() + if err != nil { + return err + } + defer service.Close() + return vm.fetchExistingResourceSettings(service, "Msvm_MemorySettingData", m) +} + +// Update processor and/or mem +func (vm *VirtualMachine) UpdateProcessorMemSettings(updateProcessor func(*ProcessorSettings), updateMemory func(*MemorySettings)) error { + service, err := NewLocalHyperVService() + if err != nil { + return err + } + defer service.Close() + + proc := &ProcessorSettings{} + mem := &MemorySettings{} + + var settings []string + if updateProcessor != nil { + err = vm.fetchExistingResourceSettings(service, "Msvm_ProcessorSettingData", proc) + if err != nil { + return err + } + + updateProcessor(proc) + + processorStr, err := createProcessorSettings(proc) + if err != nil { + return err + } + settings = append(settings, processorStr) + } + + if updateMemory != nil { + if err := vm.getMemorySettings(mem); err != nil { + return err + } + + updateMemory(mem) + + memStr, err := createMemorySettings(mem) + if err != nil { + return err + } + + settings = append(settings, memStr) + } + + if len(settings) < 1 { + return nil + } + + vsms, err := service.GetSingletonInstance("Msvm_VirtualSystemManagementService") + if err != nil { + return err + } + defer vsms.Close() + + var job *wmiext.Instance + var res int32 + err = vsms.BeginInvoke("ModifyResourceSettings"). + In("ResourceSettings", settings). + Execute(). + Out("Job", &job). + Out("ReturnValue", &res). + End() + + if err != nil { + return fmt.Errorf("failed to modify resource settings: %w", err) + } + + return waitVMResult(res, service, job, "failed to modify resource settings", translateModifyError) +} + +func (vm *VirtualMachine) remove() (int32, error) { + var ( + err error + res int32 + srv *wmiext.Service + ) + + refreshVM, err := vm.vmm.GetMachine(vm.ElementName) + if err != nil { + return 0, err + } + // Check for disabled/stopped state + if !Disabled.equal(refreshVM.EnabledState) { + return -1, ErrMachineStateInvalid + } + if srv, err = NewLocalHyperVService(); err != nil { + return -1, err + } + + vsms, err := srv.GetSingletonInstance("Msvm_VirtualSystemManagementService") + if err != nil { + return -1, err + } + defer vsms.Close() + + var ( + job *wmiext.Instance + ) + + // https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/cim-virtualsystemmanagementservice-destroysystem + if err := vsms.BeginInvoke("DestroySystem"). + In("AffectedSystem", vm.Path()). + Execute(). + Out("Job", &job). + Out("ReturnValue", &res).End(); err != nil { + return -1, err + } + + // do i have this correct? you can get an error without a result? + if err := waitVMResult(res, srv, job, "failed to remove vm", nil); err != nil { + return -1, err + } + return res, nil +} + +func (vm *VirtualMachine) Remove(diskPath string) error { + if _, err := vm.remove(); err != nil { + return err + } + + // Remove disk only if we were given one + if len(diskPath) > 0 { + if err := os.Remove(diskPath); err != nil { + return err + } + } + return nil +} + +func (vm *VirtualMachine) State() EnabledState { + return EnabledState(vm.EnabledState) +} + +func (vm *VirtualMachine) IsStarting() bool { + return Starting.equal(vm.EnabledState) +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vm_config.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vm_config.go new file mode 100644 index 0000000000..4ea14d660a --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vm_config.go @@ -0,0 +1,117 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "time" +) + +type vmState uint16 + +const ( + // Changes the state to 'Running'. + start vmState = 2 + // Stops the job temporarily. The intention is to subsequently restart the job with 'Start'. It might be possible to + // enter the 'Service' state while suspended. (This is job-specific.) + suspend vmState = 3 //nolint: unused + // Stops the job cleanly, saves data, preserves the state, and shuts down all underlying processes' + // in an orderly manner. + terminate vmState = 4 //nolint: unused + //Terminates the job immediately with no requirement to save data or preserve the state. + kill vmState = 5 //nolint: unused +) + +type EnabledState uint16 + +const ( + // Unknown The state of the element could not be determined. + Unknown EnabledState = 0 + // Other No description + Other EnabledState = 1 + // Enabled The element is running. + Enabled EnabledState = 2 + // Disabled The element is turned off. + Disabled EnabledState = 3 + // ShuttingDown Shutting_Down The element is in the process of going to a Disabled state. + ShuttingDown EnabledState = 4 + // NotApplicable The element does not support being enabled or disabled. + NotApplicable EnabledState = 5 + // EnabledButOffline The element might be completing commands, and it will drop any new requests. + EnabledButOffline EnabledState = 6 + // InTest The element is in a test state. + InTest EnabledState = 7 + // Deferred The element might be completing commands, but it will queue any new requests. + Deferred EnabledState = 8 + // Quiesce The element is enabled but in a restricted mode. The behavior of the element is similar to the Enabled state + //(2), but it processes only a restricted set of commands. All other requests are queued. + Quiesce EnabledState = 9 + // Starting The element is in the process of going to an Enabled state (2). New requests are queued. + Starting EnabledState = 10 +) + +func (es EnabledState) String() string { + switch es { + case Unknown: + return "unknown" + case Other: + return "other" + case Enabled: + return "enabled" + case Disabled: + return "disabled" + case ShuttingDown: + return "shutting down" + case NotApplicable: + return "not applicable" + case EnabledButOffline: + return "enabled but offline" + case InTest: + return "in test" + case Deferred: + return "deferred" + case Quiesce: + return "quiesce" + } + return "starting" +} + +func (es EnabledState) equal(s uint16) bool { + return es == EnabledState(s) +} + +// HyperVConfig describes physical attributes of the machine +type HyperVConfig struct { + // Hardware configuration for HyperV + Hardware HardwareConfig + // state and descriptive info + Status Statuses +} + +type HardwareConfig struct { + // CPUs to be assigned to the VM + CPUs uint16 + // Diskpath is fully qualified location of the + // bootable disk image + DiskPath string + // Disk size in gigabytes assigned to the vm + DiskSize uint64 + // Memory in megabytes assigned to the vm + Memory uint64 + // Network is bool to add a Network Connection to the + // default network switch in Microsoft HyperV + Network bool +} + +type Statuses struct { + // time vm created + Created time.Time + // last time vm ran + LastUp time.Time + // is vm running + Running bool + // is vm starting up + Starting bool + // vm state + State EnabledState +} diff --git a/vendor/github.com/containers/libhvee/pkg/hypervctl/vmm.go b/vendor/github.com/containers/libhvee/pkg/hypervctl/vmm.go new file mode 100644 index 0000000000..ea7f239488 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/hypervctl/vmm.go @@ -0,0 +1,235 @@ +//go:build windows +// +build windows + +package hypervctl + +import ( + "errors" + "fmt" + + "github.com/containers/libhvee/pkg/wmiext" +) + +const ( + HyperVNamespace = "root\\virtualization\\v2" + VirtualSystemManagementService = "Msvm_VirtualSystemManagementService" + MsvmComputerSystem = "Msvm_ComputerSystem" +) + +// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-computersystem + +type VirtualMachineManager struct { +} + +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) { + // 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 = NewLocalHyperVService(); err != nil { + return []*VirtualMachine{}, err + } + defer service.Close() + + var enum *wmiext.Enum + if enum, err = service.ExecQuery(wql); err != nil { + return nil, err + } + defer enum.Close() + var vms []*VirtualMachine + + for { + settings, err := enum.Next() + if err != nil { + return vms, err + } + + // 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 { + return false, err + } + for _, i := range vms { + // TODO should case be honored or ignored? + if i.Name == name { + return true, nil + } + } + return false, nil +} + +// GetMachine is a stub to lookup and get settings for a VM +func (vmm *VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) { + return vmm.getMachine(name) +} + +// GetMachineExists looks for a machine defined in hyperv and returns it if it exists +func (vmm *VirtualMachineManager) GetMachineExists(name string) (bool, *VirtualMachine, error) { + vm, err := vmm.getMachine(name) + if err != nil { + if errors.Is(err, wmiext.ErrNoResults) { + return false, nil, nil + } + return false, nil, err + } + return true, vm, nil +} + +// getMachine looks up a single VM by name +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 = NewLocalHyperVService(); err != nil { + return vm, err + } + defer service.Close() + + var enum *wmiext.Enum + if enum, err = service.ExecQuery(fmt.Sprintf(wql, name)); err != nil { + return nil, err + } + defer enum.Close() + + settings, err := service.FindFirstInstance(fmt.Sprintf(wql, name)) + if err != nil { + if errors.Is(err, wmiext.ErrNoResults) { + return nil, err + } + return vm, fmt.Errorf("could not find virtual machine %q: %w", name, err) + } + defer settings.Close() + + 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 + } + + 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 = NewLocalHyperVService(); err != nil { + return err + } + defer service.Close() + + settings := &VirtualHardDiskSettings{} + settings.Format = 3 + settings.MaxInternalSize = maxSize + settings.Type = 3 + settings.Path = path + + instance, err := service.CreateInstance("Msvm_VirtualHardDiskSettingData", settings) + if err != nil { + return err + } + defer instance.Close() + settingsStr := instance.GetCimText() + + imms, err := service.GetSingletonInstance("Msvm_ImageManagementService") + if err != nil { + return err + } + defer imms.Close() + + var job *wmiext.Instance + var ret int32 + err = imms.BeginInvoke("CreateVirtualHardDisk"). + In("VirtualDiskSettingData", settingsStr). + Execute(). + Out("Job", &job). + Out("ReturnValue", &ret). + End() + + if err != nil { + return fmt.Errorf("failed to create vhdx: %w", err) + } + + return waitVMResult(ret, service, job, "failed to create vhdx", nil) +} + +// GetSummaryInformation returns the live VM summary information for all virtual machines. +// The requestedFields parameter controls which fields of summary information are populated. +// SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this +// parameter. +func (vmm *VirtualMachineManager) GetSummaryInformation(requestedFields SummaryRequestSet) ([]SummaryInformation, error) { + return vmm.getSummaryInformation("", requestedFields) +} + +func (vmm *VirtualMachineManager) getSummaryInformation(settingsPath string, requestedFields SummaryRequestSet) ([]SummaryInformation, error) { + var service *wmiext.Service + var err error + if service, err = NewLocalHyperVService(); err != nil { + return nil, err + } + defer service.Close() + + vsms, err := service.GetSingletonInstance(VirtualSystemManagementService) + if err != nil { + return nil, err + } + defer vsms.Close() + + var summary []SummaryInformation + + inv := vsms.BeginInvoke("GetSummaryInformation"). + In("RequestedInformation", []uint(requestedFields)) + + if len(settingsPath) > 0 { + inv.In("SettingData", []string{settingsPath}) + } + + err = inv.Execute(). + Out("SummaryInformation", &summary). + End() + + if err != nil { + return nil, err + } + + return summary, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/kvp/ginsu/util.go b/vendor/github.com/containers/libhvee/pkg/kvp/ginsu/util.go new file mode 100644 index 0000000000..49e5e27487 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/kvp/ginsu/util.go @@ -0,0 +1,46 @@ +package ginsu + +// +// This function is purposely "namespaces" for easy and +// lightweight vendoring. +// + +import ( + "bytes" +) + +const ( + // KvpValueMaxLen is the maximum real-world length of bytes that can + // be stored in the value of the wmi key-value pair data exchange + KvpValueMaxLen = int(990) +) + +// Dice takes input and splits it into a string array so it can be +// passed by hyperv and wmi. Each part must be less than the maximum size +// of a kvp value +func Dice(k *bytes.Reader) ([]string, error) { + var ( + // done is a simple bool indicator that we no longer + // need to iterate + done bool + parts []string + ) + for { + sl := make([]byte, KvpValueMaxLen) + n, err := k.Read(sl) + if err != nil { + return parts, err + } + // if we read and the length is less that the max read, + // then we are at the end + if n < KvpValueMaxLen { + sl = sl[0:n] + done = true + } + parts = append(parts, string(sl)) + if done { + break + } + } + return parts, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/array.go b/vendor/github.com/containers/libhvee/pkg/wmiext/array.go new file mode 100644 index 0000000000..3f059360cb --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/array.go @@ -0,0 +1,202 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "errors" + "fmt" + "reflect" + "unsafe" + + "github.com/go-ole/go-ole" +) + +func CreateStringArrayVariant(array []string) (ole.VARIANT, error) { + safeByteArray, err := safeArrayFromStringSlice(array) + if err != nil { + return ole.VARIANT{}, err + } + arrayVariant := ole.NewVariant(ole.VT_ARRAY|ole.VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray)))) + return arrayVariant, nil +} + +func CreateNumericArrayVariant(array interface{}, itemType ole.VT) (ole.VARIANT, error) { + safeArray, err := safeArrayFromNumericSlice(array, itemType) + if err != nil { + return ole.VARIANT{}, err + } + arrayVariant := ole.NewVariant(ole.VT_ARRAY|itemType, int64(uintptr(unsafe.Pointer(safeArray)))) + return arrayVariant, nil +} + +// The following safearray routines are unfortunately not yet exported from go-ole, +// so replicate them for now +func safeArrayCreateVector(variantType ole.VT, lowerBound int32, length uint32) (safearray *ole.SafeArray, err error) { + ret, _, _ := procSafeArrayCreateVector.Call( + uintptr(variantType), + uintptr(lowerBound), + uintptr(length)) + + if ret == 0 { // NULL return value + err = fmt.Errorf("could not create safe array") + } + safearray = (*ole.SafeArray)(unsafe.Pointer(ret)) + return +} + +func safeArrayFromNumericSlice(slice interface{}, itemType ole.VT) (*ole.SafeArray, error) { + sliceType := reflect.TypeOf(slice) + if sliceType.Kind() != reflect.Slice { + return nil, errors.New("expected a slice converting to safe array") + } + + val := reflect.ValueOf(slice) + + array, err := safeArrayCreateVector(itemType, 0, uint32(val.Len())) + if err != nil { + return nil, err + } + + // assignable holders used for conversion + var ( + vui1 uint8 + vui2 uint16 + vui4 uint32 + vui8 uint64 + vi1 int8 + vi2 int16 + vi4 int32 + vi8 int64 + ) + + for i := 0; i < val.Len(); i++ { + var data uintptr + item := val.Index(i) + switch itemType { + case ole.VT_UI1: + data = convertToUnsafeAddr(item, &vui1) + case ole.VT_UI2: + data = convertToUnsafeAddr(item, &vui2) + case ole.VT_UI4: + data = convertToUnsafeAddr(item, &vui4) + case ole.VT_UI8: + data = convertToUnsafeAddr(item, &vui8) + case ole.VT_I1: + data = convertToUnsafeAddr(item, &vi1) + case ole.VT_I2: + data = convertToUnsafeAddr(item, &vi2) + case ole.VT_I4: + data = convertToUnsafeAddr(item, &vi4) + case ole.VT_I8: + data = convertToUnsafeAddr(item, &vi8) + } + + err = safeArrayPutElement(array, int64(i), data) + if err != nil { + _ = safeArrayDestroy(array) + return nil, err + } + } + + return array, nil +} + +func convertToUnsafeAddr(src reflect.Value, target interface{}) uintptr { + val := reflect.ValueOf(target) + val = val.Elem() + val.Set(src.Convert(val.Type())) + return val.UnsafeAddr() +} + +func safeArrayDestroy(safearray *ole.SafeArray) (err error) { + ret, _, _ := procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray))) + + if ret != 0 { + return NewWmiError(ret) + } + + return nil +} + +func safeArrayPutElement(safearray *ole.SafeArray, index int64, element uintptr) (err error) { + + ret, _, _ := procSafeArrayPutElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + element) + + if ret != 0 { + return NewWmiError(ret) + } + + return nil +} + +func safeArrayGetElement(safearray *ole.SafeArray, index int64, element unsafe.Pointer) error { + + ret, _, _ := procSafeArrayGetElement.Call( + uintptr(unsafe.Pointer(safearray)), + uintptr(unsafe.Pointer(&index)), + uintptr(element)) + + if ret != 0 { + return NewWmiError(ret) + } + + return nil +} + +func isVariantValConvertible(variant ole.VARIANT) bool { + return !(variant.VT == ole.VT_RECORD || variant.VT == ole.VT_VARIANT) +} + +func safeArrayGetAsVariantVal(safeArray *ole.SafeArray, index int64, variant ole.VARIANT) (int64, error) { + var block int64 + + if !isVariantValConvertible(variant) { + return 0, fmt.Errorf("numeric call on a non-numeric value: %d", variant.VT) + } + + if err := safeArrayGetElement(safeArray, index, unsafe.Pointer(&block)); err != nil { + return 0, err + } + + switch variant.VT { + case ole.VT_UI1: + return int64(uint64(*(*uint8)(unsafe.Pointer(&block)))), nil + case ole.VT_UI2: + return int64(uint64(*(*uint16)(unsafe.Pointer(&block)))), nil + case ole.VT_UI4: + return int64(uint64(*(*uint32)(unsafe.Pointer(&block)))), nil + case ole.VT_I1: + return int64(*(*int8)(unsafe.Pointer(&block))), nil + case ole.VT_I2: + return int64(*(*int16)(unsafe.Pointer(&block))), nil + case ole.VT_I4: + return int64(*(*int32)(unsafe.Pointer(&block))), nil + case ole.VT_UI8, ole.VT_I8: + fallthrough + case ole.VT_R4, ole.VT_R8: + fallthrough + default: + return block, nil + } +} + +func safeArrayFromStringSlice(slice []string) (*ole.SafeArray, error) { + array, err := safeArrayCreateVector(ole.VT_BSTR, 0, uint32(len(slice))) + + if err != nil { + return nil, err + } + + for i, v := range slice { + err = safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(ole.SysAllocStringLen(v)))) + if err != nil { + _ = safeArrayDestroy(array) + return nil, err + } + } + return array, nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/conversion.go b/vendor/github.com/containers/libhvee/pkg/wmiext/conversion.go new file mode 100644 index 0000000000..9dc9690606 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/conversion.go @@ -0,0 +1,474 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "errors" + "fmt" + "math" + "reflect" + "strconv" + "strings" + "time" + "unsafe" + + "github.com/go-ole/go-ole" +) + +var ( + unixEpoch = time.Unix(0, 0) + zeroTime = time.Time{} +) + +// Automation variants do not follow the OLE rules, instead they use the following mapping: +// sint8 VT_I2 Signed 8-bit integer. +// sint16 VT_I2 Signed 16-bit integer. +// sint32 VT_I4 Signed 32-bit integer. +// sint64 VT_BSTR Signed 64-bit integer in string form. This type follows hexadecimal or decimal format +// +// according to the American National Standards Institute (ANSI) C rules. +// +// real32 VT_R4 4-byte floating-point value that follows the Institute of Electrical and Electronics +// +// Engineers, Inc. (IEEE) standard. +// +// real64 VT_R8 8-byte floating-point value that follows the IEEE standard. +// uint8 VT_UI1 Unsigned 8-bit integer. +// uint16 VT_I4 Unsigned 16-bit integer. +// uint32 VT_I4 Unsigned 32-bit integer. +// uint64 VT_BSTR Unsigned 64-bit integer in string form. This type follows hexadecimal or decimal format +// +// according to ANSI C rules. + +// NewAutomationVariant returns a new VARIANT com +// +//gocyclo:ignore +func NewAutomationVariant(value interface{}) (ole.VARIANT, error) { + switch cast := value.(type) { + case bool: + if cast { + return ole.NewVariant(ole.VT_BOOL, 0xffff), nil + } else { + return ole.NewVariant(ole.VT_BOOL, 0), nil + } + case int8: + return ole.NewVariant(ole.VT_I2, int64(cast)), nil + case []int8: + return CreateNumericArrayVariant(cast, ole.VT_I2) + case int16: + return ole.NewVariant(ole.VT_I2, int64(cast)), nil + case []int16: + return CreateNumericArrayVariant(cast, ole.VT_I2) + case int32: + return ole.NewVariant(ole.VT_I4, int64(cast)), nil + case []int32: + return CreateNumericArrayVariant(cast, ole.VT_I4) + case int64: + s := fmt.Sprintf("%d", cast) + return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))), nil + case []int64: + strs := make([]string, len(cast)) + for i, num := range cast { + strs[i] = fmt.Sprintf("%d", num) + } + return CreateStringArrayVariant(strs) + case float32: + return ole.NewVariant(ole.VT_R4, int64(math.Float32bits(cast))), nil + case float64: + return ole.NewVariant(ole.VT_R8, int64(math.Float64bits(cast))), nil + case uint8: + return ole.NewVariant(ole.VT_UI1, int64(cast)), nil + case []uint8: + return CreateNumericArrayVariant(cast, ole.VT_UI1) + case uint16: + return ole.NewVariant(ole.VT_I4, int64(cast)), nil + case []uint16: + return CreateNumericArrayVariant(cast, ole.VT_I4) + case uint32: + return ole.NewVariant(ole.VT_I4, int64(cast)), nil + case []uint32: + return CreateNumericArrayVariant(cast, ole.VT_I4) + case uint64: + s := fmt.Sprintf("%d", cast) + return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))), nil + case []uint64: + strs := make([]string, len(cast)) + for i, num := range cast { + strs[i] = fmt.Sprintf("%d", num) + } + return CreateStringArrayVariant(strs) + + // Assume 32 bit for generic (u)ints + case int: + return ole.NewVariant(ole.VT_I4, int64(cast)), nil + case uint: + return ole.NewVariant(ole.VT_I4, int64(cast)), nil + case []int: + return CreateNumericArrayVariant(cast, ole.VT_I4) + case []uint: + return CreateNumericArrayVariant(cast, ole.VT_I4) + + case string: + return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(value.(string)))))), nil + case []string: + if len(cast) == 0 { + return ole.NewVariant(ole.VT_NULL, 0), nil + } + return CreateStringArrayVariant(cast) + + case time.Time: + return convertTimeToDataTime(&cast), nil + case *time.Time: + return convertTimeToDataTime(cast), nil + case time.Duration: + return convertDurationToDateTime(cast), nil + case nil: + return ole.NewVariant(ole.VT_NULL, 0), nil + case *ole.IUnknown: + if cast == nil { + return ole.NewVariant(ole.VT_NULL, 0), nil + } + return ole.NewVariant(ole.VT_UNKNOWN, int64(uintptr(unsafe.Pointer(cast)))), nil + case *Instance: + if cast == nil { + return ole.NewVariant(ole.VT_NULL, 0), nil + } + return ole.NewVariant(ole.VT_UNKNOWN, int64(uintptr(unsafe.Pointer(cast.object)))), nil + default: + return ole.VARIANT{}, fmt.Errorf("unsupported type for automation variants %T", value) + } +} + +func convertToGoType(variant *ole.VARIANT, outputValue reflect.Value, outputType reflect.Type) (value interface{}, err error) { + if variant.VT&ole.VT_ARRAY == ole.VT_ARRAY { + return convertVariantToArray(variant, outputType) + } + + if variant.VT == ole.VT_UNKNOWN { + return convertVariantToStruct(variant, outputType) + } + + switch cast := outputValue.Interface().(type) { + case bool: + return variant.Val != 0, nil + case time.Time: + return convertDataTimeToTime(variant) + case *time.Time: + x, err := convertDataTimeToTime(variant) + return &x, err + case time.Duration: + return convertIntervalToDuration(variant) + case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64: + return convertVariantToInt(variant, outputType) + case float32, float64: + return convertVariantToFloat(variant, outputType) + case string: + return variant.ToString(), nil + default: + if variant.VT == ole.VT_NULL { + return nil, nil + } + return nil, fmt.Errorf("could not convert %d to %v", variant.VT, cast) + } +} + +func convertInt64ToInt(value int64, outputType reflect.Type) (interface{}, error) { + switch outputType.Kind() { + case reflect.Int: + return int(value), nil + case reflect.Int8: + return int8(value), nil + case reflect.Int16: + return int16(value), nil + case reflect.Int32: + return int32(value), nil + case reflect.Int64: + return int64(value), nil + case reflect.Uint: + return uint(value), nil + case reflect.Uint8: + return uint8(value), nil + case reflect.Uint16: + return uint16(value), nil + case reflect.Uint32: + return uint32(value), nil + case reflect.Uint64: + return uint64(value), nil + default: + return 0, fmt.Errorf("could not convert int64 to %v", outputType) + } +} + +func convertStringToInt64(str string, unsigned bool) (int64, error) { + if unsigned { + val, err := strconv.ParseUint(str, 0, 64) + return int64(val), err + } + + return strconv.ParseInt(str, 0, 64) +} + +func convertVariantToInt(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) { + var value int64 + switch variant.VT { + case ole.VT_NULL: + fallthrough + case ole.VT_BOOL: + fallthrough + case ole.VT_I1, ole.VT_I2, ole.VT_I4, ole.VT_I8, ole.VT_INT: + fallthrough + case ole.VT_UI1, ole.VT_UI2, ole.VT_UI4, ole.VT_UI8, ole.VT_UINT: + value = variant.Val + case ole.VT_R4: + // not necessarily a useful conversion but handle it anyway + value = int64(*(*float32)(unsafe.Pointer(&variant.Val))) + case ole.VT_R8: + value = int64(*(*float64)(unsafe.Pointer(&variant.Val))) + case ole.VT_BSTR: + var err error + value, err = convertStringToInt64(variant.ToString(), outputType.Kind() == reflect.Uint64) + if err != nil { + return value, err + } + default: + return nil, fmt.Errorf("could not convert variant type %d to %v", variant.VT, outputType) + } + + return convertInt64ToInt(value, outputType) +} + +func convertVariantToFloat(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) { + var value float64 + switch variant.VT { + case ole.VT_NULL: + fallthrough + case ole.VT_BOOL: + fallthrough + case ole.VT_I1, ole.VT_I2, ole.VT_I4, ole.VT_I8, ole.VT_INT: + fallthrough + case ole.VT_UI1, ole.VT_UI2, ole.VT_UI4, ole.VT_UI8, ole.VT_UINT: + value = float64(variant.Val) + case ole.VT_R4: + value = float64(*(*float32)(unsafe.Pointer(&variant.Val))) + case ole.VT_R8: + value = *(*float64)(unsafe.Pointer(&variant.Val)) + case ole.VT_BSTR: + var err error + value, err = strconv.ParseFloat(variant.ToString(), 64) + if err != nil { + return value, err + } + default: + return nil, fmt.Errorf("could not convert variant type %d to %v", variant.VT, outputType) + } + + if outputType.Kind() == reflect.Float32 { + return float32(value), nil + } + + return value, nil +} + +func convertVariantToStruct(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) { + if variant.VT != ole.VT_UNKNOWN { + return nil, fmt.Errorf("could not convert non-IUnknown variant type %d to %v", variant.VT, outputType) + } + + ptr := variant.ToIUnknown() + + var rawInstance struct { + *ole.IUnknown + *IWbemClassObjectVtbl + } + + rawInstance.IUnknown = ptr + rawInstance.IWbemClassObjectVtbl = (*IWbemClassObjectVtbl)(unsafe.Pointer(ptr.RawVTable)) + + instance := (*Instance)(unsafe.Pointer(&rawInstance)) + val := reflect.New(outputType) + err := instance.GetAll(val.Interface()) + return val.Elem().Interface(), err +} + +func convertVariantToArray(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) { + if variant.VT&ole.VT_ARRAY != ole.VT_ARRAY { + return nil, fmt.Errorf("could not convert non-array variant type %d to %v", variant.VT, outputType) + } + + safeArrayConversion := ole.SafeArrayConversion{Array: *(**ole.SafeArray)(unsafe.Pointer(&variant.Val))} + + arrayLen, err := safeArrayConversion.TotalElements(0) + if err != nil { + return nil, err + } + elemVT := (^ole.VT_ARRAY) & variant.VT + slice := reflect.MakeSlice(reflect.SliceOf(outputType.Elem()), int(arrayLen), int(arrayLen)) + + for i := 0; i < int(arrayLen); i++ { + elemVariant := ole.VARIANT{VT: elemVT} + elemSrc, err := safeArrayGetAsVariantVal(safeArrayConversion.Array, int64(i), elemVariant) + if err != nil { + return nil, err + } + elemVariant.Val = int64(elemSrc) + elemDest, err := convertToGoType(&elemVariant, slice.Index(i), outputType.Elem()) + if err != nil { + return nil, err + } + + slice.Index(i).Set(reflect.ValueOf(elemDest)) + } + + return slice.Interface(), nil +} + +func convertToGenericValue(variant *ole.VARIANT) interface{} { + var result interface{} + if variant.VT&ole.VT_ARRAY == ole.VT_ARRAY { + safeArrayConversion := ole.SafeArrayConversion{Array: *(**ole.SafeArray)(unsafe.Pointer(&variant.Val))} + result = safeArrayConversion.ToValueArray() + } else { + result = variant.Value() + } + return result +} + +func convertTimeToDataTime(time *time.Time) ole.VARIANT { + if time == nil || !time.After(WindowsEpoch) { + return ole.NewVariant(ole.VT_NULL, 0) + } + _, offset := time.Zone() + // convert to minutes + offset /= 60 + //yyyymmddHHMMSS.mmmmmmsUUU + s := fmt.Sprintf("%s%+04d", time.Format("20060102150405.000000"), offset) + return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))) +} + +func convertDurationToDateTime(duration time.Duration) ole.VARIANT { + const daySeconds = time.Second * 86400 + + if duration == 0 { + return ole.NewVariant(ole.VT_NULL, 0) + } + + days := duration / daySeconds + duration = duration % daySeconds + + hours := duration / time.Hour + duration = duration % time.Hour + + mins := duration / time.Minute + duration = duration % time.Minute + + seconds := duration / time.Second + duration = duration % time.Second + + micros := duration / time.Microsecond + + s := fmt.Sprintf("%08d%02d%02d%02d.%06d:000", days, hours, mins, seconds, micros) + return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))) +} + +func extractDateTimeString(variant *ole.VARIANT) (string, error) { + switch variant.VT { + case ole.VT_BSTR: + return variant.ToString(), nil + case ole.VT_NULL: + return "", nil + default: + return "", errors.New("variant not compatible with dateTime field") + } +} + +func convertDataTimeToTime(variant *ole.VARIANT) (time.Time, error) { + var err error + dateTime, err := extractDateTimeString(variant) + if err != nil || len(dateTime) == 0 { + return zeroTime, err + } + + dLen := len(dateTime) + if dLen < 5 { + return zeroTime, errors.New("invalid datetime string") + } + + if strings.HasPrefix(dateTime, "00000000000000.000000") { + // Zero time + return zeroTime, nil + } + + zoneStart := dLen - 4 + timePortion := dateTime[0:zoneStart] + + var zoneMinutes int64 + if dateTime[zoneStart] == ':' { + // interval ends in :000 + return parseIntervalTime(dateTime) + } + + zoneSuffix := dateTime[zoneStart:dLen] + zoneMinutes, err = strconv.ParseInt(zoneSuffix, 10, 0) + if err != nil { + return zeroTime, errors.New("invalid datetime string, zone did not parse") + } + + timePortion = fmt.Sprintf("%s%+03d%02d", timePortion, zoneMinutes/60, abs(int(zoneMinutes%60))) + return time.Parse("20060102150405.000000-0700", timePortion) +} + +// parseIntervalTime encodes an interval time as an offset to Unix time +// allowing a duration to be computed without precision loss +func parseIntervalTime(interval string) (time.Time, error) { + if len(interval) < 25 || interval[21:22] != ":" { + return time.Time{}, fmt.Errorf("invalid interval time: %s", interval) + } + + days, err := parseUintChain(interval[0:8], nil) + hours, err := parseUintChain(interval[8:10], err) + mins, err := parseUintChain(interval[10:12], err) + secs, err := parseUintChain(interval[12:14], err) + micros, err := parseUintChain(interval[15:21], err) + + if err != nil { + return time.Time{}, err + } + + var stamp uint64 = secs + stamp += days * 86400 + stamp += hours * 3600 + stamp += mins * 60 + + return time.Unix(int64(stamp), int64(micros*1000)), nil +} + +func convertIntervalToDuration(variant *ole.VARIANT) (time.Duration, error) { + var err error + interval, err := extractDateTimeString(variant) + if err != nil || len(interval) == 0 { + return 0, err + } + + t, err := parseIntervalTime(interval) + if err != nil { + return 0, nil + } + + return t.Sub(unixEpoch), nil +} + +func parseUintChain(str string, err error) (uint64, error) { + if err != nil { + return 0, err + } + return strconv.ParseUint(str, 10, 0) +} + +func abs(num int) int { + if num < 0 { + return -num + } + + return num +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/enum.go b/vendor/github.com/containers/libhvee/pkg/wmiext/enum.go new file mode 100644 index 0000000000..2b6fd690b3 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/enum.go @@ -0,0 +1,94 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "fmt" + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +type IEnumWbemClassObjectVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + Reset uintptr + Next uintptr + NextAsync uintptr + Clone uintptr + Skip uintptr +} + +type Enum struct { + enum *ole.IUnknown + vTable *IEnumWbemClassObjectVtbl + service *Service +} + +func (e *Enum) Close() { + if e != nil && e.enum != nil { + e.enum.Release() + } +} + +func newEnum(enumerator *ole.IUnknown, service *Service) *Enum { + return &Enum{ + enum: enumerator, + vTable: (*IEnumWbemClassObjectVtbl)(unsafe.Pointer(enumerator.RawVTable)), + service: service, + } +} + +// NextObject obtains the next instance in an enumeration and sets all fields +// of the struct pointer passed through the target parameter. Otherwise, if +// the target parameter is not a struct pointer type, an error will be +// returned. +func NextObject(enum *Enum, target interface{}) (bool, error) { + var err error + + var instance *Instance + if instance, err = enum.Next(); err != nil { + return false, err + } + + if instance == nil { + return true, nil + } + + defer instance.Close() + + return false, instance.GetAll(target) +} + +// Next returns the next object instance in this iteration +func (e *Enum) Next() (instance *Instance, err error) { + var res uintptr + var apObjects *ole.IUnknown + var uReturned uint32 + + res, _, _ = syscall.SyscallN( + e.vTable.Next, // IEnumWbemClassObject::Next() + uintptr(unsafe.Pointer(e.enum)), // IEnumWbemClassObject ptr + uintptr(WBEM_INFINITE), // [in] long lTimeout, + uintptr(1), // [in] ULONG uCount, + uintptr(unsafe.Pointer(&apObjects)), // [out] IWbemClassObject **apObjects, + uintptr(unsafe.Pointer(&uReturned))) // [out] ULONG *puReturned) + if int(res) < 0 { + return nil, NewWmiError(res) + } + + if uReturned < 1 { + switch res { + case WBEM_S_NO_ERROR, WBEM_S_FALSE: + // No more elements + return nil, nil + default: + return nil, fmt.Errorf("failure advancing enumeration (%d)", res) + } + } + + return newInstance(apObjects, e.service), nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/error.go b/vendor/github.com/containers/libhvee/pkg/wmiext/error.go new file mode 100644 index 0000000000..ce7a9d10bb --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/error.go @@ -0,0 +1,202 @@ +package wmiext + +import ( + "errors" + "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 +) + +// VM Lookup errors +var ( + ErrNoResults = errors.New("no results found") +) + +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/vendor/github.com/containers/libhvee/pkg/wmiext/init.go b/vendor/github.com/containers/libhvee/pkg/wmiext/init.go new file mode 100644 index 0000000000..35c5b82971 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/init.go @@ -0,0 +1,108 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "github.com/go-ole/go-ole" + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +var ( + ole32 = windows.NewLazySystemDLL("ole32.dll") + procCoSetProxyBlanket = ole32.NewProc("CoSetProxyBlanket") + procCoInitializeSecurity = ole32.NewProc("CoInitializeSecurity") + + modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll") + procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector") + procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement") + procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement") + procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy") + + clsidWbemObjectTextSrc = ole.NewGUID("{8d1c559d-84f0-4bb3-a7d5-56a7435a9ba6}") + iidIWbemObjectTextSrc = ole.NewGUID("{bfbf883a-cad7-11d3-a11b-00105a1f515a}") + + wmiWbemTxtLocator *ole.IUnknown + wmiWbemLocator *ole.IUnknown + + clsidWbemLocator = ole.NewGUID("4590f811-1d3a-11d0-891f-00aa004b2e24") + iidIWbemLocator = ole.NewGUID("dc12a687-737f-11cf-884d-00aa004b2e24") +) + +const ( + // WMI Generic flags + WBEM_FLAG_RETURN_WBEM_COMPLETE = 0x0 + WBEM_FLAG_RETURN_IMMEDIATELY = 0x10 + WBEM_FLAG_FORWARD_ONLY = 0x20 + + // WMI Query flags + WBEM_FLAG_SHALLOW = 1 + + // Timeout flags + WBEM_NO_WAIT = 0 + WBEM_INFINITE = 0xFFFFFFFF + + // COM Auth Flags + EOAC_NONE = 0 + + // RPC Authentication + RPC_C_AUTHN_WINNT = 10 + + // RPC Authentication Level + RPC_C_AUTHN_LEVEL_DEFAULT = 0 + RPC_C_AUTHN_LEVEL_CALL = 3 + + // RPC Authorization + RPC_C_AUTHZ_NONE = 0 + + // RPC Impersonation + RPC_C_IMP_LEVEL_IMPERSONATE = 3 +) + +func init() { + var err error + + err = ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) + if err != nil { + if oleCode, ok := err.(*ole.OleError); ok { + code := oleCode.Code() + // 1 = Already init + if code != 0 && code != 1 { + logrus.Errorf("Unable to initialize COM: %s", err.Error()) + return + } + } + } + + initSecurity() + + wmiWbemLocator, err = ole.CreateInstance(clsidWbemLocator, iidIWbemLocator) + if err != nil { + logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error()) + } + + // IID_IWbemObjectTextSrc Obtain the initial locator to WMI + wmiWbemTxtLocator, err = ole.CreateInstance(clsidWbemObjectTextSrc, iidIWbemObjectTextSrc) + if err != nil { + logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error()) + } +} + +func initSecurity() { + var svc int32 = -1 + + res, _, _ := procCoInitializeSecurity.Call( // CoInitializeSecurity + uintptr(0), // [in, optional] PSECURITY_DESCRIPTOR pSecDesc, + uintptr(svc), // [in] LONG cAuthSvc, + uintptr(0), // [in, optional] SOLE_AUTHENTICATION_SERVICE *asAuthSvc, + uintptr(0), // [in, optional] void *pReserved1, + uintptr(RPC_C_AUTHN_LEVEL_DEFAULT), // [in] DWORD dwAuthnLevel, + uintptr(RPC_C_IMP_LEVEL_IMPERSONATE), // [in] DWORD dwImpLevel, + uintptr(0), // [in, optional] void *pAuthList, + uintptr(EOAC_NONE), // [in] DWORD dwCapabilities, + uintptr(0)) // [in, optional] void *pReserved3 + if int(res) < 0 { + logrus.Errorf("Unable to initialize COM security: %s", NewWmiError(res).Error()) + } +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/instance.go b/vendor/github.com/containers/libhvee/pkg/wmiext/instance.go new file mode 100644 index 0000000000..b60d636fe2 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/instance.go @@ -0,0 +1,652 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "syscall" + "time" + "unsafe" + + "github.com/go-ole/go-ole" + "github.com/sirupsen/logrus" +) + +const ( + WmiPathKey = "__PATH" +) + +var ( + WindowsEpoch = time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC) +) + +type Instance struct { + object *ole.IUnknown + vTable *IWbemClassObjectVtbl + service *Service +} + +type IWbemClassObjectVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + GetQualifierSet uintptr + Get uintptr + Put uintptr + Delete uintptr + GetNames uintptr + BeginEnumeration uintptr + Next uintptr + EndEnumeration uintptr + GetPropertyQualifierSet uintptr + Clone uintptr + GetObjectText uintptr + SpawnDerivedClass uintptr + SpawnInstance uintptr + CompareTo uintptr + GetPropertyOrigin uintptr + InheritsFrom uintptr + GetMethod uintptr + PutMethod uintptr + DeleteMethod uintptr + BeginMethodEnumeration uintptr + NextMethod uintptr + EndMethodEnumeration uintptr + GetMethodQualifierSet uintptr + GetMethodOrigin uintptr +} + +type CIMTYPE_ENUMERATION uint32 + +const ( + CIM_ILLEGAL CIMTYPE_ENUMERATION = 0xFFF + CIM_EMPTY CIMTYPE_ENUMERATION = 0 + CIM_SINT8 CIMTYPE_ENUMERATION = 16 + CIM_UINT8 CIMTYPE_ENUMERATION = 17 + CIM_SINT16 CIMTYPE_ENUMERATION = 2 + CIM_UINT16 CIMTYPE_ENUMERATION = 18 + CIM_SINT32 CIMTYPE_ENUMERATION = 3 + CIM_UINT32 CIMTYPE_ENUMERATION = 19 + CIM_SINT64 CIMTYPE_ENUMERATION = 20 + CIM_UINT64 CIMTYPE_ENUMERATION = 21 + CIM_REAL32 CIMTYPE_ENUMERATION = 4 + CIM_REAL64 CIMTYPE_ENUMERATION = 5 + CIM_BOOLEAN CIMTYPE_ENUMERATION = 11 + CIM_STRING CIMTYPE_ENUMERATION = 8 + CIM_DATETIME CIMTYPE_ENUMERATION = 101 + CIM_REFERENCE CIMTYPE_ENUMERATION = 102 + CIM_CHAR16 CIMTYPE_ENUMERATION = 103 + CIM_OBJECT CIMTYPE_ENUMERATION = 13 + CIM_FLAG_ARRAY CIMTYPE_ENUMERATION = 0x2000 +) + +type WBEM_FLAVOR_TYPE uint32 + +const ( + WBEM_FLAVOR_DONT_PROPAGATE WBEM_FLAVOR_TYPE = 0 + WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE WBEM_FLAVOR_TYPE = 0x1 + WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS WBEM_FLAVOR_TYPE = 0x2 + WBEM_FLAVOR_MASK_PROPAGATION WBEM_FLAVOR_TYPE = 0xf + WBEM_FLAVOR_OVERRIDABLE WBEM_FLAVOR_TYPE = 0 + WBEM_FLAVOR_NOT_OVERRIDABLE WBEM_FLAVOR_TYPE = 0x10 + WBEM_FLAVOR_MASK_PERMISSIONS WBEM_FLAVOR_TYPE = 0x10 + WBEM_FLAVOR_ORIGIN_LOCAL WBEM_FLAVOR_TYPE = 0 + WBEM_FLAVOR_ORIGIN_PROPAGATED WBEM_FLAVOR_TYPE = 0x20 + WBEM_FLAVOR_ORIGIN_SYSTEM WBEM_FLAVOR_TYPE = 0x40 + WBEM_FLAVOR_MASK_ORIGIN WBEM_FLAVOR_TYPE = 0x60 + WBEM_FLAVOR_NOT_AMENDED WBEM_FLAVOR_TYPE = 0 + WBEM_FLAVOR_AMENDED WBEM_FLAVOR_TYPE = 0x80 + WBEM_FLAVOR_MASK_AMENDED WBEM_FLAVOR_TYPE = 0x80 +) + +func newInstance(object *ole.IUnknown, service *Service) *Instance { + instance := &Instance{ + object: object, + vTable: (*IWbemClassObjectVtbl)(unsafe.Pointer(object.RawVTable)), + service: service, + } + + return instance +} + +// Close cleans up all memory associated with this instance. +func (i *Instance) Close() { + if i != nil && i.object != nil { + i.object.Release() + } +} + +// GetClassName Gets the WMI class name for this WMI object instance +func (i *Instance) GetClassName() (className string, err error) { + return i.GetAsString(`__CLASS`) +} + +// Path gets the WMI object path of this instance +func (i *Instance) Path() (string, error) { + ref, _, _, err := i.GetAsAny(WmiPathKey) + return ref.(string), err +} + +// IsReferenceProperty returns whether the property is of type CIM_REFERENCE, a string which points to +// an object path of another instance. +func (i *Instance) IsReferenceProperty(name string) (bool, error) { + _, cimType, _, err := i.GetAsAny(name) + return cimType == CIM_REFERENCE, err +} + +// SpawnInstance create a new WMI object instance that is zero-initialized. The returned instance +// will not respect expected default values, which must be populated by other means. +func (i *Instance) SpawnInstance() (instance *Instance, err error) { + var res uintptr + var newUnknown *ole.IUnknown + + res, _, _ = syscall.SyscallN( + i.vTable.SpawnInstance, // IWbemClassObject::SpawnInstance( + uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr + uintptr(0), // [in] long lFlags, + uintptr(unsafe.Pointer(&newUnknown))) // [out] IWbemClassObject **ppNewInstance) + if res != 0 { + return nil, NewWmiError(res) + } + + return newInstance(newUnknown, i.service), nil +} + +// CloneInstance create a new cloned copy of this WMI instance. +func (i *Instance) CloneInstance() (*Instance, error) { + classObj := i.object + vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable)) + var cloned *ole.IUnknown + + ret, _, _ := syscall.SyscallN( + vTable.Clone, // IWbemClassObject::Clone( + uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr + uintptr(unsafe.Pointer(&cloned))) // [out] IWbemClassObject **ppCopy) + if ret != 0 { + return nil, NewWmiError(ret) + } + + return newInstance(cloned, i.service), nil +} + +// PutAll sets all fields of this instance to the passed src parameter's fields, converting accordingly. +// The src parameter must be a pointer to a struct, otherwise an error will be returned. +func (i *Instance) PutAll(src interface{}) error { + val := reflect.ValueOf(src) + if val.Kind() == reflect.Pointer { + val = val.Elem() + } + + if val.Kind() != reflect.Struct { + return errors.New("not a struct or pointer to struct") + } + + props, err := i.GetAllProperties() + if err != nil { + return err + } + + return i.instancePutAllTraverse(val, props) +} + +func (i *Instance) instancePutAllTraverse(val reflect.Value, propMap map[string]interface{}) error { + for j := 0; j < val.NumField(); j++ { + fieldVal := val.Field(j) + fieldType := val.Type().Field(j) + + if fieldType.Type.Kind() == reflect.Struct && fieldType.Anonymous { + if err := i.instancePutAllTraverse(fieldVal, propMap); err != nil { + return err + } + continue + } + if strings.HasPrefix(fieldType.Name, "S__") { + continue + } + + if !fieldType.IsExported() { + continue + } + + if _, exists := propMap[fieldType.Name]; !exists { + continue + } + + if fieldVal.Kind() == reflect.String && fieldVal.Len() == 0 { + continue + } + + if err := i.Put(fieldType.Name, fieldVal.Interface()); err != nil { + return err + } + } + + return nil +} + +// Put sets the specified property to the passed Golang value, converting appropriately. +func (i *Instance) Put(name string, value interface{}) (err error) { + var variant ole.VARIANT + + switch cast := value.(type) { + case ole.VARIANT: + variant = cast + case *ole.VARIANT: + variant = *cast + default: + variant, err = NewAutomationVariant(value) + if err != nil { + return err + } + } + + var wszName *uint16 + if wszName, err = syscall.UTF16PtrFromString(name); err != nil { + return + } + + classObj := i.object + vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable)) + res, _, _ := syscall.SyscallN( + vTable.Put, // IWbemClassObject::Put( + uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr + uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName, + uintptr(0), // [in] long lFlags, + uintptr(unsafe.Pointer(&variant)), // [in] VARIANT *pVal, + uintptr(0)) // [in] CIMTYPE Type) + if res != 0 { + return NewWmiError(res) + } + + _ = variant.Clear() + return +} + +// GetCimText returns the CIM XML representation of this instance. Some WMI methods use a string +// parameter to represent a full complex object, and this method is used to generate +// the expected format. +func (i *Instance) GetCimText() string { + type wmiWbemTxtSrcVtable struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + GetTxt uintptr + } + const CIM_XML_FORMAT = 1 + + classObj := i.object + + vTable := (*wmiWbemTxtSrcVtable)(unsafe.Pointer(wmiWbemTxtLocator.RawVTable)) + var retString *uint16 + res, _, _ := syscall.SyscallN( + vTable.GetTxt, // IWbemObjectTextSrc::GetText() + uintptr(unsafe.Pointer(wmiWbemLocator)), // IWbemObjectTextSrc ptr + uintptr(0), // [in] long lFlags + uintptr(unsafe.Pointer(classObj)), // [in] IWbemClassObject *pObj + uintptr(CIM_XML_FORMAT), // [in] ULONG uObjTextFormat, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(&retString))) // [out] BSTR *strText) + if res != 0 { + return "" + } + itemStr := ole.BstrToString(retString) + return itemStr +} + +// GetAll gets all fields that map to a target struct and populates all struct fields according to +// the expected type information. The target parameter should be a pointer to a struct, and +// will return an error otherwise. +func (i *Instance) GetAll(target interface{}) error { + elem := reflect.ValueOf(target) + if elem.Kind() != reflect.Ptr || elem.IsNil() { + return errors.New("invalid destination type for mapping a WMI instance to an object") + } + + // deref pointer + elem = elem.Elem() + var err error + + if err = i.BeginEnumeration(); err != nil { + return err + } + + properties := make(map[string]*ole.VARIANT) + + for { + var name string + var value *ole.VARIANT + var done bool + + if done, name, value, _, _, err = i.NextAsVariant(); err != nil { + return err + } + + if done { + break + } + + if value != nil { + properties[name] = value + } + } + + defer func() { + for _, v := range properties { + _ = v.Clear() + } + }() + + _ = i.EndEnumeration() + + return i.instanceGetAllPopulate(elem, elem.Type(), properties) +} + +// GetAsAny gets a property and converts it to a Golang type that matches the internal +// variant automation type passed back from WMI. For usage with predictable static +// type mapping, use GetAsString(), GetAsUint(), or GetAll() instead of this method. +func (i *Instance) GetAsAny(name string) (interface{}, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) { + variant, cimType, flavor, err := i.GetAsVariant(name) + if err != nil { + return nil, cimType, flavor, err + } + + defer func() { + if err := variant.Clear(); err != nil { + logrus.Error(err) + } + }() + + // Since there is no type information only perform the stock conversion + result := convertToGenericValue(variant) + + return result, cimType, flavor, err +} + +// GetAsString gets a property value as a string value, converting if necessary +func (i *Instance) GetAsString(name string) (value string, err error) { + variant, _, _, err := i.GetAsVariant(name) + if err != nil || variant == nil { + return "", err + } + defer func() { + if err := variant.Clear(); err != nil { + logrus.Error(err) + } + }() + + // TODO: replace with something better + return fmt.Sprintf("%v", convertToGenericValue(variant)), nil +} + +// GetAsUint gets a property value as a uint value, if conversion is possible. Otherwise, +// returns an error. +func (i *Instance) GetAsUint(name string) (uint, error) { + val, _, _, err := i.GetAsAny(name) + if err != nil { + return 0, err + } + + switch ret := val.(type) { + case int: + return uint(ret), nil + case int8: + return uint(ret), nil + case int16: + return uint(ret), nil + case int32: + return uint(ret), nil + case int64: + return uint(ret), nil + case uint: + return ret, nil + case uint8: + return uint(ret), nil + case uint16: + return uint(ret), nil + case uint32: + return uint(ret), nil + case uint64: + return uint(ret), nil + case string: + parse, err := strconv.ParseUint(ret, 10, 64) + return uint(parse), err + default: + return 0, fmt.Errorf("type conversion from %T on param %s not supported", val, name) + } +} + +// GetAsVariant obtains a specified property value, if it exists. +func (i *Instance) GetAsVariant(name string) (*ole.VARIANT, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) { + var variant ole.VARIANT + var err error + var wszName *uint16 + var cimType CIMTYPE_ENUMERATION + var flavor WBEM_FLAVOR_TYPE + + if wszName, err = syscall.UTF16PtrFromString(name); err != nil { + return nil, 0, 0, err + } + + classObj := i.object + vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable)) + + res, _, _ := syscall.SyscallN( + vTable.Get, // IWbemClassObject::Get( + uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr + uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName, + uintptr(0), // [in] long lFlags, + uintptr(unsafe.Pointer(&variant)), // [out] VARIANT *pVal, + uintptr(unsafe.Pointer(&cimType)), // [out, optional] CIMTYPE *pType, + uintptr(unsafe.Pointer(&flavor))) // [out, optional] long *plFlavor) + if res != 0 { + return nil, 0, 0, NewWmiError(res) + } + + return &variant, cimType, flavor, nil +} + +// Next retrieves the next property as a Golang type when iterating the properties using an enumerator +// created by BeginEnumeration(). The returned value's type represents the internal automation type +// used by WMI. It is usually preferred to use GetAsXXX(), GetAll(), or GetAll Properties() over this +// method. +func (i *Instance) Next() (done bool, name string, value interface{}, cimType CIMTYPE_ENUMERATION, flavor WBEM_FLAVOR_TYPE, err error) { + var variant *ole.VARIANT + done, name, variant, cimType, flavor, err = i.NextAsVariant() + + if err == nil && !done { + defer func() { + if err := variant.Clear(); err != nil { + logrus.Error(err) + } + }() + value = convertToGenericValue(variant) + } + + return +} + +// NextAsVariant retrieves the next property as a VARIANT type when iterating the properties using an enumerator +// created by BeginEnumeration(). The returned value's type represents the internal automation type +// used by WMI. It is usually preferred to use GetAsXXX(), GetAll(), or GetAllProperties() over this +// method. Callers are responsible for clearing the VARIANT, otherwise associated memory will leak. +func (i *Instance) NextAsVariant() (bool, string, *ole.VARIANT, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) { + var res uintptr + var strName *uint16 + var variant ole.VARIANT + var cimType CIMTYPE_ENUMERATION + var flavor WBEM_FLAVOR_TYPE + + res, _, _ = syscall.SyscallN( + i.vTable.Next, // IWbemClassObject::Next( + uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr + uintptr(0), // [in] long lFlags, + uintptr(unsafe.Pointer(&strName)), // [out] BSTR *strName, + uintptr(unsafe.Pointer(&variant)), // [out] VARIANT *pVal, + 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, NewWmiError(res) + } + + if res == WBEM_S_NO_MORE_DATA { + return true, "", nil, cimType, flavor, nil + } + + defer ole.SysFreeString((*int16)(unsafe.Pointer(strName))) //nolint:errcheck + name := ole.BstrToString(strName) + + return false, name, &variant, cimType, flavor, nil +} + +// GetAllProperties gets all properties on this instance. The returned map is keyed by the field name and the value +// is a Golang type which matches the WMI internal implementation. For static type conversions, +// it's recommended to use either GetAll(), which uses struct fields for type information, or +// the GetAsXXX() methods. +func (i *Instance) GetAllProperties() (map[string]interface{}, error) { + var err error + properties := make(map[string]interface{}) + + if err = i.BeginEnumeration(); err != nil { + return nil, err + } + + defer func() { + if err := i.EndEnumeration(); err != nil { + logrus.Error(err) + } + }() + + for { + var name string + var value interface{} + var done bool + + if done, name, value, _, _, err = i.Next(); err != nil || done { + return properties, err + } + + properties[name] = value + } +} + +// GetMethodParameters returns a WMI class object which represents the [in] method parameters for a method invocation. +// This is an advanced method, used for dynamic introspection or manual method invocation. In most +// cases it is recommended to use BeginInvoke() instead, which constructs the parameter payload +// automatically. +func (i *Instance) GetMethodParameters(method string) (*Instance, error) { + var err error + var res uintptr + var inSignature *ole.IUnknown + + var wszName *uint16 + if wszName, err = syscall.UTF16PtrFromString(method); err != nil { + return nil, err + } + + res, _, _ = syscall.SyscallN( + i.vTable.GetMethod, // IWbemClassObject::GetMethod( + uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr + uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName + uintptr(0), // [in] long lFlags, + uintptr(unsafe.Pointer(&inSignature)), // [out] IWbemClassObject **ppInSignature, + uintptr(0)) // [out] IWbemClassObject **ppOutSignature) + if res != 0 { + return nil, NewWmiError(res) + } + + return newInstance(inSignature, i.service), nil +} + +func (i *Instance) instanceGetAllPopulate(elem reflect.Value, elemType reflect.Type, properties map[string]*ole.VARIANT) error { + var err error + + for j := 0; j < elemType.NumField(); j++ { + fieldType := elemType.Field(j) + fieldVal := elem.Field(j) + + if !fieldType.IsExported() { + continue + } + + if fieldType.Type.Kind() == reflect.Struct && fieldType.Anonymous { + if err := i.instanceGetAllPopulate(fieldVal, fieldType.Type, properties); err != nil { + return err + } + continue + } + + fieldName := fieldType.Name + + if strings.HasPrefix(fieldName, "S__") { + fieldName = fieldName[1:] + } + if variant, ok := properties[fieldName]; ok { + var val interface{} + if val, err = convertToGoType(variant, fieldVal, fieldType.Type); err != nil { + return err + } + + if val != nil { + fieldVal.Set(reflect.ValueOf(val)) + } + } + } + + return nil +} + +// BeginEnumeration begins iterating the property list on this instance. This is an advanced method. +// In most cases, the GetAsXXX() methods, GetAll(), and GetAllProperties() methods should be +// preferred. +func (i *Instance) BeginEnumeration() error { + classObj := i.object + vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable)) + + result, _, _ := syscall.SyscallN( + vTable.BeginEnumeration, // IWbemClassObject::BeginEnumeration( + uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr, + uintptr(0)) // [in] long lEnumFlags) // 0 = defaults + if result != 0 { + return NewWmiError(result) + } + + return nil +} + +// EndEnumeration completes iterating a property list on this instance. This is an advanced method. +// In most cases, the GetAsXXX() methods, GetAll(), and GetAllProperties() methods +// should be preferred. +func (i *Instance) EndEnumeration() error { + res, _, _ := syscall.SyscallN( + i.vTable.EndEnumeration, // IWbemClassObject::EndEnumeration( + uintptr(unsafe.Pointer(i.object))) // IWbemClassObject ptr) + if res != 0 { + return NewWmiError(res) + } + + return nil +} + +// BeginInvoke invokes a method on this Instance. Returns a MethodExecutor builder object +// that is used to construct the input parameters (via calls to In()), perform the +// invocation (using calls to Execute()), retrieve output parameters (via calls to +// Out()), and finally the method return value (using a call to End()) +func (i *Instance) BeginInvoke(method string) *MethodExecutor { + objPath, err := i.Path() + if err != nil { + return &MethodExecutor{err: err} + } + + var class, inParam *Instance + if class, err = i.service.GetClassInstance(i); err == nil { + inParam, err = class.GetMethodParameters(method) + class.Close() + } + + return &MethodExecutor{method: method, path: objPath, service: i.service, inParam: inParam, err: err} +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/invoke.go b/vendor/github.com/containers/libhvee/pkg/wmiext/invoke.go new file mode 100644 index 0000000000..6875e2de69 --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/invoke.go @@ -0,0 +1,132 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "fmt" + "reflect" + + "github.com/go-ole/go-ole" + "github.com/sirupsen/logrus" +) + +type MethodExecutor struct { + err error + path string + method string + service *Service + inParam *Instance + outParam *Instance +} + +// In sets an input parameter for the method of this invocation, converting appropriately +func (e *MethodExecutor) In(name string, value interface{}) *MethodExecutor { + if e.err == nil && e.inParam != nil { + switch t := value.(type) { + case *Instance: + var ref bool + if ref, e.err = e.inParam.IsReferenceProperty(name); e.err != nil { + return e + } + if !ref { + // Embedded Object + break + } + if value, e.err = t.Path(); e.err != nil { + return e + } + } + + e.err = e.inParam.Put(name, value) + } + + return e +} + +// Out sets the specified output parameter, and assigns the value parameter to the result. +// The value parameter must be a reference to the field that should be set. +func (e *MethodExecutor) Out(name string, value interface{}) *MethodExecutor { + if e.err == nil && e.outParam != nil { + var variant *ole.VARIANT + var cimType CIMTYPE_ENUMERATION + var result interface{} + dest := reflect.ValueOf(value) + if dest.Kind() != reflect.Ptr { + e.err = fmt.Errorf("Out() on %q called with %T, out parameters must be a reference", name, value) + return e + } + dest = dest.Elem() + + variant, cimType, _, e.err = e.outParam.GetAsVariant(name) + if e.err != nil || variant == nil { + return e + } + + defer func() { + if err := variant.Clear(); err != nil { + logrus.Error(err) + } + }() + + if _, ok := value.(**Instance); ok && cimType == CIM_REFERENCE { + path := variant.ToString() + result, e.err = e.service.GetObject(path) + if e.err != nil { + return e + } + } else { + target := reflect.ValueOf(value).Elem() + result, e.err = convertToGoType(variant, target, target.Type()) + if e.err != nil { + return e + } + } + + newValue := reflect.ValueOf(result) + if result == nil { + // Nil must be typed to the destination + newValue = reflect.Zero(dest.Type()) + } + + dest.Set(newValue) + } + return e +} + +// Execute executes the method after in parameters have been specified using In() +func (e *MethodExecutor) Execute() *MethodExecutor { + defer e.cleanupInputs() + + if e.err == nil { + e.outParam, e.err = e.service.ExecMethod(e.path, e.method, e.inParam) + } + + return e +} + +func (e *MethodExecutor) cleanupInputs() { + if e.inParam != nil { + e.inParam.Close() + e.inParam = nil + } +} + +// End completes the method invocation and returns an error indicating the return +// code of the underlying method +func (e *MethodExecutor) End() error { + e.cleanupInputs() + + if e.outParam != nil { + e.outParam.Close() + e.outParam = nil + } + + return e.err +} + +// Obtains the last error that occurred while building the invocation. Once +// an error has occurred, all future operations are treated as a no-op. +func (e *MethodExecutor) Error() error { + return e.err +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/job.go b/vendor/github.com/containers/libhvee/pkg/wmiext/job.go new file mode 100644 index 0000000000..180c1e515f --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/job.go @@ -0,0 +1,53 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "fmt" + "time" +) + +type JobError struct { + ErrorCode int +} + +func (err *JobError) Error() string { + return fmt.Sprintf("Job failed with error code: %d", err.ErrorCode) +} + +// WaitJob waits on the specified job instance until it has completed and +// returns a JobError containing the result code in the event of +// a failure. +func WaitJob(service *Service, job *Instance) error { + var jobs []*Instance + defer func() { + for _, job := range jobs { + job.Close() + } + }() + for { + state, _, _, err := job.GetAsAny("JobState") + if err != nil { + return err + } + time.Sleep(100 * time.Millisecond) + job, _ = service.RefetchObject(job) + jobs = append(jobs, job) + // 7+ = completed + if state.(int32) >= 7 { + break + } + } + + result, _, _, err := job.GetAsAny("ErrorCode") + if err != nil { + return err + } + + if result.(int32) != 0 { + return &JobError{ErrorCode: int(result.(int32))} + } + + return nil +} diff --git a/vendor/github.com/containers/libhvee/pkg/wmiext/service.go b/vendor/github.com/containers/libhvee/pkg/wmiext/service.go new file mode 100644 index 0000000000..c58d06c1aa --- /dev/null +++ b/vendor/github.com/containers/libhvee/pkg/wmiext/service.go @@ -0,0 +1,416 @@ +//go:build windows +// +build windows + +package wmiext + +import ( + "errors" + "fmt" + "syscall" + "unsafe" + + "github.com/go-ole/go-ole" +) + +type IWbemLocatorVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + ConnectServer uintptr +} + +type Service struct { + service *ole.IUnknown + vTable *IWbemServicesVtbl +} + +type IWbemServicesVtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + OpenNamespace uintptr + CancelAsyncCall uintptr + QueryObjectSink uintptr + GetObject uintptr + GetObjectAsync uintptr + PutClass uintptr + PutClassAsync uintptr + DeleteClass uintptr + DeleteClassAsync uintptr + CreateClassEnum uintptr + CreateClassEnumAsync uintptr + PutInstance uintptr + PutInstanceAsync uintptr + DeleteInstance uintptr + DeleteInstanceAsync uintptr + CreateInstanceEnum uintptr + CreateInstanceEnumAsync uintptr + ExecQuery uintptr + ExecQueryAsync uintptr + ExecNotificationQuery uintptr + ExecNotificationQueryAsync uintptr + ExecMethod uintptr + ExecMethodAsync uintptr +} + +func connectService(namespace string) (*Service, error) { + + if wmiWbemLocator == nil { + return nil, errors.New("WMI failed initialization, service calls can not proceed") + } + + var err error + var res uintptr + var strResource *uint16 + var strLocale *uint16 + var service *ole.IUnknown + + loc := fmt.Sprintf(`\\.\%s`, namespace) + + if strResource, err = syscall.UTF16PtrFromString(loc); err != nil { + return nil, err + } + + // Connect with en_US LCID since we do pattern matching against English key values + if strLocale, err = syscall.UTF16PtrFromString("MS_409"); err != nil { + return nil, err + } + + myVTable := (*IWbemLocatorVtbl)(unsafe.Pointer(wmiWbemLocator.RawVTable)) + res, _, _ = syscall.SyscallN( + myVTable.ConnectServer, // IWbemLocator::ConnectServer( + uintptr(unsafe.Pointer(wmiWbemLocator)), // IWbemLocator ptr + uintptr(unsafe.Pointer(strResource)), // [in] const BSTR strNetworkResource, + uintptr(0), // [in] const BSTR strUser, + uintptr(0), // [in] const BSTR strPassword, + uintptr(unsafe.Pointer(strLocale)), // [in] const BSTR strLocale, + uintptr(WBEM_FLAG_CONNECT_USE_MAX_WAIT), // [in] long lSecurityFlags, + uintptr(0), // [in] const BSTR strAuthority, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(&service))) // [out] IWbemServices **ppNamespace) + + if res != 0 { + return nil, NewWmiError(res) + } + + if err = CoSetProxyBlanket(service); err != nil { + return nil, err + } + + return newService(service), nil +} + +func newService(service *ole.IUnknown) *Service { + return &Service{ + service: service, + vTable: (*IWbemServicesVtbl)(unsafe.Pointer(service.RawVTable)), + } +} + +const ( + WBEM_FLAG_CONNECT_USE_MAX_WAIT = 0x80 +) + +func CoSetProxyBlanket(service *ole.IUnknown) (err error) { + res, _, _ := procCoSetProxyBlanket.Call( //CoSetProxyBlanket( + uintptr(unsafe.Pointer(service)), // [in] IUnknown *pProxy, + uintptr(RPC_C_AUTHN_WINNT), // [in] DWORD dwAuthnSvc, + uintptr(RPC_C_AUTHZ_NONE), // [in] DWORD dwAuthzSvc, + uintptr(0), // [in, opt] OLECHAR *pServerPrincName, + uintptr(RPC_C_AUTHN_LEVEL_CALL), // [in] DWORD dwAuthnLevel, + uintptr(RPC_C_IMP_LEVEL_IMPERSONATE), // [in] DWORD dwImpLevel, + uintptr(0), // [in, opt] RPC_AUTH_IDENTITY_HANDLE pAuthInfo, + uintptr(EOAC_NONE)) // [in] DWORD dwCapabilities) + + if res != 0 { + return NewWmiError(res) + } + + return nil +} + +// NewLocalService creates a service and connect it to the local system at the specified namespace +func NewLocalService(namespace string) (s *Service, err error) { + return connectService(namespace) +} + +// Close frees all associated memory with this service +func (s *Service) Close() { + if s != nil && s.service != nil { + s.service.Release() + } +} + +// ExecQuery executes a WQL query and returns an enumeration to iterate the result set. +// Queries are executed in a semi-synchronous fashion. +func (s *Service) ExecQuery(wqlQuery string) (*Enum, error) { + var err error + var pEnum *ole.IUnknown + var strQuery *uint16 + var strQL *uint16 + + if strQL, err = syscall.UTF16PtrFromString("WQL"); err != nil { + return nil, err + } + + if strQuery, err = syscall.UTF16PtrFromString(wqlQuery); err != nil { + return nil, err + } + + // Semisynchronous mode = return immed + forward (for perf) + flags := WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY + + hres, _, _ := syscall.SyscallN( + s.vTable.ExecQuery, // IWbemServices::ExecQuery( + uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr + uintptr(unsafe.Pointer(strQL)), // [in] const BSTR strQueryLanguage, + uintptr(unsafe.Pointer(strQuery)), // [in] const BSTR strQuery, + uintptr(flags), // [in] long lFlags, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum) + if hres != 0 { + return nil, NewWmiError(hres) + } + + if err = CoSetProxyBlanket(pEnum); err != nil { + return nil, err + } + + return newEnum(pEnum, s), nil +} + +// GetObject obtains a single WMI class or instance given its path +func (s *Service) GetObject(objectPath string) (instance *Instance, err error) { + var pObject *ole.IUnknown + var strObjectPath *uint16 + + if strObjectPath, err = syscall.UTF16PtrFromString(objectPath); err != nil { + return + } + + // Synchronous call + flags := WBEM_FLAG_RETURN_WBEM_COMPLETE + + res, _, _ := syscall.SyscallN( + s.vTable.GetObject, // IWbemServices::GetObject( + uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr + uintptr(unsafe.Pointer(strObjectPath)), // [in] const BSTR strObjectPath, + uintptr(flags), // [in] long lFlags, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(&pObject)), // [out] IWbemClassObject **ppObject, + uintptr(0)) // [out] IWbemCallResult **ppCallResult) + if int(res) < 0 { + // returns WBEM_E_PROVIDER_NOT_FOUND when no entry found + return nil, NewWmiError(res) + } + + return newInstance(pObject, s), nil +} + +// GetObjectAsObject gets an object by its path and set all fields of the passed in target to match the instance's +// properties. Conversion is performed as appropriate. +func (s *Service) GetObjectAsObject(objPath string, target interface{}) error { + instance, err := s.GetObject(objPath) + if err != nil { + return err + } + defer instance.Close() + + return instance.GetAll(target) +} + +// CreateInstanceEnum creates an enumerator that iterates all registered object instances for a given className. +func (s *Service) CreateInstanceEnum(className string) (*Enum, error) { + var err error + var pEnum *ole.IUnknown + var strFilter *uint16 + + if strFilter, err = syscall.UTF16PtrFromString(className); err != nil { + return nil, err + } + + // No subclasses in result set + flags := WBEM_FLAG_SHALLOW + + res, _, _ := syscall.SyscallN( + s.vTable.CreateInstanceEnum, // IWbemServices::CreateInstanceEnum( + uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr + uintptr(unsafe.Pointer(strFilter)), // [in] const BSTR strFilter, + uintptr(flags), // [in] long lFlags, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum) + if int(res) < 0 { + return nil, NewWmiError(res) + } + + if err = CoSetProxyBlanket(pEnum); err != nil { + return nil, err + } + + return newEnum(pEnum, s), nil +} + +// ExecMethod executes a method using the specified class and parameter payload instance. The parameter payload +// instance can be constructed using Instance.GetMethodParameters(). This is an advanced method, it is +// recommended to use BeginInvoke() instead, where possible. +func (s *Service) ExecMethod(className string, methodName string, inParams *Instance) (*Instance, error) { + var err error + var outParams *ole.IUnknown + var strObjectPath *uint16 + var strMethodName *uint16 + + if strObjectPath, err = syscall.UTF16PtrFromString(className); err != nil { + return nil, err + } + + if strMethodName, err = syscall.UTF16PtrFromString(methodName); err != nil { + return nil, err + } + + res, _, _ := syscall.SyscallN( + s.vTable.ExecMethod, // IWbemServices::ExecMethod( + uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr + uintptr(unsafe.Pointer(strObjectPath)), // [in] const BSTR strObjectPath, + uintptr(unsafe.Pointer(strMethodName)), // [in] const BSTR strMethodName, + uintptr(0), // [in] long lFlags, + uintptr(0), // [in] IWbemContext *pCtx, + uintptr(unsafe.Pointer(inParams.object)), // [in] IWbemClassObject *pInParams, + uintptr(unsafe.Pointer(&outParams)), // [out] IWbemClassObject **ppOutParams, + uintptr(0)) // [out] IWbemCallResult **ppCallResult) + if int(res) < 0 { + return nil, NewWmiError(res) + } + + return newInstance(outParams, s), nil +} + +// FindFirstInstance find and returns the first WMI Instance in the result set for a WSL query. +func (s *Service) FindFirstInstance(wql string) (*Instance, error) { + var enum *Enum + var err error + if enum, err = s.ExecQuery(wql); err != nil { + return nil, err + } + defer enum.Close() + + instance, err := enum.Next() + if err != nil { + return nil, err + } + + if instance == nil { + return nil, ErrNoResults + } + + return instance, nil +} + +// FindFirstRelatedInstance finds and returns a related associator of the specified WMI object path of the +// expected className type. +func (s *Service) FindFirstRelatedInstance(objPath string, className string) (*Instance, error) { + wql := fmt.Sprintf("ASSOCIATORS OF {%s} WHERE ResultClass = %s", objPath, className) + return s.FindFirstInstance(wql) +} + +// FindFirstRelatedInstanceThrough finds and returns a related associator of the specified WMI object path of the +// expected className type, and only through the expected association type. +func (s *Service) FindFirstRelatedInstanceThrough(objPath string, resultClass string, assocClass string) (*Instance, error) { + wql := fmt.Sprintf("ASSOCIATORS OF {%s} WHERE AssocClass = %s ResultClass = %s ", objPath, assocClass, resultClass) + return s.FindFirstInstance(wql) +} + +// FindFirstRelatedObject finds and returns a related associator of the specified WMI object path of the +// expected className type, and populates the passed in struct with its fields +func (s *Service) FindFirstRelatedObject(objPath string, className string, target interface{}) error { + wql := fmt.Sprintf("ASSOCIATORS OF {%s} WHERE ResultClass = %s", objPath, className) + return s.FindFirstObject(wql, target) +} + +// FindFirstObject finds and returns the first WMI Instance in the result set for a WSL query, and +// populates the specified struct pointer passed in through the target parameter. +func (s *Service) FindFirstObject(wql string, target interface{}) error { + var enum *Enum + var err error + if enum, err = s.ExecQuery(wql); err != nil { + return err + } + defer enum.Close() + + done, err := NextObject(enum, target) + if err != nil { + return err + } + + if done { + return errors.New("no results found") + } + + return nil +} + +// GetSingletonInstance gets the first WMI instance of the specified object class type. This is a +// shortcut method for uses where only one instance is expected. +func (s *Service) GetSingletonInstance(className string) (*Instance, error) { + var ( + enum *Enum + instance *Instance + err error + ) + + if enum, err = s.CreateInstanceEnum(className); err != nil { + return nil, err + } + defer enum.Close() + + if instance, err = enum.Next(); err != nil { + return nil, err + } + + return instance, nil +} + +// CreateInstance creates a new WMI object class instance of the specified className, and sets +// all properties according to the passed in struct pointer through the src +// parameter, converting appropriately. +func (s *Service) CreateInstance(className string, src interface{}) (*Instance, error) { + instance, err := s.SpawnInstance(className) + if err != nil { + return nil, err + } + + return instance, instance.PutAll(src) +} + +// SpawnInstance creates a new zeroed WMI instance. This instance will not contain expected values. +// Those must be retrieved and set separately, or CreateInstance() can be used instead. +func (s *Service) SpawnInstance(className string) (*Instance, error) { + var class *Instance + var err error + if class, err = s.GetObject(className); err != nil { + return nil, err + } + defer class.Close() + + return class.SpawnInstance() +} + +// RefetchObject re-fetches the object and returns a new instance. The original instance will not +// automatically Close(). Callers of this method will need to manually close the +// original. +func (s *Service) RefetchObject(instance *Instance) (*Instance, error) { + path, err := instance.Path() + if err != nil { + return instance, err + } + return s.GetObject(path) +} + +// GetClassInstance gets the WMI class instance associated with the specified object instance. +// This method is used to perform schema queries. +func (s *Service) GetClassInstance(obj *Instance) (*Instance, error) { + name, err := obj.GetClassName() + if err != nil { + return nil, err + } + return s.GetObject(name) +} diff --git a/vendor/github.com/go-ole/go-ole/SECURITY.md b/vendor/github.com/go-ole/go-ole/SECURITY.md new file mode 100644 index 0000000000..dac281523b --- /dev/null +++ b/vendor/github.com/go-ole/go-ole/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/go-ole/go-ole/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/go-ole/go-ole/appveyor.yml b/vendor/github.com/go-ole/go-ole/appveyor.yml index 0d557ac2ff..8df7fa26e3 100644 --- a/vendor/github.com/go-ole/go-ole/appveyor.yml +++ b/vendor/github.com/go-ole/go-ole/appveyor.yml @@ -6,14 +6,9 @@ version: "1.3.0.{build}-alpha-{branch}" -os: Windows Server 2012 R2 +os: Visual Studio 2019 -branches: - only: - - master - - v1.2 - - v1.1 - - v1.0 +build: off skip_tags: true @@ -21,20 +16,40 @@ clone_folder: c:\gopath\src\github.com\go-ole\go-ole environment: GOPATH: c:\gopath - matrix: - - GOARCH: amd64 - GOVERSION: 1.5 - GOROOT: c:\go - DOWNLOADPLATFORM: "x64" + GOROOT: c:\go + DOWNLOADPLATFORM: "x64" -install: - - choco install mingw - - SET PATH=c:\tools\mingw64\bin;%PATH% +before_test: # - Download COM Server - ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip" - 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL - c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat - # - set + +test_script: + - go test -v -cover ./... + # go vet has false positives on unsafe.Pointer with windows/sys. Disabling since it is recommended to use go test instead. + # - go vet ./... + +branches: + only: + - master + - v1.2 + - v1.1 + - v1.0 + +matrix: + allow_failures: + - environment: + GOROOT: C:\go-x86 + DOWNLOADPLATFORM: "x86" + - environment: + GOROOT: C:\go118 + DOWNLOADPLATFORM: "x64" + - environment: + GOROOT: C:\go118-x86 + DOWNLOADPLATFORM: "x86" + +install: - go version - go env - go get -u golang.org/x/tools/cmd/cover @@ -45,10 +60,9 @@ build_script: - cd c:\gopath\src\github.com\go-ole\go-ole - go get -v -t ./... - go build - - go test -v -cover ./... # disable automatic tests -test: off +test: on # disable deployment deploy: off diff --git a/vendor/github.com/go-ole/go-ole/com.go b/vendor/github.com/go-ole/go-ole/com.go index a9bef150a3..cabbac0122 100644 --- a/vendor/github.com/go-ole/go-ole/com.go +++ b/vendor/github.com/go-ole/go-ole/com.go @@ -11,6 +11,7 @@ import ( var ( procCoInitialize = modole32.NewProc("CoInitialize") procCoInitializeEx = modole32.NewProc("CoInitializeEx") + procCoInitializeSecurity = modole32.NewProc("CoInitializeSecurity") procCoUninitialize = modole32.NewProc("CoUninitialize") procCoCreateInstance = modole32.NewProc("CoCreateInstance") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") @@ -37,6 +38,9 @@ var ( procDispatchMessageW = moduser32.NewProc("DispatchMessageW") ) +// This is to enable calling COM Security initialization multiple times +var bSecurityInit bool = false + // coInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call @@ -68,6 +72,35 @@ func coInitializeEx(coinit uint32) (err error) { return } +// coInitializeSecurity: Registers security and sets the default security values +// for the process. +func coInitializeSecurity(cAuthSvc int32, + dwAuthnLevel uint32, + dwImpLevel uint32, + dwCapabilities uint32) (err error) { + // Check COM Security initialization has done previously + if !bSecurityInit { + // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializesecurity + hr, _, _ := procCoInitializeSecurity.Call( + uintptr(0), // Allow *all* VSS writers to communicate back! + uintptr(cAuthSvc), // Default COM authentication service + uintptr(0), // Default COM authorization service + uintptr(0), // Reserved parameter + uintptr(dwAuthnLevel), // Strongest COM authentication level + uintptr(dwImpLevel), // Minimal impersonation abilities + uintptr(0), // Default COM authentication settings + uintptr(dwCapabilities), // Cloaking + uintptr(0)) // eserved parameter + if hr != 0 { + err = NewError(hr) + } else { + // COM Security initialization done make global flag true. + bSecurityInit = true + } + } + return +} + // CoInitialize initializes COM library on current thread. // // MSDN documentation suggests that this function should not be called. Call @@ -96,6 +129,15 @@ func CoUninitialize() { procCoUninitialize.Call() } +// CoInitializeSecurity: Registers security and sets the default security values +// for the process. +func CoInitializeSecurity(cAuthSvc int32, + dwAuthnLevel uint32, + dwImpLevel uint32, + dwCapabilities uint32) (err error) { + return coInitializeSecurity(cAuthSvc, dwAuthnLevel, dwImpLevel, dwCapabilities) +} + // CoTaskMemFree frees memory pointer. func CoTaskMemFree(memptr uintptr) { procCoTaskMemFree.Call(memptr) diff --git a/vendor/github.com/go-ole/go-ole/idispatch_windows.go b/vendor/github.com/go-ole/go-ole/idispatch_windows.go index b399f04791..649c0734ff 100644 --- a/vendor/github.com/go-ole/go-ole/idispatch_windows.go +++ b/vendor/github.com/go-ole/go-ole/idispatch_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package ole @@ -92,7 +93,7 @@ func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{} case int8: vargs[n] = NewVariant(VT_I1, int64(v.(int8))) case *int8: - vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8))))) + vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int8))))) case int16: vargs[n] = NewVariant(VT_I2, int64(v.(int16))) case *int16: diff --git a/vendor/github.com/go-ole/go-ole/variant.go b/vendor/github.com/go-ole/go-ole/variant.go index 967a23fea9..a2c8402f7b 100644 --- a/vendor/github.com/go-ole/go-ole/variant.go +++ b/vendor/github.com/go-ole/go-ole/variant.go @@ -99,7 +99,7 @@ func (v *VARIANT) Value() interface{} { case VT_DISPATCH: return v.ToIDispatch() case VT_BOOL: - return v.Val != 0 + return (v.Val & 0xffff) != 0 } return nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index c906e0dfdb..0771ddae61 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -92,6 +92,9 @@ github.com/cloudflare/circl/math/mlsbset github.com/cloudflare/circl/sign github.com/cloudflare/circl/sign/ed25519 github.com/cloudflare/circl/sign/ed448 +# github.com/containers/common v0.59.1 +## explicit; go 1.21 +github.com/containers/common/pkg/strongunits # github.com/containers/gvisor-tap-vsock v0.7.5 ## explicit; go 1.21 github.com/containers/gvisor-tap-vsock/pkg/client @@ -158,6 +161,11 @@ github.com/containers/image/v5/signature/simplesigning github.com/containers/image/v5/transports github.com/containers/image/v5/types github.com/containers/image/v5/version +# github.com/containers/libhvee v0.7.1 +## explicit; go 1.18 +github.com/containers/libhvee/pkg/hypervctl +github.com/containers/libhvee/pkg/kvp/ginsu +github.com/containers/libhvee/pkg/wmiext # github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 ## explicit github.com/containers/libtrust @@ -296,7 +304,7 @@ github.com/go-jose/go-jose/v4/json ## explicit; go 1.18 github.com/go-logr/logr github.com/go-logr/logr/funcr -# github.com/go-ole/go-ole v1.2.6 +# github.com/go-ole/go-ole v1.3.0 ## explicit; go 1.12 github.com/go-ole/go-ole github.com/go-ole/go-ole/oleutil