Files
Jason T. Greene 4fffa78eec 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>
2024-02-15 16:31:10 -06:00

126 lines
2.6 KiB
Go

//go:build windows
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}
cmd.Stdout = nil
cmd.Stderr = nil
return cmd.Run()
}
func SilentExecCmd(command string, args ...string) *exec.Cmd {
cmd := exec.Command(command, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: 0x08000000}
return cmd
}
func IsWSLInstalled() bool {
cmd := SilentExecCmd(FindWSL(), "--status")
out, err := cmd.StdoutPipe()
cmd.Stderr = nil
if err != nil {
return false
}
if err = cmd.Start(); err != nil {
return false
}
kernelNotFound := matchOutputLine(out, "kernel file is not found")
if err := cmd.Wait(); err != nil {
return false
}
return !kernelNotFound
}
func IsWSLStoreVersionInstalled() bool {
cmd := SilentExecCmd(FindWSL(), "--version")
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return false
}
return true
}
func matchOutputLine(output io.ReadCloser, match string) bool {
scanner := bufio.NewScanner(transform.NewReader(output, unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, match) {
return true
}
}
return false
}