summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorJason T. Greene <jason.greene@redhat.com>2022-01-18 14:39:48 -0600
committerJason T. Greene <jason.greene@redhat.com>2022-01-19 13:03:49 -0600
commit2d0b5ebb5bdc06cf34488a75318c0ba66e3f2edf (patch)
treee9a59f462b65e6d46d42a779c88c8b38804de78b /pkg
parent094b11cbcb528a7d120c31402a1bcd9c82d84938 (diff)
downloadpodman-2d0b5ebb5bdc06cf34488a75318c0ba66e3f2edf.tar.gz
podman-2d0b5ebb5bdc06cf34488a75318c0ba66e3f2edf.tar.bz2
podman-2d0b5ebb5bdc06cf34488a75318c0ba66e3f2edf.zip
Implement API forwarding for podman machine on Windows
Signed-off-by: Jason T. Greene <jason.greene@redhat.com>
Diffstat (limited to 'pkg')
-rw-r--r--pkg/machine/wsl/machine.go163
-rw-r--r--pkg/machine/wsl/util_windows.go19
2 files changed, 179 insertions, 3 deletions
diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go
index 6cab855d3..c7d857954 100644
--- a/pkg/machine/wsl/machine.go
+++ b/pkg/machine/wsl/machine.go
@@ -1,4 +1,3 @@
-//go:build windows
// +build windows
package wsl
@@ -143,6 +142,11 @@ http://docs.microsoft.com/en-us/windows/wsl/install\
`
+const (
+ winSShProxy = "win-sshproxy.exe"
+ winSshProxyTid = "win-sshproxy.tid"
+)
+
type Provider struct{}
type MachineVM struct {
@@ -705,8 +709,6 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return errors.Errorf("%q is already running", name)
}
- fmt.Println("Starting machine...")
-
dist := toDist(name)
err := runCmdPassThrough("wsl", "-d", dist, "/root/bootstrap")
@@ -714,9 +716,107 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error {
return errors.Wrap(err, "WSL bootstrap script failed")
}
+ globalName, pipeName, err := launchWinProxy(v)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "API forwarding for Docker API clients is not available due to the following startup failures.")
+ fmt.Fprintf(os.Stderr, "\t%s\n", err.Error())
+ fmt.Fprintln(os.Stderr, "\nPodman clients are still able to connect.")
+ } else {
+ fmt.Printf("API forwarding listening on: %s\n", pipeName)
+ if globalName {
+ fmt.Printf("\nDocker API clients default to this address. You do not need to set DOCKER_HOST.\n")
+ } else {
+ fmt.Printf("\nAnother process was listening on the default Docker API pipe address.\n")
+ fmt.Printf("You can still connect Docker API clients by setting DOCKER HOST using the\n")
+ fmt.Printf("following powershell command in your terminal session:\n")
+ fmt.Printf("\n\t$Env:DOCKER_HOST = '%s'\n", pipeName)
+ fmt.Printf("\nOr in a classic CMD prompt:\n")
+ fmt.Printf("\n\tset DOCKER_HOST = '%s'\n", pipeName)
+ fmt.Printf("\nAlternatively terminate the other process and restart podman machine.\n")
+ }
+ }
+
return markStart(name)
}
+func launchWinProxy(v *MachineVM) (bool, string, error) {
+ globalName := true
+ pipeName := "docker_engine"
+ if !pipeAvailable(pipeName) {
+ pipeName = toDist(v.Name)
+ globalName = false
+ if !pipeAvailable(pipeName) {
+ return globalName, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", pipeName)
+ }
+ }
+ fullPipeName := "npipe:////./pipe/" + pipeName
+
+ exe, err := os.Executable()
+ if err != nil {
+ return globalName, "", err
+ }
+
+ exe, err = filepath.EvalSymlinks(exe)
+ if err != nil {
+ return globalName, "", err
+ }
+
+ command := filepath.Join(filepath.Dir(exe), winSShProxy)
+ stateDir, err := getWinProxyStateDir(v)
+ if err != nil {
+ return globalName, "", err
+ }
+
+ dest := fmt.Sprintf("ssh://root@localhost:%d/run/podman/podman.sock", v.Port)
+ cmd := exec.Command(command, v.Name, stateDir, fullPipeName, dest, v.IdentityPath)
+ if err := cmd.Start(); err != nil {
+ return globalName, "", err
+ }
+
+ return globalName, fullPipeName, waitPipeExists(pipeName, 30, func() error {
+ active, exitCode := getProcessState(cmd.Process.Pid)
+ if !active {
+ return errors.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode)
+ }
+
+ return nil
+ })
+}
+
+func getWinProxyStateDir(v *MachineVM) (string, error) {
+ dir, err := machine.GetDataDir(vmtype)
+ if err != nil {
+ return "", err
+ }
+ stateDir := filepath.Join(dir, v.Name)
+ if err = os.MkdirAll(stateDir, 0755); err != nil {
+ return "", err
+ }
+
+ return stateDir, nil
+}
+
+func pipeAvailable(pipeName string) bool {
+ _, err := os.Stat(`\\.\pipe\` + pipeName)
+ return os.IsNotExist(err)
+}
+
+func waitPipeExists(pipeName string, retries int, checkFailure func() error) error {
+ var err error
+ for i := 0; i < retries; i++ {
+ _, err = os.Stat(`\\.\pipe\` + pipeName)
+ if err == nil {
+ break
+ }
+ if fail := checkFailure(); fail != nil {
+ return fail
+ }
+ time.Sleep(100 * time.Millisecond)
+ }
+
+ return err
+}
+
func isWSLInstalled() bool {
cmd := exec.Command("wsl", "--status")
out, err := cmd.StdoutPipe()
@@ -817,6 +917,10 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return errors.Errorf("%q is not running", v.Name)
}
+ if err := stopWinProxy(v); err != nil {
+ fmt.Fprintf(os.Stderr, "Could not stop API forwarding service (win-sshproxy.exe): %s\n", err.Error())
+ }
+
cmd := exec.Command("wsl", "-d", dist, "sh")
cmd.Stdin = strings.NewReader(waitTerm)
if err = cmd.Start(); err != nil {
@@ -840,6 +944,59 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return nil
}
+func stopWinProxy(v *MachineVM) error {
+ pid, tid, tidFile, err := readWinProxyTid(v)
+ if err != nil {
+ return err
+ }
+
+ proc, err := os.FindProcess(int(pid))
+ if err != nil {
+ return nil
+ }
+ sendQuit(tid)
+ _ = waitTimeout(proc, 20*time.Second)
+ _ = os.Remove(tidFile)
+
+ return nil
+}
+
+func waitTimeout(proc *os.Process, timeout time.Duration) bool {
+ done := make(chan bool)
+ go func() {
+ proc.Wait()
+ done <- true
+ }()
+ ret := false
+ select {
+ case <-time.After(timeout):
+ proc.Kill()
+ <-done
+ case <-done:
+ ret = true
+ break
+ }
+
+ return ret
+}
+
+func readWinProxyTid(v *MachineVM) (uint32, uint32, string, error) {
+ stateDir, err := getWinProxyStateDir(v)
+ if err != nil {
+ return 0, 0, "", err
+ }
+
+ tidFile := filepath.Join(stateDir, winSshProxyTid)
+ contents, err := ioutil.ReadFile(tidFile)
+ if err != nil {
+ return 0, 0, "", err
+ }
+
+ var pid, tid uint32
+ fmt.Sscanf(string(contents), "%d:%d", &pid, &tid)
+ return pid, tid, tidFile, nil
+}
+
//nolint:cyclop
func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) {
var files []string
diff --git a/pkg/machine/wsl/util_windows.go b/pkg/machine/wsl/util_windows.go
index 95e4c9894..b5c28e015 100644
--- a/pkg/machine/wsl/util_windows.go
+++ b/pkg/machine/wsl/util_windows.go
@@ -67,6 +67,7 @@ const (
TOKEN_QUERY = 0x0008
SE_PRIVILEGE_ENABLED = 0x00000002
SE_ERR_ACCESSDENIED = 0x05
+ WM_QUIT = 0x12
)
func winVersionAtLeast(major uint, minor uint, build uint) bool {
@@ -279,6 +280,18 @@ func obtainShutdownPrivilege() error {
return nil
}
+func getProcessState(pid int) (active bool, exitCode int) {
+ const da = syscall.STANDARD_RIGHTS_READ | syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
+ handle, err := syscall.OpenProcess(da, false, uint32(pid))
+ if err != nil {
+ return false, int(syscall.ERROR_PROC_NOT_FOUND)
+ }
+
+ var code uint32
+ syscall.GetExitCodeProcess(handle, &code)
+ return code == 259, int(code)
+}
+
func addRunOnceRegistryEntry(command string) error {
k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\RunOnce`, registry.WRITE)
if err != nil {
@@ -336,3 +349,9 @@ func buildCommandArgs(elevate bool) string {
}
return strings.Join(args, " ")
}
+
+func sendQuit(tid uint32) {
+ user32 := syscall.NewLazyDLL("user32.dll")
+ postMessage := user32.NewProc("PostThreadMessageW")
+ postMessage.Call(uintptr(tid), WM_QUIT, 0, 0)
+}