aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-09-13 19:50:17 +0900
committerOphestra <cat@gensokyo.uk>2025-09-13 20:01:33 +0900
commit3f25c3f0af5631dcf46584ec122f7466fb8cfe37 (patch)
tree6dbe98c080f90200c2192e416e5be319af5ae5dc /container
parente271fa77aa72d3e9b937d77a9151157b9caeadf1 (diff)
container: initialise cmd early
This allows use of more cmd methods. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/container.go64
1 files changed, 52 insertions, 12 deletions
diff --git a/container/container.go b/container/container.go
index dee4a44d..f49faa89 100644
--- a/container/container.go
+++ b/container/container.go
@@ -64,7 +64,7 @@ type (
Args []string
// Deliver SIGINT to the initial process on context cancellation.
ForwardCancel bool
- // time to wait for linger processes after death of initial process
+ // Time to wait for processes lingering after the initial process terminates.
AdoptWaitDelay time.Duration
// Mapped Uid in user namespace.
@@ -152,16 +152,14 @@ func (e *StartError) Message() string {
// Start starts the container init. The init process blocks until Serve is called.
func (p *Container) Start() error {
- if p.cmd != nil {
- return errors.New("container: already started")
+ if p == nil || p.cmd == nil ||
+ p.Ops == nil || len(*p.Ops) == 0 {
+ return errors.New("container: starting an invalid container")
}
- if p.Ops == nil || len(*p.Ops) == 0 {
- return errors.New("container: starting an empty container")
+ if p.cmd.Process != nil {
+ return errors.New("container: already started")
}
- ctx, cancel := context.WithCancel(p.ctx)
- p.cancel = cancel
-
// map to overflow id to work around ownership checks
if p.Uid < 1 {
p.Uid = OverflowUid()
@@ -182,9 +180,17 @@ func (p *Container) Start() error {
p.AdoptWaitDelay = 0
}
- p.cmd = exec.CommandContext(ctx, MustExecutable())
+ if p.cmd.Stdin == nil {
+ p.cmd.Stdin = p.Stdin
+ }
+ if p.cmd.Stdout == nil {
+ p.cmd.Stdout = p.Stdout
+ }
+ if p.cmd.Stderr == nil {
+ p.cmd.Stderr = p.Stderr
+ }
+
p.cmd.Args = []string{initName}
- p.cmd.Stdin, p.cmd.Stdout, p.cmd.Stderr = p.Stdin, p.Stdout, p.Stderr
p.cmd.WaitDelay = p.WaitDelay
if p.Cancel != nil {
p.cmd.Cancel = func() error { return p.Cancel(p.cmd) }
@@ -330,7 +336,7 @@ func (p *Container) Serve() error {
// Wait waits for the container init process to exit and releases any resources associated with the [Container].
func (p *Container) Wait() error {
- if p.cmd == nil {
+ if p.cmd == nil || p.cmd.Process == nil {
return EINVAL
}
@@ -342,6 +348,36 @@ func (p *Container) Wait() error {
return err
}
+// StdinPipe calls the [exec.Cmd] method with the same name.
+func (p *Container) StdinPipe() (w io.WriteCloser, err error) {
+ if p.Stdin != nil {
+ return nil, errors.New("container: Stdin already set")
+ }
+ w, err = p.cmd.StdinPipe()
+ p.Stdin = p.cmd.Stdin
+ return
+}
+
+// StdoutPipe calls the [exec.Cmd] method with the same name.
+func (p *Container) StdoutPipe() (r io.ReadCloser, err error) {
+ if p.Stdout != nil {
+ return nil, errors.New("container: Stdout already set")
+ }
+ r, err = p.cmd.StdoutPipe()
+ p.Stdout = p.cmd.Stdout
+ return
+}
+
+// StderrPipe calls the [exec.Cmd] method with the same name.
+func (p *Container) StderrPipe() (r io.ReadCloser, err error) {
+ if p.Stderr != nil {
+ return nil, errors.New("container: Stderr already set")
+ }
+ r, err = p.cmd.StderrPipe()
+ p.Stderr = p.cmd.Stderr
+ return
+}
+
func (p *Container) String() string {
return fmt.Sprintf("argv: %q, filter: %v, rules: %d, flags: %#x, presets: %#x",
p.Args, !p.SeccompDisable, len(p.SeccompRules), int(p.SeccompFlags), int(p.SeccompPresets))
@@ -357,7 +393,11 @@ func (p *Container) ProcessState() *os.ProcessState {
// New returns the address to a new instance of [Container] that requires further initialisation before use.
func New(ctx context.Context) *Container {
- return &Container{ctx: ctx, Params: Params{Ops: new(Ops)}}
+ p := &Container{ctx: ctx, Params: Params{Ops: new(Ops)}}
+ c, cancel := context.WithCancel(ctx)
+ p.cancel = cancel
+ p.cmd = exec.CommandContext(c, MustExecutable())
+ return p
}
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.