Files
podman/pkg/machine/wsl/wutil/wutil.go
lstocchi 8532ecb710 fix wsl install workflow on machine init command
this patch changes how the detection of wsl works.
The old way of using wsl --status command output to detect some missing features required by WSL is not fully reliable.
WSL checks if the wsl feature is enabled and if the vmcompute service do exist. However, this is not enough to identify if the virtual machine platform feature is enabled. The vmcompute service could exist because it has been installed by other tools or it could exist but being stopped.

The way proposed by this patch is to try execute the import command and,
if it fails, check the error and if it is related to the Host Compute
Service try to install all features required by WSL.

The flow is the same as before, the user is asked to execute the podman
machine init command with elevated privileges. Eventually, after
enabling WSL and VMP features, the user is asked to reboot the machine.

When the machine restarts, the powershell gets invoked again and execute
the command init.

The code also fixes some issues that could cause misbehaviors when
invoking recursively the elevated shell, like an unreleased lock, or a
missing file.

Signed-off-by: lstocchi <lstocchi@redhat.com>
2025-06-04 14:26:48 +02:00

119 lines
2.6 KiB
Go

//go:build windows
package wutil
import (
"bufio"
"fmt"
"io"
"os/exec"
"strings"
"sync"
"syscall"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
var (
onceStatus sync.Once
status wslStatus
wslNotInstalledMessages = []string{"kernel file is not found", "The Windows Subsystem for Linux is not installed"}
vmpDisabledMessages = []string{"enable the Virtual Machine Platform Windows feature", "Enable \"Virtual Machine Platform\""}
wslDisabledMessages = []string{"enable the \"Windows Subsystem for Linux\" optional component"}
)
type wslStatus struct {
installed bool
vmpFeatureEnabled bool
wslFeatureEnabled bool
}
func SilentExec(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return fmt.Errorf("command %s %v failed: %w", command, args, err)
}
return nil
}
func SilentExecCmd(command string, args ...string) *exec.Cmd {
cmd := exec.Command(command, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
return cmd
}
func parseWSLStatus() wslStatus {
onceStatus.Do(func() {
status = wslStatus{
installed: false,
vmpFeatureEnabled: false,
wslFeatureEnabled: false,
}
cmd := SilentExecCmd("wsl", "--status")
out, err := cmd.StdoutPipe()
cmd.Stderr = nil
if err != nil {
return
}
if err = cmd.Start(); err != nil {
return
}
status = matchOutputLine(out)
if err := cmd.Wait(); err != nil {
return
}
})
return status
}
func IsWSLInstalled() bool {
status := parseWSLStatus()
return status.installed && status.vmpFeatureEnabled
}
func IsWSLStoreVersionInstalled() bool {
cmd := SilentExecCmd("wsl", "--version")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return false
}
return true
}
func matchOutputLine(output io.ReadCloser) wslStatus {
status := wslStatus{
installed: true,
vmpFeatureEnabled: true,
wslFeatureEnabled: true,
}
scanner := bufio.NewScanner(transform.NewReader(output, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
for scanner.Scan() {
line := scanner.Text()
for _, match := range wslNotInstalledMessages {
if strings.Contains(line, match) {
status.installed = false
}
}
for _, match := range vmpDisabledMessages {
if strings.Contains(line, match) {
status.vmpFeatureEnabled = false
}
}
for _, match := range wslDisabledMessages {
if strings.Contains(line, match) {
status.wslFeatureEnabled = false
}
}
}
return status
}