internal/app: remove seal interface
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Test / Create distribution (push) Successful in 34s
				
			
		
			
				
	
				Test / Sandbox (push) Successful in 2m1s
				
			
		
			
				
	
				Test / Hakurei (push) Successful in 3m13s
				
			
		
			
				
	
				Test / Hpkg (push) Successful in 3m55s
				
			
		
			
				
	
				Test / Sandbox (race detector) (push) Successful in 4m33s
				
			
		
			
				
	
				Test / Hakurei (race detector) (push) Successful in 5m19s
				
			
		
			
				
	
				Test / Flake checks (push) Successful in 1m36s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Test / Create distribution (push) Successful in 34s
				
			Test / Sandbox (push) Successful in 2m1s
				
			Test / Hakurei (push) Successful in 3m13s
				
			Test / Hpkg (push) Successful in 3m55s
				
			Test / Sandbox (race detector) (push) Successful in 4m33s
				
			Test / Hakurei (race detector) (push) Successful in 5m19s
				
			Test / Flake checks (push) Successful in 1m36s
				
			This further cleans up the package for the restructure. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									d0b6852cd7
								
							
						
					
					
						commit
						b14690aa77
					
				| @ -2,35 +2,81 @@ | ||||
| package app | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"hakurei.app/hst" | ||||
| 	"hakurei.app/internal/app/state" | ||||
| 	"hakurei.app/internal/sys" | ||||
| ) | ||||
| 
 | ||||
| type SealedApp interface { | ||||
| 	// Run commits sealed system setup and starts the app process. | ||||
| 	Run(rs *RunState) error | ||||
| func New(ctx context.Context, os sys.State) (*App, error) { | ||||
| 	a := new(App) | ||||
| 	a.sys = os | ||||
| 	a.ctx = ctx | ||||
| 
 | ||||
| 	id := new(state.ID) | ||||
| 	err := state.NewAppID(id) | ||||
| 	a.id = newID(id) | ||||
| 
 | ||||
| 	return a, err | ||||
| } | ||||
| 
 | ||||
| // RunState stores the outcome of a call to [SealedApp.Run]. | ||||
| type RunState struct { | ||||
| 	// Time is the exact point in time where the process was created. | ||||
| 	// Location must be set to UTC. | ||||
| 	// | ||||
| 	// Time is nil if no process was ever created. | ||||
| 	Time *time.Time | ||||
| 	// RevertErr is stored by the deferred revert call. | ||||
| 	RevertErr error | ||||
| 	// WaitErr is the generic error value created by the standard library. | ||||
| 	WaitErr error | ||||
| 
 | ||||
| 	syscall.WaitStatus | ||||
| } | ||||
| 
 | ||||
| // SetStart stores the current time in [RunState] once. | ||||
| func (rs *RunState) SetStart() { | ||||
| 	if rs.Time != nil { | ||||
| 		panic("attempted to store time twice") | ||||
| func MustNew(ctx context.Context, os sys.State) *App { | ||||
| 	a, err := New(ctx, os) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("cannot create app: %v", err) | ||||
| 	} | ||||
| 	now := time.Now().UTC() | ||||
| 	rs.Time = &now | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| type App struct { | ||||
| 	outcome *Outcome | ||||
| 
 | ||||
| 	id  *stringPair[state.ID] | ||||
| 	sys sys.State | ||||
| 	ctx context.Context | ||||
| 	mu  sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| // ID returns a copy of [state.ID] held by App. | ||||
| func (a *App) ID() state.ID { a.mu.RLock(); defer a.mu.RUnlock(); return a.id.unwrap() } | ||||
| 
 | ||||
| func (a *App) String() string { | ||||
| 	if a == nil { | ||||
| 		return "(invalid app)" | ||||
| 	} | ||||
| 
 | ||||
| 	a.mu.RLock() | ||||
| 	defer a.mu.RUnlock() | ||||
| 
 | ||||
| 	if a.outcome != nil { | ||||
| 		if a.outcome.user.uid == nil { | ||||
| 			return fmt.Sprintf("(sealed app %s with invalid uid)", a.id) | ||||
| 		} | ||||
| 		return fmt.Sprintf("(sealed app %s as uid %s)", a.id, a.outcome.user.uid) | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf("(unsealed app %s)", a.id) | ||||
| } | ||||
| 
 | ||||
| // Seal determines the outcome of [hst.Config] as a [SealedApp]. | ||||
| // Values stored in and referred to by [hst.Config] might be overwritten and must not be used again. | ||||
| func (a *App) Seal(config *hst.Config) (*Outcome, error) { | ||||
| 	a.mu.Lock() | ||||
| 	defer a.mu.Unlock() | ||||
| 
 | ||||
| 	if a.outcome != nil { | ||||
| 		panic("app sealed twice") | ||||
| 	} | ||||
| 
 | ||||
| 	seal := new(Outcome) | ||||
| 	seal.id = a.id | ||||
| 	err := seal.finalise(a.ctx, a.sys, config) | ||||
| 	if err == nil { | ||||
| 		a.outcome = seal | ||||
| 	} | ||||
| 	return seal, err | ||||
| } | ||||
|  | ||||
| @ -1,81 +0,0 @@ | ||||
| package app | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"hakurei.app/hst" | ||||
| 	"hakurei.app/internal/app/state" | ||||
| 	"hakurei.app/internal/sys" | ||||
| ) | ||||
| 
 | ||||
| func New(ctx context.Context, os sys.State) (*App, error) { | ||||
| 	a := new(App) | ||||
| 	a.sys = os | ||||
| 	a.ctx = ctx | ||||
| 
 | ||||
| 	id := new(state.ID) | ||||
| 	err := state.NewAppID(id) | ||||
| 	a.id = newID(id) | ||||
| 
 | ||||
| 	return a, err | ||||
| } | ||||
| 
 | ||||
| func MustNew(ctx context.Context, os sys.State) *App { | ||||
| 	a, err := New(ctx, os) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("cannot create app: %v", err) | ||||
| 	} | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| type App struct { | ||||
| 	id  *stringPair[state.ID] | ||||
| 	sys sys.State | ||||
| 	ctx context.Context | ||||
| 
 | ||||
| 	*outcome | ||||
| 	mu sync.RWMutex | ||||
| } | ||||
| 
 | ||||
| // ID returns a copy of [state.ID] held by App. | ||||
| func (a *App) ID() state.ID { a.mu.RLock(); defer a.mu.RUnlock(); return a.id.unwrap() } | ||||
| 
 | ||||
| func (a *App) String() string { | ||||
| 	if a == nil { | ||||
| 		return "(invalid app)" | ||||
| 	} | ||||
| 
 | ||||
| 	a.mu.RLock() | ||||
| 	defer a.mu.RUnlock() | ||||
| 
 | ||||
| 	if a.outcome != nil { | ||||
| 		if a.outcome.user.uid == nil { | ||||
| 			return fmt.Sprintf("(sealed app %s with invalid uid)", a.id) | ||||
| 		} | ||||
| 		return fmt.Sprintf("(sealed app %s as uid %s)", a.id, a.outcome.user.uid) | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Sprintf("(unsealed app %s)", a.id) | ||||
| } | ||||
| 
 | ||||
| // Seal determines the outcome of [hst.Config] as a [SealedApp]. | ||||
| // Values stored in and referred to by [hst.Config] might be overwritten and must not be used again. | ||||
| func (a *App) Seal(config *hst.Config) (SealedApp, error) { | ||||
| 	a.mu.Lock() | ||||
| 	defer a.mu.Unlock() | ||||
| 
 | ||||
| 	if a.outcome != nil { | ||||
| 		panic("app sealed twice") | ||||
| 	} | ||||
| 
 | ||||
| 	seal := new(outcome) | ||||
| 	seal.id = a.id | ||||
| 	err := seal.finalise(a.ctx, a.sys, config) | ||||
| 	if err == nil { | ||||
| 		a.outcome = seal | ||||
| 	} | ||||
| 	return seal, err | ||||
| } | ||||
| @ -14,8 +14,7 @@ func NewWithID(id state.ID, os sys.State) *App { | ||||
| 	return a | ||||
| } | ||||
| 
 | ||||
| func AppIParams(a *App, sa SealedApp) (*system.I, *container.Params) { | ||||
| 	seal := sa.(*outcome) | ||||
| func AppIParams(a *App, seal *Outcome) (*system.I, *container.Params) { | ||||
| 	if a.outcome != seal || a.id != seal.id { | ||||
| 		panic("broken app/outcome link") | ||||
| 	} | ||||
| @ -21,9 +21,34 @@ import ( | ||||
| 
 | ||||
| const shimWaitTimeout = 5 * time.Second | ||||
| 
 | ||||
| func (seal *outcome) Run(rs *RunState) error { | ||||
| // RunState stores the outcome of a call to [Outcome.Run]. | ||||
| type RunState struct { | ||||
| 	// Time is the exact point in time where the process was created. | ||||
| 	// Location must be set to UTC. | ||||
| 	// | ||||
| 	// Time is nil if no process was ever created. | ||||
| 	Time *time.Time | ||||
| 	// RevertErr is stored by the deferred revert call. | ||||
| 	RevertErr error | ||||
| 	// WaitErr is the generic error value created by the standard library. | ||||
| 	WaitErr error | ||||
| 
 | ||||
| 	syscall.WaitStatus | ||||
| } | ||||
| 
 | ||||
| // setStart stores the current time in [RunState] once. | ||||
| func (rs *RunState) setStart() { | ||||
| 	if rs.Time != nil { | ||||
| 		panic("attempted to store time twice") | ||||
| 	} | ||||
| 	now := time.Now().UTC() | ||||
| 	rs.Time = &now | ||||
| } | ||||
| 
 | ||||
| // Run commits deferred system setup and starts the container. | ||||
| func (seal *Outcome) Run(rs *RunState) error { | ||||
| 	if !seal.f.CompareAndSwap(false, true) { | ||||
| 		// run does much more than just starting a process; calling it twice, even if the first call fails, will result | ||||
| 		// Run does much more than just starting a process; calling it twice, even if the first call fails, will result | ||||
| 		// in inconsistent state that is impossible to clean up; return here to limit damage and hopefully give the | ||||
| 		// other Run a chance to return | ||||
| 		return errors.New("outcome: attempted to run twice") | ||||
| @ -118,7 +143,7 @@ func (seal *outcome) Run(rs *RunState) error { | ||||
| 		return hlog.WrapErrSuffix(err, | ||||
| 			"cannot start setuid wrapper:") | ||||
| 	} | ||||
| 	rs.SetStart() | ||||
| 	rs.setStart() | ||||
| 
 | ||||
| 	// this prevents blocking forever on an early failure | ||||
| 	waitErr, setupErr := make(chan error, 1), make(chan error, 1) | ||||
| @ -173,10 +198,13 @@ func (seal *outcome) Run(rs *RunState) error { | ||||
| 			switch { | ||||
| 			case rs.Exited(): | ||||
| 				hlog.Verbosef("process %d exited with code %d", cmd.Process.Pid, rs.ExitStatus()) | ||||
| 
 | ||||
| 			case rs.CoreDump(): | ||||
| 				hlog.Verbosef("process %d dumped core", cmd.Process.Pid) | ||||
| 
 | ||||
| 			case rs.Signaled(): | ||||
| 				hlog.Verbosef("process %d got %s", cmd.Process.Pid, rs.Signal()) | ||||
| 
 | ||||
| 			default: | ||||
| 				hlog.Verbosef("process %d exited with status %#x", cmd.Process.Pid, rs.WaitStatus) | ||||
| 			} | ||||
| @ -58,8 +58,8 @@ var ( | ||||
| 	ErrPulseMode   = errors.New("unexpected pulse socket mode") | ||||
| ) | ||||
| 
 | ||||
| // outcome stores copies of various parts of [hst.Config] | ||||
| type outcome struct { | ||||
| // An Outcome is the runnable state of a hakurei container via [hst.Config]. | ||||
| type Outcome struct { | ||||
| 	// copied from initialising [app] | ||||
| 	id *stringPair[state.ID] | ||||
| 	// copied from [sys.State] | ||||
| @ -92,7 +92,7 @@ type shareHost struct { | ||||
| 	// process-specific directory in XDG_RUNTIME_DIR, empty if unused | ||||
| 	runtimeSharePath *container.Absolute | ||||
| 
 | ||||
| 	seal *outcome | ||||
| 	seal *Outcome | ||||
| 	sc   hst.Paths | ||||
| } | ||||
| 
 | ||||
| @ -145,7 +145,7 @@ type hsuUser struct { | ||||
| 	username string | ||||
| } | ||||
| 
 | ||||
| func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Config) error { | ||||
| func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Config) error { | ||||
| 	if seal.ctx != nil { | ||||
| 		panic("finalise called twice") | ||||
| 	} | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user