aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-04-07 15:50:59 +0900
committerOphestra <cat@gensokyo.uk>2026-04-07 16:05:33 +0900
commit062edb3487c1cc116cae78bf77420b65c43517d1 (patch)
treeac91267721d7a288ea301b060b0110aa5d3b9b9a /container
parente4355279a1624833e36455a1a85788dbe091464e (diff)
container: remove setup pipe helper
The API forces use of finalizer to close the read end of the setup pipe, which is no longer considered acceptable. Exporting this as part of package container also imposes unnecessary maintenance burden. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/container.go42
-rw-r--r--container/params.go11
-rw-r--r--container/params_test.go72
3 files changed, 27 insertions, 98 deletions
diff --git a/container/container.go b/container/container.go
index 480452cd..d45d7a97 100644
--- a/container/container.go
+++ b/container/container.go
@@ -53,7 +53,7 @@ type (
ExtraFiles []*os.File
// Write end of a pipe connected to the init to deliver [Params].
- setup *os.File
+ setup [2]*os.File
// Cancels the context passed to the underlying cmd.
cancel context.CancelFunc
// Closed after Wait returns. Keeps the spawning thread alive.
@@ -287,14 +287,16 @@ func (p *Container) Start() error {
}
// place setup pipe before user supplied extra files, this is later restored by init
- if fd, f, err := Setup(&p.cmd.ExtraFiles); err != nil {
+ if r, w, err := os.Pipe(); err != nil {
return &StartError{
Fatal: true,
Step: "set up params stream",
Err: err,
}
} else {
- p.setup = f
+ fd := 3 + len(p.cmd.ExtraFiles)
+ p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, r)
+ p.setup[0], p.setup[1] = r, w
p.cmd.Env = []string{setupEnv + "=" + strconv.Itoa(fd)}
}
p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, p.ExtraFiles...)
@@ -428,14 +430,30 @@ func (p *Container) Start() error {
// Serve serves [Container.Params] to the container init.
//
// Serve must only be called once.
-func (p *Container) Serve() error {
- if p.setup == nil {
+func (p *Container) Serve() (err error) {
+ if p.setup[0] == nil || p.setup[1] == nil {
panic("invalid serve")
}
+ defer func() {
+ if closeErr := p.setup[1].Close(); err == nil {
+ err = closeErr
+ }
+
+ if err != nil {
+ p.cancel()
+ }
+ p.setup[0], p.setup[1] = nil, nil
+ }()
+ if err = p.setup[0].Close(); err != nil {
+ return &StartError{
+ Fatal: true,
+ Step: "close read end of init pipe",
+ Err: err,
+ Passthrough: true,
+ }
+ }
- setup := p.setup
- p.setup = nil
- if err := setup.SetDeadline(time.Now().Add(initSetupTimeout)); err != nil {
+ if err = p.setup[1].SetDeadline(time.Now().Add(initSetupTimeout)); err != nil {
return &StartError{
Fatal: true,
Step: "set init pipe deadline",
@@ -445,7 +463,6 @@ func (p *Container) Serve() error {
}
if p.Path == nil {
- p.cancel()
return &StartError{
Step: "invalid executable pathname",
Err: EINVAL,
@@ -461,18 +478,13 @@ func (p *Container) Serve() error {
p.SeccompRules = make([]std.NativeRule, 0)
}
- err := gob.NewEncoder(setup).Encode(&initParams{
+ return gob.NewEncoder(p.setup[1]).Encode(&initParams{
p.Params,
Getuid(),
Getgid(),
len(p.ExtraFiles),
p.msg.IsVerbose(),
})
- _ = setup.Close()
- if err != nil {
- p.cancel()
- }
- return err
}
// Wait blocks until the container init process to exit and releases any
diff --git a/container/params.go b/container/params.go
index 46c3b9b2..e6cd26f8 100644
--- a/container/params.go
+++ b/container/params.go
@@ -8,17 +8,6 @@ import (
"syscall"
)
-// Setup appends the read end of a pipe for setup params transmission and returns its fd.
-func Setup(extraFiles *[]*os.File) (int, *os.File, error) {
- if r, w, err := os.Pipe(); err != nil {
- return -1, nil, err
- } else {
- fd := 3 + len(*extraFiles)
- *extraFiles = append(*extraFiles, r)
- return fd, w, nil
- }
-}
-
var (
ErrReceiveEnv = errors.New("environment variable not set")
)
diff --git a/container/params_test.go b/container/params_test.go
index 11d08850..4d2f84f6 100644
--- a/container/params_test.go
+++ b/container/params_test.go
@@ -1,10 +1,8 @@
package container_test
import (
- "encoding/gob"
"errors"
"os"
- "slices"
"strconv"
"syscall"
"testing"
@@ -52,74 +50,4 @@ func TestSetupReceive(t *testing.T) {
t.Errorf("Receive: error = %v, want %v", err, syscall.EDOM)
}
})
-
- t.Run("setup receive", func(t *testing.T) {
- check := func(t *testing.T, useNilFdp bool) {
- const key = "TEST_SETUP_RECEIVE"
- payload := []uint64{syscall.MS_MGC_VAL, syscall.MS_MGC_MSK, syscall.MS_ASYNC, syscall.MS_ACTIVE}
-
- encoderDone := make(chan error, 1)
- extraFiles := make([]*os.File, 0, 1)
- deadline, _ := t.Deadline()
- if fd, f, err := container.Setup(&extraFiles); err != nil {
- t.Fatalf("Setup: error = %v", err)
- } else if fd != 3 {
- t.Fatalf("Setup: fd = %d, want 3", fd)
- } else {
- if err = f.SetDeadline(deadline); err != nil {
- t.Fatal(err.Error())
- }
- go func() { encoderDone <- gob.NewEncoder(f).Encode(payload) }()
- }
-
- if len(extraFiles) != 1 {
- t.Fatalf("extraFiles: len = %v, want 1", len(extraFiles))
- }
-
- var dupFd int
- if fd, err := syscall.Dup(int(extraFiles[0].Fd())); err != nil {
- t.Fatalf("Dup: error = %v", err)
- } else {
- syscall.CloseOnExec(fd)
- dupFd = fd
- t.Setenv(key, strconv.Itoa(fd))
- }
-
- var (
- gotPayload []uint64
- fdp *uintptr
- )
- if !useNilFdp {
- fdp = new(uintptr)
- }
- var closeFile func() error
- if f, err := container.Receive(key, &gotPayload, fdp); err != nil {
- t.Fatalf("Receive: error = %v", err)
- } else {
- closeFile = f
-
- if !slices.Equal(payload, gotPayload) {
- t.Errorf("Receive: %#v, want %#v", gotPayload, payload)
- }
- }
- if !useNilFdp {
- if int(*fdp) != dupFd {
- t.Errorf("Fd: %d, want %d", *fdp, dupFd)
- }
- }
-
- if err := <-encoderDone; err != nil {
- t.Errorf("Encode: error = %v", err)
- }
-
- if closeFile != nil {
- if err := closeFile(); err != nil {
- t.Errorf("Close: error = %v", err)
- }
- }
- }
-
- t.Run("fp", func(t *testing.T) { check(t, false) })
- t.Run("nil", func(t *testing.T) { check(t, true) })
- })
}