aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-08-28 01:07:51 +0900
committerOphestra <cat@gensokyo.uk>2025-08-28 01:07:51 +0900
commitb14690aa77b3d5f86c4d1b1965675d6d82789e32 (patch)
tree5765f705fd69dae7a65a0666451ac71a729a6aa8 /internal/app
parentd0b6852cd7db2bb1214c3267719d3ac108c0a53e (diff)
internal/app: remove seal interface
This further cleans up the package for the restructure. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app')
-rw-r--r--internal/app/app.go92
-rw-r--r--internal/app/app_linux.go81
-rw-r--r--internal/app/app_test.go (renamed from internal/app/app_linux_test.go)0
-rw-r--r--internal/app/container.go (renamed from internal/app/container_linux.go)0
-rw-r--r--internal/app/export_test.go (renamed from internal/app/export_linux_test.go)3
-rw-r--r--internal/app/process.go (renamed from internal/app/process_linux.go)34
-rw-r--r--internal/app/seal.go (renamed from internal/app/seal_linux.go)8
-rw-r--r--internal/app/shim.go (renamed from internal/app/shim_linux.go)0
8 files changed, 105 insertions, 113 deletions
diff --git a/internal/app/app.go b/internal/app/app.go
index d59b145d..17b4103d 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -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
+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
+}
- syscall.WaitStatus
+type App struct {
+ outcome *Outcome
+
+ id *stringPair[state.ID]
+ sys sys.State
+ ctx context.Context
+ mu sync.RWMutex
}
-// SetStart stores the current time in [RunState] once.
-func (rs *RunState) SetStart() {
- if rs.Time != nil {
- panic("attempted to store time twice")
+// 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
}
- now := time.Now().UTC()
- rs.Time = &now
+ return seal, err
}
diff --git a/internal/app/app_linux.go b/internal/app/app_linux.go
deleted file mode 100644
index eebfacdc..00000000
--- a/internal/app/app_linux.go
+++ /dev/null
@@ -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
-}
diff --git a/internal/app/app_linux_test.go b/internal/app/app_test.go
index 8b96cfba..8b96cfba 100644
--- a/internal/app/app_linux_test.go
+++ b/internal/app/app_test.go
diff --git a/internal/app/container_linux.go b/internal/app/container.go
index cdefe170..cdefe170 100644
--- a/internal/app/container_linux.go
+++ b/internal/app/container.go
diff --git a/internal/app/export_linux_test.go b/internal/app/export_test.go
index f7af527f..b7ee7f73 100644
--- a/internal/app/export_linux_test.go
+++ b/internal/app/export_test.go
@@ -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")
}
diff --git a/internal/app/process_linux.go b/internal/app/process.go
index c7da4746..44355699 100644
--- a/internal/app/process_linux.go
+++ b/internal/app/process.go
@@ -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)
}
diff --git a/internal/app/seal_linux.go b/internal/app/seal.go
index be18a2ae..d6a23f2f 100644
--- a/internal/app/seal_linux.go
+++ b/internal/app/seal.go
@@ -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")
}
diff --git a/internal/app/shim_linux.go b/internal/app/shim.go
index 33ede520..33ede520 100644
--- a/internal/app/shim_linux.go
+++ b/internal/app/shim.go