From 2f74adc8bdeda2499be0a8b2711f68c342695bf2 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 10 Nov 2025 20:35:59 +0900 Subject: [PATCH] container/init: close initial process files on termination This closes them during the adopt wait delay. This also keeps them alive. Signed-off-by: Ophestra --- container/check/absolute_test.go | 2 +- container/container_test.go | 1 + container/fhs/abs.go | 2 +- container/init.go | 12 ++++++++++-- container/init_test.go | 13 +++++++++++++ container/stub/exit_test.go | 2 +- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/container/check/absolute_test.go b/container/check/absolute_test.go index 5cb96d7..466bbac 100644 --- a/container/check/absolute_test.go +++ b/container/check/absolute_test.go @@ -9,7 +9,7 @@ import ( "strings" "syscall" "testing" - _ "unsafe" + _ "unsafe" // for go:linkname . "hakurei.app/container/check" ) diff --git a/container/container_test.go b/container/container_test.go index b77ce5f..5aaa876 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -722,6 +722,7 @@ func TestMain(m *testing.M) { func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*check.Absolute, args ...string) (c *container.Container) { msg := message.New(nil) + msg.SwapVerbose(testing.Verbose()) c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...) c.Env = append(c.Env, envDoCheck+"=1") c.Bind(check.MustAbs(os.Args[0]), absHelperInnerPath, 0) diff --git a/container/fhs/abs.go b/container/fhs/abs.go index af3588b..6bb7216 100644 --- a/container/fhs/abs.go +++ b/container/fhs/abs.go @@ -1,7 +1,7 @@ package fhs import ( - _ "unsafe" + _ "unsafe" // for go:linkname "hakurei.app/container/check" ) diff --git a/container/init.go b/container/init.go index fd513ef..592ef30 100644 --- a/container/init.go +++ b/container/init.go @@ -427,6 +427,16 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { } if w.wpid == cmd.Process.Pid { + // start timeout early + go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }() + + // close initial process files; this also keeps them alive + for _, f := range extraFiles { + if err := f.Close(); err != nil { + msg.Verbose(err.Error()) + } + } + switch { case w.wstatus.Exited(): r = w.wstatus.ExitStatus() @@ -440,8 +450,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) { r = 255 msg.Verbosef("initial process exited with status %#x", w.wstatus) } - - go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }() } case <-timeout: diff --git a/container/init_test.go b/container/init_test.go index 336c7c4..6217537 100644 --- a/container/init_test.go +++ b/container/init_test.go @@ -2070,6 +2070,8 @@ func TestInitEntrypoint(t *testing.T) { // magicWait4Signal as ret causes wait4 stub to unblock call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", os.Interrupt}, magicWait4Signal, stub.UniqueError(9)), call("printf", stub.ExpectArgs{"cannot forward cancellation: %v", []any{stub.UniqueError(9)}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), @@ -2168,6 +2170,8 @@ func TestInitEntrypoint(t *testing.T) { // magicWait4Signal as ret causes wait4 stub to unblock call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", syscall.SIGQUIT}, magicWait4Signal, stub.UniqueError(0xfe)), call("printf", stub.ExpectArgs{"cannot forward signal: %v", []any{stub.UniqueError(0xfe)}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", []any(nil)}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), @@ -2353,6 +2357,8 @@ func TestInitEntrypoint(t *testing.T) { call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("printf", stub.ExpectArgs{"timeout exceeded waiting for lingering processes", ([]any)(nil)}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), @@ -2446,6 +2452,8 @@ func TestInitEntrypoint(t *testing.T) { call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), call("exit", stub.ExpectArgs{0xce}, nil, nil), @@ -2582,6 +2590,8 @@ func TestInitEntrypoint(t *testing.T) { call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with code %d", []any{1}}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), call("exit", stub.ExpectArgs{1}, nil, nil), @@ -2722,6 +2732,9 @@ func TestInitEntrypoint(t *testing.T) { call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil), call("New", stub.ExpectArgs{}, nil, nil), call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), + call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil), call("verbosef", stub.ExpectArgs{"initial process exited with status %#x", []any{syscall.WaitStatus(0xfade007f)}}, nil, nil), call("beforeExit", stub.ExpectArgs{}, nil, nil), call("exit", stub.ExpectArgs{0xff}, nil, nil), diff --git a/container/stub/exit_test.go b/container/stub/exit_test.go index 5d85075..e4c4f96 100644 --- a/container/stub/exit_test.go +++ b/container/stub/exit_test.go @@ -2,7 +2,7 @@ package stub_test import ( "testing" - _ "unsafe" + _ "unsafe" // for go:linkname "hakurei.app/container/stub" )