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 | package app | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"syscall" | 	"context" | ||||||
| 	"time" | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 	"sync" | ||||||
|  | 
 | ||||||
|  | 	"hakurei.app/hst" | ||||||
|  | 	"hakurei.app/internal/app/state" | ||||||
|  | 	"hakurei.app/internal/sys" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type SealedApp interface { | func New(ctx context.Context, os sys.State) (*App, error) { | ||||||
| 	// Run commits sealed system setup and starts the app process. | 	a := new(App) | ||||||
| 	Run(rs *RunState) error | 	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]. | func MustNew(ctx context.Context, os sys.State) *App { | ||||||
| type RunState struct { | 	a, err := New(ctx, os) | ||||||
| 	// Time is the exact point in time where the process was created. | 	if err != nil { | ||||||
| 	// Location must be set to UTC. | 		log.Fatalf("cannot create app: %v", err) | ||||||
| 	// |  | ||||||
| 	// 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() | 	return a | ||||||
| 	rs.Time = &now | } | ||||||
|  | 
 | ||||||
|  | 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 | 	return a | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func AppIParams(a *App, sa SealedApp) (*system.I, *container.Params) { | func AppIParams(a *App, seal *Outcome) (*system.I, *container.Params) { | ||||||
| 	seal := sa.(*outcome) |  | ||||||
| 	if a.outcome != seal || a.id != seal.id { | 	if a.outcome != seal || a.id != seal.id { | ||||||
| 		panic("broken app/outcome link") | 		panic("broken app/outcome link") | ||||||
| 	} | 	} | ||||||
| @ -21,9 +21,34 @@ import ( | |||||||
| 
 | 
 | ||||||
| const shimWaitTimeout = 5 * time.Second | 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) { | 	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 | 		// in inconsistent state that is impossible to clean up; return here to limit damage and hopefully give the | ||||||
| 		// other Run a chance to return | 		// other Run a chance to return | ||||||
| 		return errors.New("outcome: attempted to run twice") | 		return errors.New("outcome: attempted to run twice") | ||||||
| @ -118,7 +143,7 @@ func (seal *outcome) Run(rs *RunState) error { | |||||||
| 		return hlog.WrapErrSuffix(err, | 		return hlog.WrapErrSuffix(err, | ||||||
| 			"cannot start setuid wrapper:") | 			"cannot start setuid wrapper:") | ||||||
| 	} | 	} | ||||||
| 	rs.SetStart() | 	rs.setStart() | ||||||
| 
 | 
 | ||||||
| 	// this prevents blocking forever on an early failure | 	// this prevents blocking forever on an early failure | ||||||
| 	waitErr, setupErr := make(chan error, 1), make(chan error, 1) | 	waitErr, setupErr := make(chan error, 1), make(chan error, 1) | ||||||
| @ -173,10 +198,13 @@ func (seal *outcome) Run(rs *RunState) error { | |||||||
| 			switch { | 			switch { | ||||||
| 			case rs.Exited(): | 			case rs.Exited(): | ||||||
| 				hlog.Verbosef("process %d exited with code %d", cmd.Process.Pid, rs.ExitStatus()) | 				hlog.Verbosef("process %d exited with code %d", cmd.Process.Pid, rs.ExitStatus()) | ||||||
|  | 
 | ||||||
| 			case rs.CoreDump(): | 			case rs.CoreDump(): | ||||||
| 				hlog.Verbosef("process %d dumped core", cmd.Process.Pid) | 				hlog.Verbosef("process %d dumped core", cmd.Process.Pid) | ||||||
|  | 
 | ||||||
| 			case rs.Signaled(): | 			case rs.Signaled(): | ||||||
| 				hlog.Verbosef("process %d got %s", cmd.Process.Pid, rs.Signal()) | 				hlog.Verbosef("process %d got %s", cmd.Process.Pid, rs.Signal()) | ||||||
|  | 
 | ||||||
| 			default: | 			default: | ||||||
| 				hlog.Verbosef("process %d exited with status %#x", cmd.Process.Pid, rs.WaitStatus) | 				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") | 	ErrPulseMode   = errors.New("unexpected pulse socket mode") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // outcome stores copies of various parts of [hst.Config] | // An Outcome is the runnable state of a hakurei container via [hst.Config]. | ||||||
| type outcome struct { | type Outcome struct { | ||||||
| 	// copied from initialising [app] | 	// copied from initialising [app] | ||||||
| 	id *stringPair[state.ID] | 	id *stringPair[state.ID] | ||||||
| 	// copied from [sys.State] | 	// copied from [sys.State] | ||||||
| @ -92,7 +92,7 @@ type shareHost struct { | |||||||
| 	// process-specific directory in XDG_RUNTIME_DIR, empty if unused | 	// process-specific directory in XDG_RUNTIME_DIR, empty if unused | ||||||
| 	runtimeSharePath *container.Absolute | 	runtimeSharePath *container.Absolute | ||||||
| 
 | 
 | ||||||
| 	seal *outcome | 	seal *Outcome | ||||||
| 	sc   hst.Paths | 	sc   hst.Paths | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -145,7 +145,7 @@ type hsuUser struct { | |||||||
| 	username string | 	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 { | 	if seal.ctx != nil { | ||||||
| 		panic("finalise called twice") | 		panic("finalise called twice") | ||||||
| 	} | 	} | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user