diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-02-19 12:33:51 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-02-19 12:33:51 +0900 |
| commit | 2978a6f046f52861a7ac749d1e414758caab8bd5 (patch) | |
| tree | eb5b7d129197320c145ca0a8e54b9cd1bcb98fcd /internal/app/start.go | |
| parent | dfd94675236844ebcec9ea51f2547ad4fd049357 (diff) | |
app: separate appSeal finalise method
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/start.go')
| -rw-r--r-- | internal/app/start.go | 268 |
1 files changed, 0 insertions, 268 deletions
diff --git a/internal/app/start.go b/internal/app/start.go deleted file mode 100644 index 4cbe6168..00000000 --- a/internal/app/start.go +++ /dev/null @@ -1,268 +0,0 @@ -package app - -import ( - "context" - "errors" - "fmt" - "log" - "os/exec" - "path/filepath" - "strings" - "time" - - "git.gensokyo.uk/security/fortify/fst" - "git.gensokyo.uk/security/fortify/helper" - "git.gensokyo.uk/security/fortify/internal/app/shim" - "git.gensokyo.uk/security/fortify/internal/fmsg" - "git.gensokyo.uk/security/fortify/internal/state" - "git.gensokyo.uk/security/fortify/system" -) - -const shimSetupTimeout = 5 * time.Second - -func (a *app) Run(ctx context.Context, rs *fst.RunState) error { - a.mu.Lock() - defer a.mu.Unlock() - - if rs == nil { - panic("attempted to pass nil state to run") - } - - // resolve exec paths - shimExec := [2]string{helper.BubblewrapName} - if len(a.appSeal.command) > 0 { - shimExec[1] = a.appSeal.command[0] - } - for i, n := range shimExec { - if len(n) == 0 { - continue - } - if filepath.Base(n) == n { - if s, err := exec.LookPath(n); err == nil { - shimExec[i] = s - } else { - return fmsg.WrapError(err, - fmt.Sprintf("executable file %q not found in $PATH", n)) - } - } - } - - // startup will go ahead, commit system setup - if err := a.appSeal.sys.Commit(ctx); err != nil { - return err - } - a.appSeal.needRevert = true - - // start shim via manager - a.shim = new(shim.Shim) - waitErr := make(chan error, 1) - if startTime, err := a.shim.Start( - a.appSeal.user.aid.String(), - a.appSeal.user.supp, - a.appSeal.bwrapSync, - ); err != nil { - return err - } else { - // shim process created - rs.Start = true - - shimSetupCtx, shimSetupCancel := context.WithDeadline(ctx, time.Now().Add(shimSetupTimeout)) - defer shimSetupCancel() - - // start waiting for shim - go func() { - waitErr <- a.shim.Unwrap().Wait() - // cancel shim setup in case shim died before receiving payload - shimSetupCancel() - }() - - // send payload - if err = a.shim.Serve(shimSetupCtx, &shim.Payload{ - Argv: a.appSeal.command, - Exec: shimExec, - Bwrap: a.appSeal.container, - Home: a.appSeal.user.data, - - Verbose: fmsg.Load(), - }); err != nil { - return err - } - - // shim accepted setup payload, create process state - sd := state.State{ - ID: a.id.unwrap(), - PID: a.shim.Unwrap().Process.Pid, - Time: *startTime, - } - - // register process state - var err0 = new(StateStoreError) - err0.Inner, err0.DoErr = a.appSeal.store.Do(a.appSeal.user.aid.unwrap(), func(c state.Cursor) { - err0.InnerErr = c.Save(&sd, a.appSeal.ct) - }) - a.appSeal.stateInStore = true - if err = err0.equiv("cannot save process state:"); err != nil { - return err - } - } - - select { - // wait for process and resolve exit code - case err := <-waitErr: - if err != nil { - var exitError *exec.ExitError - if !errors.As(err, &exitError) { - // should be unreachable - rs.WaitErr = err - } - - // store non-zero return code - rs.ExitCode = exitError.ExitCode() - } else { - rs.ExitCode = a.shim.Unwrap().ProcessState.ExitCode() - } - if fmsg.Load() { - fmsg.Verbosef("process %d exited with exit code %d", a.shim.Unwrap().Process.Pid, rs.ExitCode) - } - - // this is reached when a fault makes an already running shim impossible to continue execution - // however a kill signal could not be delivered (should actually always happen like that since fsu) - // the effects of this is similar to the alternative exit path and ensures shim death - case err := <-a.shim.WaitFallback(): - rs.ExitCode = 255 - log.Printf("cannot terminate shim on faulted setup: %v", err) - - // alternative exit path relying on shim behaviour on monitor process exit - case <-ctx.Done(): - fmsg.Verbose("alternative exit path selected") - } - - // child process exited, resume output - fmsg.Resume() - - // print queued up dbus messages - if a.appSeal.dbusMsg != nil { - a.appSeal.dbusMsg() - } - - // update store and revert app setup transaction - e := new(StateStoreError) - e.Inner, e.DoErr = a.appSeal.store.Do(a.appSeal.user.aid.unwrap(), func(b state.Cursor) { - e.InnerErr = func() error { - // destroy defunct state entry - if cmd := a.shim.Unwrap(); cmd != nil && a.appSeal.stateInStore { - if err := b.Destroy(a.id.unwrap()); err != nil { - return err - } - } - - // enablements of remaining launchers - rt, ec := new(system.Enablements), new(system.Criteria) - ec.Enablements = new(system.Enablements) - ec.Set(system.Process) - if states, err := b.Load(); err != nil { - return err - } else { - if l := len(states); l == 0 { - // cleanup globals as the final launcher - fmsg.Verbose("no other launchers active, will clean up globals") - ec.Set(system.User) - } else { - fmsg.Verbosef("found %d active launchers, cleaning up without globals", l) - } - - // accumulate capabilities of other launchers - for i, s := range states { - if s.Config != nil { - *rt |= s.Config.Confinement.Enablements - } else { - log.Printf("state entry %d does not contain config", i) - } - } - } - // invert accumulated enablements for cleanup - for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ { - if !rt.Has(i) { - ec.Set(i) - } - } - if fmsg.Load() { - labels := make([]string, 0, system.ELen+1) - for i := system.Enablement(0); i < system.Enablement(system.ELen+2); i++ { - if ec.Has(i) { - labels = append(labels, system.TypeString(i)) - } - } - if len(labels) > 0 { - fmsg.Verbose("reverting operations labelled", strings.Join(labels, ", ")) - } - } - - if a.appSeal.needRevert { - if err := a.appSeal.sys.Revert(ec); err != nil { - return err.(RevertCompoundError) - } - } - - return nil - }() - }) - - e.Err = a.appSeal.store.Close() - return e.equiv("error returned during cleanup:", e) -} - -// StateStoreError is returned for a failed state save -type StateStoreError struct { - // whether inner function was called - Inner bool - // error returned by state.Store Do method - DoErr error - // error returned by state.Backend Save method - InnerErr error - // any other errors needing to be tracked - Err error -} - -func (e *StateStoreError) equiv(a ...any) error { - if e.Inner && e.DoErr == nil && e.InnerErr == nil && e.Err == nil { - return nil - } else { - return fmsg.WrapErrorSuffix(e, a...) - } -} - -func (e *StateStoreError) Error() string { - if e.Inner && e.InnerErr != nil { - return e.InnerErr.Error() - } - - if e.DoErr != nil { - return e.DoErr.Error() - } - - if e.Err != nil { - return e.Err.Error() - } - - return "(nil)" -} - -func (e *StateStoreError) Unwrap() (errs []error) { - errs = make([]error, 0, 3) - if e.DoErr != nil { - errs = append(errs, e.DoErr) - } - if e.InnerErr != nil { - errs = append(errs, e.InnerErr) - } - if e.Err != nil { - errs = append(errs, e.Err) - } - return -} - -type RevertCompoundError interface { - Error() string - Unwrap() []error -} |
