diff options
Diffstat (limited to 'container/initdaemon.go')
| -rw-r--r-- | container/initdaemon.go | 55 |
1 files changed, 42 insertions, 13 deletions
diff --git a/container/initdaemon.go b/container/initdaemon.go index ee360dc9..0e631b25 100644 --- a/container/initdaemon.go +++ b/container/initdaemon.go @@ -8,7 +8,7 @@ import ( "os" "os/exec" "slices" - "sync/atomic" + "strconv" "syscall" "time" @@ -41,6 +41,38 @@ type DaemonOp struct { Args []string } +// earlyTerminationError is returned by [DaemonOp] when a daemon terminates +// before [DaemonOp.Target] appears. +type earlyTerminationError struct { + // Returned by [DaemonOp.String]. + op string + // Copied from wait4 loop. + wstatus syscall.WaitStatus +} + +func (e *earlyTerminationError) Error() string { + res := "" + switch { + case e.wstatus.Exited(): + res = "exit status " + strconv.Itoa(e.wstatus.ExitStatus()) + case e.wstatus.Signaled(): + res = "signal: " + e.wstatus.Signal().String() + case e.wstatus.Stopped(): + res = "stop signal: " + e.wstatus.StopSignal().String() + if e.wstatus.StopSignal() == syscall.SIGTRAP && e.wstatus.TrapCause() != 0 { + res += " (trap " + strconv.Itoa(e.wstatus.TrapCause()) + ")" + } + case e.wstatus.Continued(): + res = "continued" + } + if e.wstatus.CoreDump() { + res += " (core dumped)" + } + return res +} + +func (e *earlyTerminationError) Message() string { return e.op + " " + e.Error() } + func (d *DaemonOp) Valid() bool { return d != nil && d.Target != nil && d.Path != nil } func (d *DaemonOp) early(*setupState, syscallDispatcher) error { return nil } func (d *DaemonOp) apply(*setupState, syscallDispatcher) error { return nil } @@ -59,17 +91,9 @@ func (d *DaemonOp) late(state *setupState, k syscallDispatcher) error { return err } - var done atomic.Pointer[error] - k.new(func(k syscallDispatcher) { - err := k.wait(cmd) - done.Store(&err) - - if err != nil { - state.Verbosef("%s %v", d.String(), err) - } - }) - deadline := time.Now().Add(daemonTimeout) + var wstatusErr error + for { if _, err := k.stat(d.Target.String()); err != nil { if !errors.Is(err, os.ErrNotExist) { @@ -82,8 +106,13 @@ func (d *DaemonOp) late(state *setupState, k syscallDispatcher) error { return context.DeadlineExceeded } - if errP := done.Load(); errP != nil { - return *errP + if wstatusErr != nil { + return wstatusErr + } + if wstatus, ok := state.terminated(cmd.Process.Pid); ok { + // check once again: process could have satisfied Target between stat and the lookup + wstatusErr = &earlyTerminationError{d.String(), wstatus} + continue } time.Sleep(500 * time.Microsecond) |
