aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/start.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-02-19 12:33:51 +0900
committerOphestra <cat@gensokyo.uk>2025-02-19 12:33:51 +0900
commit2978a6f046f52861a7ac749d1e414758caab8bd5 (patch)
treeeb5b7d129197320c145ca0a8e54b9cd1bcb98fcd /internal/app/start.go
parentdfd94675236844ebcec9ea51f2547ad4fd049357 (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.go268
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
-}