aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app
diff options
context:
space:
mode:
Diffstat (limited to 'internal/app')
-rw-r--r--internal/app/errors.go184
-rw-r--r--internal/app/process.go389
-rw-r--r--internal/app/seal.go44
-rw-r--r--internal/app/state/multi.go10
-rw-r--r--internal/app/state/state.go2
5 files changed, 271 insertions, 358 deletions
diff --git a/internal/app/errors.go b/internal/app/errors.go
deleted file mode 100644
index f7fa2b1c..00000000
--- a/internal/app/errors.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package app
-
-import (
- "errors"
- "log"
-
- "hakurei.app/container"
- "hakurei.app/hst"
- "hakurei.app/internal/hlog"
- "hakurei.app/internal/sys"
-)
-
-// PrintRunStateErr prints an error message via [log] if runErr is not nil, and returns an appropriate exit code.
-//
-// TODO(ophestra): remove this function once RunState has been replaced
-func PrintRunStateErr(rs *RunState, runErr error) (code int) {
- code = rs.ExitStatus()
-
- if runErr != nil {
- if rs.Time == nil {
- // no process has been created
- printMessageError("cannot start app:", runErr)
- } else {
- if m, ok := container.GetErrorMessage(runErr); !ok {
- // catch-all for unexpected errors
- log.Println("run returned error:", runErr)
- } else {
- var se *StateStoreError
- if !errors.As(runErr, &se) {
- // this could only be returned from a shim setup failure path
- log.Print(m)
- } else {
- // InnerErr is returned by c.Save(&sd, seal.ct), and are always unwrapped
- printMessageError("error returned during revert:",
- &hst.AppError{Step: "save process state", Err: se.InnerErr})
- }
- }
- }
-
- if code == 0 {
- code = 126
- }
- }
-
- if rs.RevertErr != nil {
- var stateStoreError *StateStoreError
- if !errors.As(rs.RevertErr, &stateStoreError) || stateStoreError == nil {
- printMessageError("cannot clean up:", rs.RevertErr)
- goto out
- }
-
- if stateStoreError.Errs != nil {
- if len(stateStoreError.Errs) == 2 { // storeErr.save(revertErr, store.Close())
- if stateStoreError.Errs[0] != nil { // revertErr is MessageError joined by errors.Join
- var joinedErrors interface {
- Unwrap() []error
- error
- }
- if !errors.As(stateStoreError.Errs[0], &joinedErrors) {
- printMessageError("cannot revert:", stateStoreError.Errs[0])
- } else {
- for _, err := range joinedErrors.Unwrap() {
- if err != nil {
- printMessageError("cannot revert:", err)
- }
- }
- }
- }
- if stateStoreError.Errs[1] != nil { // store.Close() is joined by errors.Join
- log.Printf("cannot close store: %v", stateStoreError.Errs[1])
- }
- } else {
- log.Printf("fault during cleanup: %v", errors.Join(stateStoreError.Errs...))
- }
- }
-
- if stateStoreError.OpErr != nil {
- log.Printf("blind revert due to store fault: %v", stateStoreError.OpErr)
- }
-
- if stateStoreError.DoErr != nil {
- printMessageError("state store operation unsuccessful:", stateStoreError.DoErr)
- }
-
- if stateStoreError.Inner && stateStoreError.InnerErr != nil {
- printMessageError("cannot destroy state entry:", stateStoreError.InnerErr)
- }
-
- out:
- if code == 0 {
- code = 128
- }
- }
- if rs.WaitErr != nil {
- hlog.Verbosef("wait: %v", rs.WaitErr)
- }
- return
-}
-
-// TODO(ophestra): this duplicates code in cmd/hakurei/command.go, keep this up to date until removal
-func printMessageError(fallback string, err error) {
- // this indicates the error message has already reached stderr, outside the current process's control;
- // this is only reached when hsu fails for any reason, as a second error message following hsu is confusing
- if errors.Is(err, sys.ErrHsuAccess) {
- hlog.Verbose("*"+fallback, err)
- return
- }
-
- m, ok := container.GetErrorMessage(err)
- if !ok {
- log.Println(fallback, err)
- return
- }
-
- log.Print(m)
-}
-
-// StateStoreError is returned for a failed state save.
-type StateStoreError struct {
- // whether inner function was called
- Inner bool
- // returned by the Save/Destroy method of [state.Cursor]
- InnerErr error
- // returned by the Do method of [state.Store]
- DoErr error
- // stores an arbitrary store operation error
- OpErr error
- // stores arbitrary errors
- Errs []error
-}
-
-// save saves arbitrary errors in [StateStoreError.Errs] once.
-func (e *StateStoreError) save(errs ...error) {
- if len(errs) == 0 || e.Errs != nil {
- panic("invalid call to save")
- }
- e.Errs = errs
-}
-
-// equiv returns an error that [StateStoreError] is equivalent to, including nil.
-func (e *StateStoreError) equiv(step string) error {
- if e.Inner && e.InnerErr == nil && e.DoErr == nil && e.OpErr == nil && errors.Join(e.Errs...) == nil {
- return nil
- } else {
- return &hst.AppError{Step: step, Err: e}
- }
-}
-
-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.OpErr != nil {
- return e.OpErr.Error()
- }
- if err := errors.Join(e.Errs...); err != nil {
- return err.Error()
- }
-
- // equiv nullifies e for values where this is reached
- panic("unreachable")
-}
-
-func (e *StateStoreError) Unwrap() (errs []error) {
- errs = make([]error, 0, 3+len(e.Errs))
- if e.InnerErr != nil {
- errs = append(errs, e.InnerErr)
- }
- if e.DoErr != nil {
- errs = append(errs, e.DoErr)
- }
- if e.OpErr != nil {
- errs = append(errs, e.OpErr)
- }
- for _, err := range e.Errs {
- if err != nil {
- errs = append(errs, err)
- }
- }
- return
-}
diff --git a/internal/app/process.go b/internal/app/process.go
index daa7832a..0ee123a1 100644
--- a/internal/app/process.go
+++ b/internal/app/process.go
@@ -13,7 +13,6 @@ import (
"time"
"hakurei.app/container"
- "hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog"
@@ -23,85 +22,166 @@ import (
// duration to wait for shim to exit, after container WaitDelay has elapsed.
const shimWaitTimeout = 5 * time.Second
-// ErrShimTimeout is returned when shim did not exit within shimWaitTimeout, after its WaitDelay has elapsed.
-// This is different from the container failing to terminate within its timeout period, as that is enforced
-// by the shim. This error is instead returned when there is a lockup in shim preventing it from completing.
-var ErrShimTimeout = errors.New("shim did not exit")
+// mainState holds persistent state bound to [Outcome.Main].
+type mainState struct {
+ // done is whether beforeExit has been called already.
+ done bool
-// 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
+ seal *Outcome
+ store state.Store
+ cancel context.CancelFunc
+ cmd *exec.Cmd
+ cmdWait chan error
+
+ uintptr
}
-// setStart stores the current time in [RunState] once.
-func (rs *RunState) setStart() {
- if rs.Time != nil {
- panic("attempted to store time twice")
+const (
+ // mainNeedsRevert indicates the call to Commit has succeeded.
+ mainNeedsRevert uintptr = 1 << iota
+ // mainNeedsDestroy indicates the instance state entry is present in the store.
+ mainNeedsDestroy
+)
+
+// beforeExit must be called immediately before a call to [os.Exit].
+func (ms mainState) beforeExit(isFault bool) {
+ if ms.done {
+ panic("attempting to call beforeExit twice")
}
- now := time.Now().UTC()
- rs.Time = &now
-}
+ ms.done = true
+ defer hlog.BeforeExit()
-// 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
- // 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")
+ if isFault && ms.cancel != nil {
+ ms.cancel()
}
- if rs == nil {
- panic("invalid state")
+ var hasErr bool
+ // updates hasErr but does not terminate
+ perror := func(err error, message string) {
+ hasErr = true
+ printMessageError("cannot "+message+":", err)
}
+ exitCode := 1
+ defer func() {
+ if hasErr {
+ os.Exit(exitCode)
+ }
+ }()
- // read comp value early to allow for early failure
- hsuPath := internal.MustHsuPath()
+ // this also handles wait for a non-fault termination
+ if ms.cmd != nil && ms.cmdWait != nil {
+ waitDone := make(chan struct{})
+ // TODO(ophestra): enforce this limit early so it does not have to be done twice
+ shimTimeoutCompensated := shimWaitTimeout
+ if ms.seal.waitDelay > MaxShimWaitDelay {
+ shimTimeoutCompensated += MaxShimWaitDelay
+ } else {
+ shimTimeoutCompensated += ms.seal.waitDelay
+ }
+ // this ties waitDone to ctx with the additional compensated timeout duration
+ go func() { <-ms.seal.ctx.Done(); time.Sleep(shimTimeoutCompensated); close(waitDone) }()
- if err := seal.sys.Commit(); err != nil {
- return err
+ select {
+ case err := <-ms.cmdWait:
+ wstatus, ok := ms.cmd.ProcessState.Sys().(syscall.WaitStatus)
+ if ok {
+ if v := wstatus.ExitStatus(); v != 0 {
+ hasErr = true
+ exitCode = v
+ }
+ }
+
+ if hlog.Load() {
+ if !ok {
+ if err != nil {
+ hlog.Verbosef("wait: %v", err)
+ }
+ } else {
+ switch {
+ case wstatus.Exited():
+ hlog.Verbosef("process %d exited with code %d", ms.cmd.Process.Pid, wstatus.ExitStatus())
+
+ case wstatus.CoreDump():
+ hlog.Verbosef("process %d dumped core", ms.cmd.Process.Pid)
+
+ case wstatus.Signaled():
+ hlog.Verbosef("process %d got %s", ms.cmd.Process.Pid, wstatus.Signal())
+
+ default:
+ hlog.Verbosef("process %d exited with status %#x", ms.cmd.Process.Pid, wstatus)
+ }
+ }
+ }
+
+ case <-waitDone:
+ hlog.Resume()
+ // this is only reachable when shim did not exit within shimWaitTimeout, after its WaitDelay has elapsed.
+ // This is different from the container failing to terminate within its timeout period, as that is enforced
+ // by the shim. This path is instead reached when there is a lockup in shim preventing it from completing.
+ log.Printf("process %d did not terminate", ms.cmd.Process.Pid)
+ }
+
+ hlog.Resume()
+ if ms.seal.sync != nil {
+ if err := ms.seal.sync.Close(); err != nil {
+ perror(err, "close wayland security context")
+ }
+ }
+ if ms.seal.dbusMsg != nil {
+ ms.seal.dbusMsg()
+ }
}
- store := state.NewMulti(seal.runDirPath.String())
- deferredStoreFunc := func(c state.Cursor) error { return nil } // noop until state in store
- defer func() {
- var revertErr error
- storeErr := new(StateStoreError)
- storeErr.Inner, storeErr.DoErr = store.Do(seal.user.identity.unwrap(), func(c state.Cursor) {
- revertErr = func() error {
- storeErr.InnerErr = deferredStoreFunc(c)
- var rt system.Enablement
+ if ms.uintptr&mainNeedsRevert != 0 {
+ if ok, err := ms.store.Do(ms.seal.user.identity.unwrap(), func(c state.Cursor) {
+ if ms.uintptr&mainNeedsDestroy != 0 {
+ if err := c.Destroy(ms.seal.id.unwrap()); err != nil {
+ perror(err, "destroy state entry")
+ }
+ }
+
+ var rt system.Enablement
+ if states, err := c.Load(); err != nil {
+ // it is impossible to continue from this point;
+ // revert per-process state here to limit damage
ec := system.Process
- if states, err := c.Load(); err != nil {
- // revert per-process state here to limit damage
- storeErr.OpErr = err
- return seal.sys.Revert((*system.Criteria)(&ec))
- } else {
- if l := len(states); l == 0 {
- ec |= system.User
+ if revertErr := ms.seal.sys.Revert((*system.Criteria)(&ec)); revertErr != nil {
+ var joinError interface {
+ Unwrap() []error
+ error
+ }
+ if !errors.As(revertErr, &joinError) || joinError == nil {
+ perror(revertErr, "revert system setup")
} else {
- hlog.Verbosef("found %d instances, cleaning up without user-scoped operations", l)
+ for _, v := range joinError.Unwrap() {
+ perror(v, "revert system setup step")
+ }
}
+ }
+ perror(err, "load instance states")
+ } else {
+ ec := system.Process
+ if l := len(states); l == 0 {
+ ec |= system.User
+ } else {
+ hlog.Verbosef("found %d instances, cleaning up without user-scoped operations", l)
+ }
- // accumulate enablements of remaining launchers
- for i, s := range states {
- if s.Config != nil {
- rt |= s.Config.Enablements.Unwrap()
- } else {
- log.Printf("state entry %d does not contain config", i)
- }
+ // accumulate enablements of remaining launchers
+ for i, s := range states {
+ if s.Config != nil {
+ rt |= s.Config.Enablements.Unwrap()
+ } else {
+ log.Printf("state entry %d does not contain config", i)
}
}
+
ec |= rt ^ (system.EWayland | system.EX11 | system.EDBus | system.EPulse)
if hlog.Load() {
if ec > 0 {
@@ -109,27 +189,70 @@ func (seal *Outcome) Run(rs *RunState) error {
}
}
- return seal.sys.Revert((*system.Criteria)(&ec))
- }()
- })
- storeErr.save(revertErr, store.Close())
- rs.RevertErr = storeErr.equiv("clean up")
- }()
+ if err = ms.seal.sys.Revert((*system.Criteria)(&ec)); err != nil {
+ perror(err, "revert system setup")
+ }
+ }
+ }); err != nil {
+ if ok {
+ perror(err, "unlock state store")
+ } else {
+ perror(err, "open state store")
+ }
+ }
+ } else if ms.uintptr&mainNeedsDestroy != 0 {
+ panic("unreachable")
+ }
+
+ if ms.store != nil {
+ if err := ms.store.Close(); err != nil {
+ perror(err, "close state store")
+ }
+ }
+}
+
+// fatal calls printMessageError, performs necessary cleanup, followed by a call to [os.Exit](1).
+func (ms mainState) fatal(fallback string, ferr error) {
+ printMessageError(fallback, ferr)
+ ms.beforeExit(true)
+ os.Exit(1)
+}
+
+// Main commits deferred system setup, runs the container, reverts changes to the system, and terminates the program.
+// Main does not return.
+func (seal *Outcome) Main() {
+ if !seal.f.CompareAndSwap(false, true) {
+ panic("outcome: attempted to run twice")
+ }
+
+ // read comp value early for early failure
+ hsuPath := internal.MustHsuPath()
+
+ // ms.beforeExit required beyond this point
+ ms := &mainState{seal: seal}
+
+ if err := seal.sys.Commit(); err != nil {
+ ms.fatal("cannot commit system setup:", err)
+ }
+ ms.uintptr |= mainNeedsRevert
+ ms.store = state.NewMulti(seal.runDirPath.String())
ctx, cancel := context.WithCancel(seal.ctx)
defer cancel()
- cmd := exec.CommandContext(ctx, hsuPath)
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
- cmd.Dir = container.FHSRoot // container init enters final working directory
+ ms.cancel = cancel
+
+ ms.cmd = exec.CommandContext(ctx, hsuPath)
+ ms.cmd.Stdin, ms.cmd.Stdout, ms.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
+ ms.cmd.Dir = container.FHSRoot // container init enters final working directory
// shim runs in the same session as monitor; see shim.go for behaviour
- cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGCONT) }
+ ms.cmd.Cancel = func() error { return ms.cmd.Process.Signal(syscall.SIGCONT) }
var e *gob.Encoder
- if fd, encoder, err := container.Setup(&cmd.ExtraFiles); err != nil {
- return &hst.AppError{Step: "create shim setup pipe", Err: err}
+ if fd, encoder, err := container.Setup(&ms.cmd.ExtraFiles); err != nil {
+ ms.fatal("cannot create shim setup pipe:", err)
} else {
e = encoder
- cmd.Env = []string{
+ ms.cmd.Env = []string{
// passed through to shim by hsu
shimEnv + "=" + strconv.Itoa(fd),
// interpreted by hsu
@@ -140,102 +263,78 @@ func (seal *Outcome) Run(rs *RunState) error {
if len(seal.user.supp) > 0 {
hlog.Verbosef("attaching supplementary group ids %s", seal.user.supp)
// interpreted by hsu
- cmd.Env = append(cmd.Env, "HAKUREI_GROUPS="+strings.Join(seal.user.supp, " "))
+ ms.cmd.Env = append(ms.cmd.Env, "HAKUREI_GROUPS="+strings.Join(seal.user.supp, " "))
}
hlog.Verbosef("setuid helper at %s", hsuPath)
hlog.Suspend()
- if err := cmd.Start(); err != nil {
- return &hst.AppError{Step: "start setuid wrapper", Err: err}
+ if err := ms.cmd.Start(); err != nil {
+ ms.fatal("cannot start setuid wrapper:", err)
}
- rs.setStart()
-
- // this prevents blocking forever on an early failure
- waitErr, setupErr := make(chan error, 1), make(chan error, 1)
- go func() { waitErr <- cmd.Wait(); cancel() }()
- go func() {
- setupErr <- e.Encode(&shimParams{
- os.Getpid(),
- seal.waitDelay,
- seal.container,
- hlog.Load(),
- })
- }()
+ startTime := time.Now().UTC()
+ ms.cmdWait = make(chan error, 1)
+ // this ties context back to the life of the process
+ go func() { ms.cmdWait <- ms.cmd.Wait(); cancel() }()
+ ms.Time = &startTime
+ // unfortunately the I/O here cannot be directly canceled;
+ // the cancellation path leads to fatal in this case so that is fine
select {
- case err := <-setupErr:
+ case err := <-func() (setupErr chan error) {
+ setupErr = make(chan error, 1)
+ go func() {
+ setupErr <- e.Encode(&shimParams{
+ os.Getpid(),
+ seal.waitDelay,
+ seal.container,
+ hlog.Load(),
+ })
+ }()
+ return
+ }():
if err != nil {
hlog.Resume()
- return &hst.AppError{Step: "transmit shim config", Err: err}
+ ms.fatal("cannot transmit shim config:", err)
}
case <-ctx.Done():
hlog.Resume()
- return newWithMessageError("shim setup canceled", syscall.ECANCELED)
+ ms.fatal("shim context canceled:", newWithMessageError("shim setup canceled", ctx.Err()))
}
- // returned after blocking on waitErr
- var earlyStoreErr = new(StateStoreError)
- {
- // shim accepted setup payload, create process state
- sd := state.State{
+ // shim accepted setup payload, create process state
+ if ok, err := ms.store.Do(seal.user.identity.unwrap(), func(c state.Cursor) {
+ if err := c.Save(&state.State{
ID: seal.id.unwrap(),
- PID: cmd.Process.Pid,
- Time: *rs.Time,
+ PID: ms.cmd.Process.Pid,
+ Time: *ms.Time,
+ }, seal.ct); err != nil {
+ ms.fatal("cannot save state entry:", err)
}
- earlyStoreErr.Inner, earlyStoreErr.DoErr = store.Do(seal.user.identity.unwrap(), func(c state.Cursor) {
- earlyStoreErr.InnerErr = c.Save(&sd, seal.ct)
- })
- }
-
- // state in store at this point, destroy defunct state entry on return
- deferredStoreFunc = func(c state.Cursor) error { return c.Destroy(seal.id.unwrap()) }
-
- waitTimeout := make(chan struct{})
- // TODO(ophestra): enforce this limit early so it does not have to be done twice
- shimTimeoutCompensated := shimWaitTimeout
- if seal.waitDelay > MaxShimWaitDelay {
- shimTimeoutCompensated += MaxShimWaitDelay
- } else {
- shimTimeoutCompensated += seal.waitDelay
- }
- go func() { <-seal.ctx.Done(); time.Sleep(shimTimeoutCompensated); close(waitTimeout) }()
-
- select {
- case rs.WaitErr = <-waitErr:
- rs.WaitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus)
- if hlog.Load() {
- 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)
- }
+ }); err != nil {
+ if ok {
+ ms.uintptr |= mainNeedsDestroy
+ ms.fatal("cannot unlock state store:", err)
+ } else {
+ ms.fatal("cannot open state store:", err)
}
-
- case <-waitTimeout:
- rs.WaitErr = ErrShimTimeout
- hlog.Resume()
- // TODO(ophestra): verify this behaviour in vm tests
- log.Printf("process %d did not terminate", cmd.Process.Pid)
}
+ // state in store at this point, destroy defunct state entry on termination
+ ms.uintptr |= mainNeedsDestroy
- hlog.Resume()
- if seal.sync != nil {
- if err := seal.sync.Close(); err != nil {
- log.Printf("cannot close wayland security context: %v", err)
- }
- }
- if seal.dbusMsg != nil {
- seal.dbusMsg()
+ // beforeExit ties shim process to context
+ ms.beforeExit(false)
+ os.Exit(0)
+}
+
+// printMessageError prints the error message according to [container.GetErrorMessage],
+// or fallback prepended to err if an error message is not available.
+func printMessageError(fallback string, err error) {
+ m, ok := container.GetErrorMessage(err)
+ if !ok {
+ log.Println(fallback, err)
+ return
}
- return earlyStoreErr.equiv("save process state")
+ log.Print(m)
}
diff --git a/internal/app/seal.go b/internal/app/seal.go
index e6cccecc..cafd6915 100644
--- a/internal/app/seal.go
+++ b/internal/app/seal.go
@@ -119,7 +119,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, k sys.State, config *hst.Config) error {
const (
home = "HOME"
shell = "SHELL"
@@ -176,19 +176,17 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
home: config.Home,
username: config.Username,
}
+
if seal.user.username == "" {
seal.user.username = "chronos"
} else if !isValidUsername(seal.user.username) {
return newWithMessage(fmt.Sprintf("invalid user name %q", seal.user.username))
}
- if u, err := sys.Uid(seal.user.identity.unwrap()); err != nil {
- return err
- } else {
- seal.user.uid = newInt(u)
- }
+ seal.user.uid = newInt(sys.MustUid(k, seal.user.identity.unwrap()))
+
seal.user.supp = make([]string, len(config.Groups))
for i, name := range config.Groups {
- if g, err := sys.LookupGroup(name); err != nil {
+ if g, err := k.LookupGroup(name); err != nil {
return newWithMessageError(fmt.Sprintf("unknown group %q", name), err)
} else {
seal.user.supp[i] = g.Gid
@@ -201,7 +199,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
if config.Shell == nil {
config.Shell = container.AbsFHSRoot.Append("bin", "sh")
- s, _ := sys.LookupEnv(shell)
+ s, _ := k.LookupEnv(shell)
if a, err := container.NewAbs(s); err == nil {
config.Shell = a
}
@@ -210,7 +208,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
// hsu clears the environment so resolve paths early
if config.Path == nil {
if len(config.Args) > 0 {
- if p, err := sys.LookPath(config.Args[0]); err != nil {
+ if p, err := k.LookPath(config.Args[0]); err != nil {
return &hst.AppError{Step: "look up executable file", Err: err}
} else if config.Path, err = container.NewAbs(p); err != nil {
return newWithMessageError(err.Error(), err)
@@ -246,7 +244,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
// hide nscd from container if present
nscd := container.AbsFHSVar.Append("run/nscd")
- if _, err := sys.Stat(nscd.String()); !errors.Is(err, fs.ErrNotExist) {
+ if _, err := k.Stat(nscd.String()); !errors.Is(err, fs.ErrNotExist) {
conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSEphemeral{Target: nscd}})
}
@@ -274,7 +272,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
{
var uid, gid int
var err error
- seal.container, seal.env, err = newContainer(config.Container, sys, seal.id.String(), &uid, &gid)
+ seal.container, seal.env, err = newContainer(config.Container, k, seal.id.String(), &uid, &gid)
seal.waitDelay = config.Container.WaitDelay
if err != nil {
return &hst.AppError{Step: "initialise container configuration", Err: err}
@@ -298,7 +296,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
seal.env[xdgSessionClass] = "user"
seal.env[xdgSessionType] = "tty"
- share := &shareHost{seal: seal, sc: sys.Paths()}
+ share := &shareHost{seal: seal, sc: k.Paths()}
seal.runDirPath = share.sc.RunDirPath
seal.sys = system.New(seal.ctx, seal.user.uid.unwrap())
seal.sys.Ensure(share.sc.SharePath.String(), 0711)
@@ -342,14 +340,14 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
}
// pass TERM for proper terminal I/O in initial process
- if t, ok := sys.LookupEnv(term); ok {
+ if t, ok := k.LookupEnv(term); ok {
seal.env[term] = t
}
if config.Enablements.Unwrap()&system.EWayland != 0 {
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath *container.Absolute
- if name, ok := sys.LookupEnv(wayland.WaylandDisplay); !ok {
+ if name, ok := k.LookupEnv(wayland.WaylandDisplay); !ok {
hlog.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName)
socketPath = share.sc.RuntimePath.Append(wayland.FallbackName)
} else if a, err := container.NewAbs(name); err != nil {
@@ -380,7 +378,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
}
if config.Enablements.Unwrap()&system.EX11 != 0 {
- if d, ok := sys.LookupEnv(display); !ok {
+ if d, ok := k.LookupEnv(display); !ok {
return newWithMessage("DISPLAY is not set")
} else {
socketDir := container.AbsFHSTmp.Append(".X11-unix")
@@ -398,7 +396,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
}
}
if socketPath != nil {
- if _, err := sys.Stat(socketPath.String()); err != nil {
+ if _, err := k.Stat(socketPath.String()); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return &hst.AppError{Step: fmt.Sprintf("access X11 socket %q", socketPath), Err: err}
}
@@ -422,14 +420,14 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
// PulseAudio socket (usually `/run/user/%d/pulse/native`)
pulseSocket := pulseRuntimeDir.Append("native")
- if _, err := sys.Stat(pulseRuntimeDir.String()); err != nil {
+ if _, err := k.Stat(pulseRuntimeDir.String()); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return &hst.AppError{Step: fmt.Sprintf("access PulseAudio directory %q", pulseRuntimeDir), Err: err}
}
return newWithMessage(fmt.Sprintf("PulseAudio directory %q not found", pulseRuntimeDir))
}
- if s, err := sys.Stat(pulseSocket.String()); err != nil {
+ if s, err := k.Stat(pulseSocket.String()); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return &hst.AppError{Step: fmt.Sprintf("access PulseAudio socket %q", pulseSocket), Err: err}
}
@@ -453,7 +451,7 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
const paLocateStep = "locate PulseAudio cookie"
// from environment
- if p, ok := sys.LookupEnv(pulseCookie); ok {
+ if p, ok := k.LookupEnv(pulseCookie); ok {
if a, err := container.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
@@ -464,14 +462,14 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
}
// $HOME/.pulse-cookie
- if p, ok := sys.LookupEnv(home); ok {
+ if p, ok := k.LookupEnv(home); ok {
if a, err := container.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
paCookiePath = a.Append(".pulse-cookie")
}
- if s, err := sys.Stat(paCookiePath.String()); err != nil {
+ if s, err := k.Stat(paCookiePath.String()); err != nil {
paCookiePath = nil
if !errors.Is(err, fs.ErrNotExist) {
return &hst.AppError{Step: "access PulseAudio cookie", Err: err}
@@ -485,13 +483,13 @@ func (seal *Outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
}
// $XDG_CONFIG_HOME/pulse/cookie
- if p, ok := sys.LookupEnv(xdgConfigHome); ok {
+ if p, ok := k.LookupEnv(xdgConfigHome); ok {
if a, err := container.NewAbs(p); err != nil {
return &hst.AppError{Step: paLocateStep, Err: err}
} else {
paCookiePath = a.Append("pulse", "cookie")
}
- if s, err := sys.Stat(paCookiePath.String()); err != nil {
+ if s, err := k.Stat(paCookiePath.String()); err != nil {
paCookiePath = nil
if !errors.Is(err, fs.ErrNotExist) {
return &hst.AppError{Step: "access PulseAudio cookie", Err: err}
diff --git a/internal/app/state/multi.go b/internal/app/state/multi.go
index 80a3abff..9a400ac6 100644
--- a/internal/app/state/multi.go
+++ b/internal/app/state/multi.go
@@ -27,27 +27,27 @@ type multiStore struct {
lock sync.RWMutex
}
-func (s *multiStore) Do(aid int, f func(c Cursor)) (bool, error) {
+func (s *multiStore) Do(identity int, f func(c Cursor)) (bool, error) {
s.lock.RLock()
defer s.lock.RUnlock()
// load or initialise new backend
b := new(multiBackend)
b.lock.Lock()
- if v, ok := s.backends.LoadOrStore(aid, b); ok {
+ if v, ok := s.backends.LoadOrStore(identity, b); ok {
b = v.(*multiBackend)
} else {
- b.path = path.Join(s.base, strconv.Itoa(aid))
+ b.path = path.Join(s.base, strconv.Itoa(identity))
// ensure directory
if err := os.MkdirAll(b.path, 0700); err != nil && !errors.Is(err, fs.ErrExist) {
- s.backends.CompareAndDelete(aid, b)
+ s.backends.CompareAndDelete(identity, b)
return false, err
}
// open locker file
if l, err := os.OpenFile(b.path+".lock", os.O_RDWR|os.O_CREATE, 0600); err != nil {
- s.backends.CompareAndDelete(aid, b)
+ s.backends.CompareAndDelete(identity, b)
return false, err
} else {
b.lockfile = l
diff --git a/internal/app/state/state.go b/internal/app/state/state.go
index 42750ec6..6a3a82db 100644
--- a/internal/app/state/state.go
+++ b/internal/app/state/state.go
@@ -17,7 +17,7 @@ type Store interface {
// Do calls f exactly once and ensures store exclusivity until f returns.
// Returns whether f is called and any errors during the locking process.
// Cursor provided to f becomes invalid as soon as f returns.
- Do(aid int, f func(c Cursor)) (ok bool, err error)
+ Do(identity int, f func(c Cursor)) (ok bool, err error)
// List queries the store and returns a list of aids known to the store.
// Note that some or all returned aids might not have any active apps.