Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 651cdf9ccb | |||
| 68ff0a2ba6 | 
| @ -25,6 +25,9 @@ const ( | ||||
| 	// CancelSignal is the signal expected by container init on context cancel. | ||||
| 	// A custom [Container.Cancel] function must eventually deliver this signal. | ||||
| 	CancelSignal = SIGUSR2 | ||||
| 
 | ||||
| 	// Timeout for writing initParams to Container.setup. | ||||
| 	initSetupTimeout = 5 * time.Second | ||||
| ) | ||||
| 
 | ||||
| type ( | ||||
| @ -37,8 +40,8 @@ type ( | ||||
| 		// with behaviour identical to its [exec.Cmd] counterpart. | ||||
| 		ExtraFiles []*os.File | ||||
| 
 | ||||
| 		// param encoder for shim and init | ||||
| 		setup *gob.Encoder | ||||
| 		// param pipe for shim and init | ||||
| 		setup *os.File | ||||
| 		// cancels cmd | ||||
| 		cancel context.CancelFunc | ||||
| 		// closed after Wait returns | ||||
| @ -228,10 +231,10 @@ func (p *Container) Start() error { | ||||
| 	} | ||||
| 
 | ||||
| 	// place setup pipe before user supplied extra files, this is later restored by init | ||||
| 	if fd, e, err := Setup(&p.cmd.ExtraFiles); err != nil { | ||||
| 	if fd, f, err := Setup(&p.cmd.ExtraFiles); err != nil { | ||||
| 		return &StartError{true, "set up params stream", err, false, false} | ||||
| 	} else { | ||||
| 		p.setup = e | ||||
| 		p.setup = f | ||||
| 		p.cmd.Env = []string{setupEnv + "=" + strconv.Itoa(fd)} | ||||
| 	} | ||||
| 	p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, p.ExtraFiles...) | ||||
| @ -310,6 +313,9 @@ func (p *Container) Serve() error { | ||||
| 
 | ||||
| 	setup := p.setup | ||||
| 	p.setup = nil | ||||
| 	if err := setup.SetDeadline(time.Now().Add(initSetupTimeout)); err != nil { | ||||
| 		return &StartError{true, "set init pipe deadline", err, false, true} | ||||
| 	} | ||||
| 
 | ||||
| 	if p.Path == nil { | ||||
| 		p.cancel() | ||||
| @ -324,15 +330,14 @@ func (p *Container) Serve() error { | ||||
| 		p.SeccompRules = make([]seccomp.NativeRule, 0) | ||||
| 	} | ||||
| 
 | ||||
| 	err := setup.Encode( | ||||
| 		&initParams{ | ||||
| 			p.Params, | ||||
| 			Getuid(), | ||||
| 			Getgid(), | ||||
| 			len(p.ExtraFiles), | ||||
| 			p.msg.IsVerbose(), | ||||
| 		}, | ||||
| 	) | ||||
| 	err := gob.NewEncoder(setup).Encode(&initParams{ | ||||
| 		p.Params, | ||||
| 		Getuid(), | ||||
| 		Getgid(), | ||||
| 		len(p.ExtraFiles), | ||||
| 		p.msg.IsVerbose(), | ||||
| 	}) | ||||
| 	_ = setup.Close() | ||||
| 	if err != nil { | ||||
| 		p.cancel() | ||||
| 	} | ||||
|  | ||||
| @ -9,13 +9,13 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| // Setup appends the read end of a pipe for setup params transmission and returns its fd. | ||||
| func Setup(extraFiles *[]*os.File) (int, *gob.Encoder, error) { | ||||
| 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, gob.NewEncoder(w), nil | ||||
| 		return fd, w, nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package container_test | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/gob" | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"slices" | ||||
| @ -59,12 +60,16 @@ func TestSetupReceive(t *testing.T) { | ||||
| 
 | ||||
| 			encoderDone := make(chan error, 1) | ||||
| 			extraFiles := make([]*os.File, 0, 1) | ||||
| 			if fd, encoder, err := container.Setup(&extraFiles); err != nil { | ||||
| 			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 { | ||||
| 				go func() { encoderDone <- encoder.Encode(payload) }() | ||||
| 				if err = f.SetDeadline(deadline); err != nil { | ||||
| 					t.Fatal(err.Error()) | ||||
| 				} | ||||
| 				go func() { encoderDone <- gob.NewEncoder(f).Encode(payload) }() | ||||
| 			} | ||||
| 
 | ||||
| 			if len(extraFiles) != 1 { | ||||
|  | ||||
| @ -6,7 +6,6 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/user" | ||||
| 	"sync/atomic" | ||||
| 
 | ||||
| 	"hakurei.app/hst" | ||||
| 	"hakurei.app/message" | ||||
| @ -26,12 +25,9 @@ type outcome struct { | ||||
| 	sys *system.I | ||||
| 	// Transmitted to shim. Populated during finalise. | ||||
| 	state *outcomeState | ||||
| 	// Kept for saving to [state]. | ||||
| 	// Retained for registering current instance. | ||||
| 	config *hst.Config | ||||
| 
 | ||||
| 	// Whether the current process is in outcome.main. | ||||
| 	active atomic.Bool | ||||
| 
 | ||||
| 	ctx context.Context | ||||
| 	syscallDispatcher | ||||
| } | ||||
|  | ||||
| @ -20,8 +20,12 @@ import ( | ||||
| 	"hakurei.app/system" | ||||
| ) | ||||
| 
 | ||||
| // Duration to wait for shim to exit on top of container WaitDelay. | ||||
| const shimWaitTimeout = 5 * time.Second | ||||
| const ( | ||||
| 	// Duration to wait for shim to exit on top of container WaitDelay. | ||||
| 	shimWaitTimeout = 5 * time.Second | ||||
| 	// Timeout for writing outcomeState to the shim setup pipe. | ||||
| 	shimSetupTimeout = 5 * time.Second | ||||
| ) | ||||
| 
 | ||||
| // mainState holds persistent state bound to outcome.main. | ||||
| type mainState struct { | ||||
| @ -202,10 +206,6 @@ func (ms mainState) fatal(fallback string, ferr error) { | ||||
| 
 | ||||
| // main carries out outcome and terminates. main does not return. | ||||
| func (k *outcome) main(msg message.Msg) { | ||||
| 	if !k.active.CompareAndSwap(false, true) { | ||||
| 		panic("outcome: attempted to run twice") | ||||
| 	} | ||||
| 
 | ||||
| 	if k.ctx == nil || k.sys == nil || k.state == nil { | ||||
| 		panic("outcome: did not finalise") | ||||
| 	} | ||||
| @ -214,7 +214,7 @@ func (k *outcome) main(msg message.Msg) { | ||||
| 	hsuPath := internal.MustHsuPath() | ||||
| 
 | ||||
| 	// ms.beforeExit required beyond this point | ||||
| 	ms := &mainState{Msg: msg, k: k} | ||||
| 	ms := mainState{Msg: msg, k: k} | ||||
| 
 | ||||
| 	if err := k.sys.Commit(); err != nil { | ||||
| 		ms.fatal("cannot commit system setup:", err) | ||||
| @ -232,11 +232,12 @@ func (k *outcome) main(msg message.Msg) { | ||||
| 	// shim runs in the same session as monitor; see shim.go for behaviour | ||||
| 	ms.cmd.Cancel = func() error { return ms.cmd.Process.Signal(syscall.SIGCONT) } | ||||
| 
 | ||||
| 	var e *gob.Encoder | ||||
| 	if fd, encoder, err := container.Setup(&ms.cmd.ExtraFiles); err != nil { | ||||
| 	var shimPipe *os.File | ||||
| 	if fd, w, err := container.Setup(&ms.cmd.ExtraFiles); err != nil { | ||||
| 		ms.fatal("cannot create shim setup pipe:", err) | ||||
| 		panic("unreachable") | ||||
| 	} else { | ||||
| 		e = encoder | ||||
| 		shimPipe = w | ||||
| 		ms.cmd.Env = []string{ | ||||
| 			// passed through to shim by hsu | ||||
| 			shimEnv + "=" + strconv.Itoa(fd), | ||||
| @ -262,23 +263,14 @@ func (k *outcome) main(msg message.Msg) { | ||||
| 	go func() { ms.cmdWait <- ms.cmd.Wait(); cancel() }() | ||||
| 	ms.Time = &startTime | ||||
| 
 | ||||
| 	// unfortunately the I/O here cannot be directly canceled; | ||||
| 	// the cancellation path leads to fatal in this case so that is fine | ||||
| 	select { | ||||
| 	case err := <-func() (setupErr chan error) { | ||||
| 		setupErr = make(chan error, 1) | ||||
| 		go func() { setupErr <- e.Encode(k.state) }() | ||||
| 		return | ||||
| 	}(): | ||||
| 		if err != nil { | ||||
| 			msg.Resume() | ||||
| 			ms.fatal("cannot transmit shim config:", err) | ||||
| 		} | ||||
| 
 | ||||
| 	case <-ctx.Done(): | ||||
| 		msg.Resume() | ||||
| 		ms.fatal("shim context canceled:", newWithMessageError("shim setup canceled", ctx.Err())) | ||||
| 	if err := shimPipe.SetDeadline(time.Now().Add(shimSetupTimeout)); err != nil { | ||||
| 		msg.Verbose(err.Error()) | ||||
| 	} | ||||
| 	if err := gob.NewEncoder(shimPipe).Encode(k.state); err != nil { | ||||
| 		msg.Resume() | ||||
| 		ms.fatal("cannot transmit shim config:", err) | ||||
| 	} | ||||
| 	_ = shimPipe.Close() | ||||
| 
 | ||||
| 	// shim accepted setup payload, create process state | ||||
| 	if ok, err := ms.store.Do(k.state.identity.unwrap(), func(c store.Cursor) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user