From 697e66c784da5390719c1d859723b22833f125c0 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Tue, 21 Nov 2023 18:14:14 +0100 Subject: [PATCH] machine applehv: create better error on start failure If gvproxy or vfkit exit we can error right away, so while we wait for the socket to get ready we also keep checking the process status with wait4() and WNOHANG so it does not block forever. This is completely untested as I do not have acces to apple machine. Signed-off-by: Paul Holzinger [NO NEW TESTS NEEDED] Signed-off-by: Matt Heon --- pkg/machine/applehv/machine.go | 53 ++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index 45737296f3..6e628c873b 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -4,6 +4,7 @@ package applehv import ( + "context" "encoding/json" "errors" "fmt" @@ -669,9 +670,36 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error { return err } - err = <-readyChan - if err != nil { - return err + processErrChan := make(chan error) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { + defer close(processErrChan) + for { + select { + case <-ctx.Done(): + return + default: + } + if err := checkProcessRunning("vfkit", cmd.Process.Pid); err != nil { + processErrChan <- err + return + } + // lets poll status every half second + time.Sleep(500 * time.Millisecond) + } + }() + + // wait for either socket or to be ready or process to have exited + select { + case err := <-processErrChan: + if err != nil { + return err + } + case err := <-readyChan: + if err != nil { + return err + } } logrus.Debug("ready notification received") @@ -902,6 +930,10 @@ func (m *MacMachine) startHostNetworking() (string, machine.APIForwardingState, if err == nil { break } + if err := checkProcessRunning("gvproxy", c.Process.Pid); err != nil { + // gvproxy is no longer running + return "", 0, err + } logrus.Debugf("gvproxy unixgram socket %q not found: %v", m.GvProxySock.GetPath(), err) // Sleep for 1/2 second time.Sleep(500 * time.Millisecond) @@ -914,6 +946,21 @@ func (m *MacMachine) startHostNetworking() (string, machine.APIForwardingState, return forwardSock, state, nil } +// checkProcessRunning checks non blocking if the pid exited +// returns nil if process is running otherwise an error if not +func checkProcessRunning(processName string, pid int) error { + var status syscall.WaitStatus + pid, err := syscall.Wait4(pid, &status, syscall.WNOHANG, nil) + if err != nil { + return fmt.Errorf("failed to read %s process status: %w", processName, err) + } + if pid > 0 { + // child exited + return fmt.Errorf("%s exited unexpectedly with exit code %d", processName, status.ExitStatus()) + } + return nil +} + func (m *MacMachine) setupAPIForwarding(cmd gvproxy.GvproxyCommand) (gvproxy.GvproxyCommand, string, machine.APIForwardingState) { socket, err := m.forwardSocketPath() if err != nil {