aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/initdaemon.go
diff options
context:
space:
mode:
Diffstat (limited to 'container/initdaemon.go')
-rw-r--r--container/initdaemon.go55
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)