diff --git a/pkg/machine/gvproxy.go b/pkg/machine/gvproxy.go index 97eef42ac0..5f75c11557 100644 --- a/pkg/machine/gvproxy.go +++ b/pkg/machine/gvproxy.go @@ -5,13 +5,37 @@ import ( "fmt" "io/fs" "strconv" + "time" "github.com/containers/podman/v6/pkg/machine/define" ) +const ( + pidFileWaitTimeout = 2 * time.Second + pidFileCheckInterval = 50 * time.Millisecond +) + +func readPIDFileWithRetry(f define.VMFile) ([]byte, error) { + deadline := time.Now().Add(pidFileWaitTimeout) + + for time.Now().Before(deadline) { + gvPid, err := f.Read() + if err == nil { + return gvPid, nil + } + if !errors.Is(err, fs.ErrNotExist) { + return nil, err + } + time.Sleep(pidFileCheckInterval) + } + + // Final attempt after timeout + return f.Read() +} + // CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it func CleanupGVProxy(f define.VMFile) error { - gvPid, err := f.Read() + gvPid, err := readPIDFileWithRetry(f) if err != nil { // The file will also be removed by gvproxy when it exits so // we need to account for the race and can just ignore it here. diff --git a/pkg/machine/hyperv/hutil.go b/pkg/machine/hyperv/hutil.go index 72cf15dc71..c2fb950285 100644 --- a/pkg/machine/hyperv/hutil.go +++ b/pkg/machine/hyperv/hutil.go @@ -15,6 +15,7 @@ var ( ErrHypervRegistryInitRequiresElevation = errors.New("the first time Podman initializes a Hyper-V machine, it requires admin rights. Please run Podman as an administrator") ErrHypervRegistryRemoveRequiresElevation = errors.New("removing this Hyper-V machine requires admin rights to clean up the Windows Registry. Please run Podman as an administrator") ErrHypervRegistryUpdateRequiresElevation = errors.New("this machine's configuration requires additional Hyper-V networking (hvsock) entries in the Windows Registry. Please run Podman as an administrator") + ErrHypervLegacyMachineRequiresElevation = errors.New("starting or stopping Hyper-V machines created with Podman 5.x or earlier requires admin rights. Please run Podman as an administrator") ) func HasHyperVAdminRights() bool { diff --git a/pkg/machine/hyperv/stubber.go b/pkg/machine/hyperv/stubber.go index 245860127d..2a30607bec 100644 --- a/pkg/machine/hyperv/stubber.go +++ b/pkg/machine/hyperv/stubber.go @@ -265,6 +265,21 @@ func (h HyperVStubber) canRemove(mc *vmconfigs.MachineConfig) error { return ErrHypervRegistryRemoveRequiresElevation } +// canStartOrStop checks if the machine can be started or stopped. +// Legacy machines require admin rights to start or stop. +func (h HyperVStubber) canStartOrStop(mc *vmconfigs.MachineConfig) error { + if windows.HasAdminRights() { + return nil + } + + // if machine is legacy (machineName field), require admin rights to start or stop + if isLegacyMachine(mc) { + return ErrHypervLegacyMachineRequiresElevation + } + + return nil +} + // countMachinesWithToolname counts only machines that have a toolname field with value "podman". func (h HyperVStubber) countMachinesWithToolname() (int, error) { dirs, err := env.GetMachineDirs(h.VMType()) @@ -345,7 +360,9 @@ func (h HyperVStubber) StartNetworking(mc *vmconfigs.MachineConfig, cmd *gvproxy } func (h HyperVStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) { - var err error + if err := h.canStartOrStop(mc); err != nil { + return nil, nil, err + } _, vm, err := GetVMFromMC(mc) if err != nil { @@ -412,6 +429,10 @@ func (h HyperVStubber) State(mc *vmconfigs.MachineConfig, _ bool) (define.Status } func (h HyperVStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error { + if err := h.canStartOrStop(mc); err != nil { + return err + } + vmm := hypervctl.NewVirtualMachineManager() vm, err := vmm.GetMachine(mc.Name) if err != nil {