Manually discover wsl.exe location

Works around a problem where recent Windows updates do not always redirect the
system wsl to the app store wsl version correctly.

Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
This commit is contained in:
Jason T. Greene 2024-02-14 16:53:30 -06:00
parent 889454104e
commit 4fffa78eec
4 changed files with 74 additions and 18 deletions

View File

@ -48,7 +48,7 @@ func installWslKernel() error {
)
backoff := 500 * time.Millisecond
for i := 1; i < 6; i++ {
err = wutil.SilentExec("wsl", "--update")
err = wutil.SilentExec(wutil.FindWSL(), "--update")
if err == nil {
break
}

View File

@ -90,7 +90,7 @@ func provisionWSLDist(name string, imagePath string, prompt string) (string, err
dist := machine.ToDist(name)
fmt.Println(prompt)
if err = runCmdPassThrough("wsl", "--import", dist, distTarget, imagePath, "--version", "2"); err != nil {
if err = runCmdPassThrough(wutil.FindWSL(), "--import", dist, distTarget, imagePath, "--version", "2"); err != nil {
return "", fmt.Errorf("the WSL import of guest OS failed: %w", err)
}
@ -446,7 +446,7 @@ func installWslKernel() error {
backoff := 500 * time.Millisecond
for i := 0; i < 5; i++ {
err = runCmdPassThroughTee(log, "wsl", "--update")
err = runCmdPassThroughTee(log, wutil.FindWSL(), "--update")
if err == nil {
break
}
@ -556,17 +556,17 @@ func withUser(s string, user string) string {
func wslInvoke(dist string, arg ...string) error {
newArgs := []string{"-u", "root", "-d", dist}
newArgs = append(newArgs, arg...)
return runCmdPassThrough("wsl", newArgs...)
return runCmdPassThrough(wutil.FindWSL(), newArgs...)
}
func wslPipe(input string, dist string, arg ...string) error {
newArgs := []string{"-u", "root", "-d", dist}
newArgs = append(newArgs, arg...)
return pipeCmdPassThrough("wsl", input, newArgs...)
return pipeCmdPassThrough(wutil.FindWSL(), input, newArgs...)
}
func wslCreateKeys(identityPath string, dist string) (string, error) {
return machine.CreateSSHKeysPrefix(identityPath, true, true, "wsl", "-u", "root", "-d", dist)
return machine.CreateSSHKeysPrefix(identityPath, true, true, wutil.FindWSL(), "-u", "root", "-d", dist)
}
func runCmdPassThrough(name string, arg ...string) error {
@ -631,7 +631,7 @@ func obtainGlobalConfigLock() (*fileLock, error) {
}
func IsWSLFeatureEnabled() bool {
return wutil.SilentExec("wsl", "--set-default-version", "2") == nil
return wutil.SilentExec(wutil.FindWSL(), "--set-default-version", "2") == nil
}
func isWSLRunning(dist string) (bool, error) {
@ -657,7 +657,7 @@ func getAllWSLDistros(running bool) (map[string]struct{}, error) {
if running {
args = append(args, "--running")
}
cmd := exec.Command("wsl", args...)
cmd := exec.Command(wutil.FindWSL(), args...)
out, err := cmd.StdoutPipe()
if err != nil {
return nil, err
@ -681,7 +681,7 @@ func getAllWSLDistros(running bool) (map[string]struct{}, error) {
}
func isSystemdRunning(dist string) (bool, error) {
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "sh")
cmd := exec.Command(wutil.FindWSL(), "-u", "root", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(sysdpid + "\necho $SYSDPID\n")
out, err := cmd.StdoutPipe()
if err != nil {
@ -706,12 +706,12 @@ func isSystemdRunning(dist string) (bool, error) {
}
func terminateDist(dist string) error {
cmd := exec.Command("wsl", "--terminate", dist)
cmd := exec.Command(wutil.FindWSL(), "--terminate", dist)
return cmd.Run()
}
func unregisterDist(dist string) error {
cmd := exec.Command("wsl", "--unregister", dist)
cmd := exec.Command(wutil.FindWSL(), "--unregister", dist)
return cmd.Run()
}
@ -753,7 +753,7 @@ func getCPUs(name string) (uint64, error) {
if run, _ := isWSLRunning(dist); !run {
return 0, nil
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "nproc")
cmd := exec.Command(wutil.FindWSL(), "-u", "root", "-d", dist, "nproc")
out, err := cmd.StdoutPipe()
if err != nil {
return 0, err
@ -777,7 +777,7 @@ func getMem(name string) (uint64, error) {
if run, _ := isWSLRunning(dist); !run {
return 0, nil
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "cat", "/proc/meminfo")
cmd := exec.Command(wutil.FindWSL(), "-u", "root", "-d", dist, "cat", "/proc/meminfo")
out, err := cmd.StdoutPipe()
if err != nil {
return 0, err

View File

@ -12,6 +12,7 @@ import (
"github.com/containers/podman/v5/pkg/machine/ocipull"
"github.com/containers/podman/v5/pkg/machine/stdpull"
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/podman/v5/pkg/machine"
@ -105,7 +106,7 @@ func (w WSLStubber) Remove(mc *vmconfigs.MachineConfig) ([]string, func() error,
// below if we wanted to hard error on the wsl unregister
// of the vm
wslRemoveFunc := func() error {
if err := runCmdPassThrough("wsl", "--unregister", machine.ToDist(mc.Name)); err != nil {
if err := runCmdPassThrough(wutil.FindWSL(), "--unregister", machine.ToDist(mc.Name)); err != nil {
logrus.Error(err)
}
return machine.ReleaseMachinePort(mc.SSH.Port)
@ -266,13 +267,13 @@ func (w WSLStubber) StopVM(mc *vmconfigs.MachineConfig, hardStop bool) error {
fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
}
cmd := exec.Command("wsl", "-u", "root", "-d", dist, "sh")
cmd := exec.Command(wutil.FindWSL(), "-u", "root", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(waitTerm)
if err = cmd.Start(); err != nil {
return fmt.Errorf("executing wait command: %w", err)
}
exitCmd := exec.Command("wsl", "-u", "root", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
exitCmd := exec.Command(wutil.FindWSL(), "-u", "root", "-d", dist, "/usr/local/bin/enterns", "systemctl", "exit", "0")
if err = exitCmd.Run(); err != nil {
return fmt.Errorf("stopping sysd: %w", err)
}

View File

@ -5,14 +5,69 @@ package wutil
import (
"bufio"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"syscall"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
)
var (
once sync.Once
wslPath string
)
func FindWSL() string {
// At the time of this writing, a defect appeared in the OS preinstalled WSL executable
// where it no longer reliably locates the preferred Windows App Store variant.
//
// Manually discover (and cache) the wsl.exe location to bypass the problem
once.Do(func() {
var locs []string
// Prefer Windows App Store version
if localapp := getLocalAppData(); localapp != "" {
locs = append(locs, filepath.Join(localapp, "Microsoft", "WindowsApps", "wsl.exe"))
}
// Otherwise, the common location for the legacy system version
root := os.Getenv("SystemRoot")
if root == "" {
root = `C:\Windows`
}
locs = append(locs, filepath.Join(root, "System32", "wsl.exe"))
for _, loc := range locs {
if _, err := os.Stat(loc); err == nil {
wslPath = loc
return
}
}
// Hope for the best
wslPath = "wsl"
})
return wslPath
}
func getLocalAppData() string {
localapp := os.Getenv("LOCALAPPDATA")
if localapp != "" {
return localapp
}
if user := os.Getenv("USERPROFILE"); user != "" {
return filepath.Join(user, "AppData", "Local")
}
return localapp
}
func SilentExec(command string, args ...string) error {
cmd := exec.Command(command, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
@ -28,7 +83,7 @@ func SilentExecCmd(command string, args ...string) *exec.Cmd {
}
func IsWSLInstalled() bool {
cmd := SilentExecCmd("wsl", "--status")
cmd := SilentExecCmd(FindWSL(), "--status")
out, err := cmd.StdoutPipe()
cmd.Stderr = nil
if err != nil {
@ -48,7 +103,7 @@ func IsWSLInstalled() bool {
}
func IsWSLStoreVersionInstalled() bool {
cmd := SilentExecCmd("wsl", "--version")
cmd := SilentExecCmd(FindWSL(), "--version")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {