diff --git a/internal/outcome/hsu.go b/internal/outcome/hsu.go index c1be676..84751b4 100644 --- a/internal/outcome/hsu.go +++ b/internal/outcome/hsu.go @@ -38,6 +38,7 @@ func (h *Hsu) ensureDispatcher() { } // ID returns the current user hsurc identifier. +// // [ErrHsuAccess] is returned if the current user is not in hsurc. func (h *Hsu) ID() (int, error) { h.ensureDispatcher() diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go index 3516eae..42b5038 100644 --- a/internal/outcome/outcome.go +++ b/internal/outcome/outcome.go @@ -1,4 +1,5 @@ -// Package outcome implements the outcome of the privileged and container sides of a hakurei container. +// Package outcome implements the outcome of the privileged and container sides +// of a hakurei container. package outcome import ( @@ -27,8 +28,9 @@ func Info() *hst.Info { return &hi } -// envAllocSize is the initial size of the env map pre-allocated when the configured env map is nil. -// It should be large enough to fit all insertions by outcomeOp.toContainer. +// envAllocSize is the initial size of the env map pre-allocated when the +// configured env map is nil. It should be large enough to fit all insertions by +// outcomeOp.toContainer. const envAllocSize = 1 << 6 func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} } @@ -43,7 +45,8 @@ func (s *stringPair[T]) unwrap() T { return s.v } func (s *stringPair[T]) String() string { return s.s } // outcomeState is copied to the shim process and available while applying outcomeOp. -// This is transmitted from the priv side to the shim, so exported fields should be kept to a minimum. +// This is transmitted from the priv side to the shim, so exported fields should +// be kept to a minimum. type outcomeState struct { // Params only used by the shim process. Populated by populateEarly. Shim *shimParams @@ -89,10 +92,15 @@ func (s *outcomeState) valid() bool { s.Paths != nil } -// newOutcomeState returns the address of a new outcomeState with its exported fields populated via syscallDispatcher. +// newOutcomeState returns the address of a new outcomeState with its exported +// fields populated via syscallDispatcher. func newOutcomeState(k syscallDispatcher, msg message.Msg, id *hst.ID, config *hst.Config, hsu *Hsu) *outcomeState { s := outcomeState{ - Shim: &shimParams{PrivPID: k.getpid(), Verbose: msg.IsVerbose()}, + Shim: &shimParams{ + PrivPID: k.getpid(), + Verbose: msg.IsVerbose(), + }, + ID: id, Identity: config.Identity, UserID: hsu.MustID(msg), @@ -121,6 +129,7 @@ func newOutcomeState(k syscallDispatcher, msg message.Msg, id *hst.ID, config *h } // populateLocal populates unexported fields from transmitted exported fields. +// // These fields are cheaper to recompute per-process. func (s *outcomeState) populateLocal(k syscallDispatcher, msg message.Msg) error { if !s.valid() || k == nil || msg == nil { @@ -136,7 +145,10 @@ func (s *outcomeState) populateLocal(k syscallDispatcher, msg message.Msg) error s.id = &stringPair[hst.ID]{*s.ID, s.ID.String()} s.Copy(&s.sc, s.UserID) - msg.Verbosef("process share directory at %q, runtime directory at %q", s.sc.SharePath, s.sc.RunDirPath) + msg.Verbosef( + "process share directory at %q, runtime directory at %q", + s.sc.SharePath, s.sc.RunDirPath, + ) s.identity = newInt(s.Identity) s.mapuid, s.mapgid = newInt(s.Mapuid), newInt(s.Mapgid) @@ -146,17 +158,25 @@ func (s *outcomeState) populateLocal(k syscallDispatcher, msg message.Msg) error } // instancePath returns a path formatted for outcomeStateSys.instance. +// // This method must only be called from outcomeOp.toContainer if // outcomeOp.toSystem has already called outcomeStateSys.instance. -func (s *outcomeState) instancePath() *check.Absolute { return s.sc.SharePath.Append(s.id.String()) } +func (s *outcomeState) instancePath() *check.Absolute { + return s.sc.SharePath.Append(s.id.String()) +} // runtimePath returns a path formatted for outcomeStateSys.runtime. +// // This method must only be called from outcomeOp.toContainer if // outcomeOp.toSystem has already called outcomeStateSys.runtime. -func (s *outcomeState) runtimePath() *check.Absolute { return s.sc.RunDirPath.Append(s.id.String()) } +func (s *outcomeState) runtimePath() *check.Absolute { + return s.sc.RunDirPath.Append(s.id.String()) +} // outcomeStateSys wraps outcomeState and [system.I]. Used on the priv side only. -// Implementations of outcomeOp must not access fields other than sys unless explicitly stated. +// +// Implementations of outcomeOp must not access fields other than sys unless +// explicitly stated. type outcomeStateSys struct { // Whether XDG_RUNTIME_DIR is used post hsu. useRuntimeDir bool @@ -219,6 +239,7 @@ func (state *outcomeStateSys) ensureRuntimeDir() { } // instance returns the pathname to a process-specific directory within TMPDIR. +// // This directory must only hold entries bound to [system.Process]. func (state *outcomeStateSys) instance() *check.Absolute { if state.sharePath != nil { @@ -230,6 +251,7 @@ func (state *outcomeStateSys) instance() *check.Absolute { } // runtime returns the pathname to a process-specific directory within XDG_RUNTIME_DIR. +// // This directory must only hold entries bound to [system.Process]. func (state *outcomeStateSys) runtime() *check.Absolute { if state.runtimeSharePath != nil { @@ -242,22 +264,29 @@ func (state *outcomeStateSys) runtime() *check.Absolute { return state.runtimeSharePath } -// outcomeStateParams wraps outcomeState and [container.Params]. Used on the shim side only. +// outcomeStateParams wraps outcomeState and [container.Params]. +// +// Used on the shim side only. type outcomeStateParams struct { - // Overrides the embedded [container.Params] in [container.Container]. The Env field must not be used. + // Overrides the embedded [container.Params] in [container.Container]. + // + // The Env field must not be used. params *container.Params // Collapsed into the Env slice in [container.Params] by the final outcomeOp. env map[string]string - // Filesystems with the optional root sliced off if present. Populated by spParamsOp. - // Safe for use by spFilesystemOp. + // Filesystems with the optional root sliced off if present. + // + // Populated by spParamsOp. Safe for use by spFilesystemOp. filesystem []hst.FilesystemConfigJSON // Inner XDG_RUNTIME_DIR default formatting of `/run/user/%d` via mapped uid. + // // Populated by spRuntimeOp. runtimeDir *check.Absolute // Path to pipewire-pulse server. + // // Populated by spPipeWireOp if DirectPipeWire is false. pipewirePulsePath *check.Absolute @@ -265,25 +294,32 @@ type outcomeStateParams struct { *outcomeState } -// errNotEnabled is returned by outcomeOp.toSystem and used internally to exclude an outcomeOp from transmission. +// errNotEnabled is returned by outcomeOp.toSystem and used internally to +// exclude an outcomeOp from transmission. var errNotEnabled = errors.New("op not enabled in the configuration") -// An outcomeOp inflicts an outcome on [system.I] and contains enough information to -// inflict it on [container.Params] in a separate process. -// An implementation of outcomeOp must store cross-process states in exported fields only. +// An outcomeOp inflicts an outcome on [system.I] and contains enough +// information to inflict it on [container.Params] in a separate process. +// +// An implementation of outcomeOp must store cross-process states in exported +// fields only. type outcomeOp interface { // toSystem inflicts the current outcome on [system.I] in the priv side process. toSystem(state *outcomeStateSys) error - // toContainer inflicts the current outcome on [container.Params] in the shim process. - // The implementation must not write to the Env field of [container.Params] as it will be overwritten - // by flattened env map. + // toContainer inflicts the current outcome on [container.Params] in the + // shim process. + // + // Implementations must not write to the Env field of [container.Params] + // as it will be overwritten by flattened env map. toContainer(state *outcomeStateParams) error } -// toSystem calls the outcomeOp.toSystem method on all outcomeOp implementations and populates shimParams.Ops. -// This function assumes the caller has already called the Validate method on [hst.Config] -// and checked that it returns nil. +// toSystem calls the outcomeOp.toSystem method on all outcomeOp implementations +// and populates shimParams.Ops. +// +// This function assumes the caller has already called the Validate method on +// [hst.Config] and checked that it returns nil. func (state *outcomeStateSys) toSystem() error { if state.Shim == nil || state.Shim.Ops != nil { return newWithMessage("invalid ops state reached") diff --git a/internal/outcome/process.go b/internal/outcome/process.go index e7b7113..be919a6 100644 --- a/internal/outcome/process.go +++ b/internal/outcome/process.go @@ -30,7 +30,9 @@ const ( ) // NewStore returns the address of a new instance of [store.Store]. -func NewStore(sc *hst.Paths) *store.Store { return store.New(sc.SharePath.Append("state")) } +func NewStore(sc *hst.Paths) *store.Store { + return store.New(sc.SharePath.Append("state")) +} // main carries out outcome and terminates. main does not return. func (k *outcome) main(msg message.Msg, identifierFd int) { @@ -116,7 +118,11 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { processStatePrev, processStateCur = processStateCur, processState if !processTime.IsZero() && processStatePrev != processLifecycle { - msg.Verbosef("state %d took %.2f ms", processStatePrev, float64(time.Since(processTime).Nanoseconds())/1e6) + msg.Verbosef( + "state %d took %.2f ms", + processStatePrev, + float64(time.Since(processTime).Nanoseconds())/1e6, + ) } processTime = time.Now() @@ -141,7 +147,10 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { case processCommit: if isBeforeRevert { - perrorFatal(newWithMessage("invalid transition to commit state"), "commit", processLifecycle) + perrorFatal( + newWithMessage("invalid transition to commit state"), + "commit", processLifecycle, + ) continue } @@ -238,15 +247,26 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { case <-func() chan struct{} { w := make(chan struct{}) - // this ties processLifecycle to ctx with the additional compensated timeout duration - // to allow transition to the next state on a locked up shim - go func() { <-ctx.Done(); time.Sleep(k.state.Shim.WaitDelay + shimWaitTimeout); close(w) }() + // This ties processLifecycle to ctx with the additional + // compensated timeout duration to allow transition to the next + // state on a locked up shim. + go func() { + <-ctx.Done() + time.Sleep(k.state.Shim.WaitDelay + shimWaitTimeout) + close(w) + }() return w }(): - // this is only reachable when wait did not return 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. - msg.GetLogger().Printf("process %d did not terminate", shimCmd.Process.Pid) + // This is only reachable when wait did not return 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. + msg.GetLogger().Printf( + "process %d did not terminate", + shimCmd.Process.Pid, + ) } msg.Resume() @@ -271,8 +291,8 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { ec := system.Process if entries, _, err := handle.Entries(); err != nil { - // it is impossible to continue from this point, - // per-process state will be reverted to limit damage + // it is impossible to continue from this point, per-process + // state will be reverted to limit damage perror(err, "read store segment entries") } else { // accumulate enablements of remaining instances @@ -295,7 +315,10 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { if n == 0 { ec |= system.User } else { - msg.Verbosef("found %d instances, cleaning up without user-scoped operations", n) + msg.Verbosef( + "found %d instances, cleaning up without user-scoped operations", + n, + ) } ec |= rt ^ (hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse) if msg.IsVerbose() { @@ -335,7 +358,9 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { // start starts the shim via cmd/hsu. // -// If successful, a [time.Time] value for [hst.State] is stored in the value pointed to by startTime. +// If successful, a [time.Time] value for [hst.State] is stored in the value +// pointed to by startTime. +// // The resulting [exec.Cmd] and write end of the shim setup pipe is returned. func (k *outcome) start(ctx context.Context, msg message.Msg, hsuPath *check.Absolute, diff --git a/internal/outcome/shim.go b/internal/outcome/shim.go index a569e7a..8fc46f2 100644 --- a/internal/outcome/shim.go +++ b/internal/outcome/shim.go @@ -37,9 +37,12 @@ const ( shimMsgBadPID = C.HAKUREI_SHIM_BAD_PID ) -// setupContSignal sets up the SIGCONT signal handler for the cross-uid shim exit hack. -// The signal handler is implemented in C, signals can be processed by reading from the returned reader. -// The returned function must be called after all signal processing concludes. +// setupContSignal sets up the SIGCONT signal handler for the cross-uid shim +// exit hack. +// +// The signal handler is implemented in C, signals can be processed by reading +// from the returned reader. The returned function must be called after all +// signal processing concludes. func setupContSignal(pid int) (io.ReadCloser, func(), error) { if r, w, err := os.Pipe(); err != nil { return nil, nil, err @@ -51,22 +54,28 @@ func setupContSignal(pid int) (io.ReadCloser, func(), error) { } } -// shimEnv is the name of the environment variable storing decimal representation of -// setup pipe fd for [container.Receive]. +// shimEnv is the name of the environment variable storing decimal representation +// of setup pipe fd for [container.Receive]. const shimEnv = "HAKUREI_SHIM" // shimParams is embedded in outcomeState and transmitted from priv side to shim. type shimParams struct { - // Priv side pid, checked against ppid in signal handler for the syscall.SIGCONT hack. + // Priv side pid, checked against ppid in signal handler for the + // syscall.SIGCONT hack. PrivPID int - // Duration to wait for after the initial process receives os.Interrupt before the container is killed. + // Duration to wait for after the initial process receives os.Interrupt + // before the container is killed. + // // Limits are enforced on the priv side. WaitDelay time.Duration // Verbosity pass through from [message.Msg]. Verbose bool + // Copied from [hst.Config]. + SchedPolicy std.SchedPolicy + // Outcome setup ops, contains setup state. Populated by outcome.finalise. Ops []outcomeOp } @@ -77,7 +86,9 @@ func (p *shimParams) valid() bool { return p != nil && p.PrivPID > 0 } // shimName is the prefix used by log.std in the shim process. const shimName = "shim" -// Shim is called by the main function of the shim process and runs as the unconstrained target user. +// Shim is called by the main function of the shim process and runs as the +// unconstrained target user. +// // Shim does not return. func Shim(msg message.Msg) { if msg == nil { @@ -131,7 +142,8 @@ func (sp *shimPrivate) destroy() { } const ( - // shimPipeWireTimeout is the duration pipewire-pulse is allowed to run before its socket becomes available. + // shimPipeWireTimeout is the duration pipewire-pulse is allowed to run + // before its socket becomes available. shimPipeWireTimeout = 5 * time.Second ) diff --git a/internal/outcome/spcontainer.go b/internal/outcome/spcontainer.go index 768e6bc..2d6bb78 100644 --- a/internal/outcome/spcontainer.go +++ b/internal/outcome/spcontainer.go @@ -27,7 +27,9 @@ const varRunNscd = fhs.Var + "run/nscd" func init() { gob.Register(new(spParamsOp)) } -// spParamsOp initialises unordered fields of [container.Params] and the optional root filesystem. +// spParamsOp initialises unordered fields of [container.Params] and the +// optional root filesystem. +// // This outcomeOp is hardcoded to always run first. type spParamsOp struct { // Value of $TERM, stored during toSystem. @@ -67,8 +69,8 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { state.params.Args = state.Container.Args } - // the container is canceled when shim is requested to exit or receives an interrupt or termination signal; - // this behaviour is implemented in the shim + // The container is cancelled when shim is requested to exit or receives an + // interrupt or termination signal. This behaviour is implemented in the shim. state.params.ForwardCancel = state.Shim.WaitDelay > 0 if state.Container.Flags&hst.FMultiarch != 0 { @@ -115,7 +117,8 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { } else { state.params.Bind(fhs.AbsDev, fhs.AbsDev, std.BindWritable|std.BindDevice) } - // /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it + // /dev is mounted readonly later on, this prevents /dev/shm from going + // readonly with it state.params.Tmpfs(fhs.AbsDevShm, 0, 01777) return nil @@ -123,7 +126,9 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error { func init() { gob.Register(new(spFilesystemOp)) } -// spFilesystemOp applies configured filesystems to [container.Params], excluding the optional root filesystem. +// spFilesystemOp applies configured filesystems to [container.Params], +// excluding the optional root filesystem. +// // This outcomeOp is hardcoded to always run last. type spFilesystemOp struct { // Matched paths to cover. Stored during toSystem. @@ -297,8 +302,8 @@ func (s *spFilesystemOp) toContainer(state *outcomeStateParams) error { return nil } -// resolveRoot handles the root filesystem special case for [hst.FilesystemConfig] and additionally resolves autoroot -// as it requires special handling during path hiding. +// resolveRoot handles the root filesystem special case for [hst.FilesystemConfig] +// and additionally resolves autoroot as it requires special handling during path hiding. func resolveRoot(c *hst.ContainerConfig) (rootfs hst.FilesystemConfig, filesystem []hst.FilesystemConfigJSON, autoroot *hst.FSBind) { // root filesystem special case filesystem = c.Filesystem @@ -316,7 +321,8 @@ func resolveRoot(c *hst.ContainerConfig) (rootfs hst.FilesystemConfig, filesyste return } -// evalSymlinks calls syscallDispatcher.evalSymlinks but discards errors unwrapping to [fs.ErrNotExist]. +// evalSymlinks calls syscallDispatcher.evalSymlinks but discards errors +// unwrapping to [fs.ErrNotExist]. func evalSymlinks(msg message.Msg, k syscallDispatcher, v *string) error { if p, err := k.evalSymlinks(*v); err != nil { if !errors.Is(err, fs.ErrNotExist) { diff --git a/internal/outcome/spdbus.go b/internal/outcome/spdbus.go index 8b3fbdb..5939ede 100644 --- a/internal/outcome/spdbus.go +++ b/internal/outcome/spdbus.go @@ -12,6 +12,7 @@ import ( func init() { gob.Register(new(spDBusOp)) } // spDBusOp maintains an xdg-dbus-proxy instance for the container. +// // Runs after spRuntimeOp. type spDBusOp struct { // Whether to bind the system bus socket. Populated during toSystem. diff --git a/internal/outcome/sppipewire.go b/internal/outcome/sppipewire.go index 8c5760b..96675eb 100644 --- a/internal/outcome/sppipewire.go +++ b/internal/outcome/sppipewire.go @@ -13,9 +13,12 @@ const pipewirePulseName = "pipewire-pulse" func init() { gob.Register(new(spPipeWireOp)) } // spPipeWireOp exports the PipeWire server to the container via SecurityContext. +// // Runs after spRuntimeOp. type spPipeWireOp struct { - // Path to pipewire-pulse server. Populated during toSystem if DirectPipeWire is false. + // Path to pipewire-pulse server. + // + // Populated during toSystem if DirectPipeWire is false. CompatServerPath *check.Absolute } diff --git a/internal/outcome/sppulse.go b/internal/outcome/sppulse.go index e86fb3f..386800b 100644 --- a/internal/outcome/sppulse.go +++ b/internal/outcome/sppulse.go @@ -20,6 +20,7 @@ const pulseCookieSizeMax = 1 << 8 func init() { gob.Register(new(spPulseOp)) } // spPulseOp exports the PulseAudio server to the container. +// // Runs after spRuntimeOp. type spPulseOp struct { // PulseAudio cookie data, populated during toSystem if a cookie is present. @@ -37,24 +38,40 @@ func (s *spPulseOp) toSystem(state *outcomeStateSys) error { if _, err := state.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 &hst.AppError{Step: fmt.Sprintf( + "access PulseAudio directory %q", + pulseRuntimeDir, + ), Err: err} } - return newWithMessageError(fmt.Sprintf("PulseAudio directory %q not found", pulseRuntimeDir), err) + return newWithMessageError(fmt.Sprintf( + "PulseAudio directory %q not found", + pulseRuntimeDir, + ), err) } if fi, err := state.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} + return &hst.AppError{Step: fmt.Sprintf( + "access PulseAudio socket %q", + pulseSocket, + ), Err: err} } - return newWithMessageError(fmt.Sprintf("PulseAudio directory %q found but socket does not exist", pulseRuntimeDir), err) + return newWithMessageError(fmt.Sprintf( + "PulseAudio directory %q found but socket does not exist", + pulseRuntimeDir, + ), err) } else { if m := fi.Mode(); m&0o006 != 0o006 { - return newWithMessage(fmt.Sprintf("unexpected permissions on %q: %s", pulseSocket, m)) + return newWithMessage(fmt.Sprintf( + "unexpected permissions on %q: %s", + pulseSocket, m, + )) } } - // pulse socket is world writable and its parent directory DAC permissions prevents access; - // hard link to target-executable share directory to grant access + // PulseAudio socket is world writable and its parent directory DAC + // permissions prevents access. Hard link to target-executable share + // directory to grant access state.sys.Link(pulseSocket, state.runtime().Append("pulse")) // load up to pulseCookieSizeMax bytes of pulse cookie for transmission to shim @@ -62,7 +79,13 @@ func (s *spPulseOp) toSystem(state *outcomeStateSys) error { return err } else if a != nil { s.Cookie = new([pulseCookieSizeMax]byte) - if s.CookieSize, err = loadFile(state.msg, state.k, "PulseAudio cookie", a.String(), s.Cookie[:]); err != nil { + if s.CookieSize, err = loadFile( + state.msg, + state.k, + "PulseAudio cookie", + a.String(), + s.Cookie[:], + ); err != nil { return err } } else { @@ -101,8 +124,9 @@ func (s *spPulseOp) commonPaths(state *outcomeState) (pulseRuntimeDir, pulseSock return } -// discoverPulseCookie attempts to discover the pathname of the PulseAudio cookie of the current user. -// If both returned pathname and error are nil, the cookie is likely unavailable and can be silently skipped. +// discoverPulseCookie attempts to discover the pathname of the PulseAudio +// cookie of the current user. If both returned pathname and error are nil, the +// cookie is likely unavailable and can be silently skipped. func discoverPulseCookie(k syscallDispatcher) (*check.Absolute, error) { const paLocateStep = "locate PulseAudio cookie" @@ -186,7 +210,10 @@ func loadFile( &os.PathError{Op: "stat", Path: pathname, Err: syscall.ENOMEM}, ) } else if s < int64(n) { - msg.Verbosef("%s at %q is %d bytes shorter than expected", description, pathname, int64(n)-s) + msg.Verbosef( + "%s at %q is %d bytes shorter than expected", + description, pathname, int64(n)-s, + ) } else { msg.Verbosef("loading %d bytes from %q", n, pathname) } diff --git a/internal/outcome/spruntime.go b/internal/outcome/spruntime.go index 6ec532b..727a5be 100644 --- a/internal/outcome/spruntime.go +++ b/internal/outcome/spruntime.go @@ -67,7 +67,9 @@ const ( // spRuntimeOp sets up XDG_RUNTIME_DIR inside the container. type spRuntimeOp struct { - // SessionType determines the value of envXDGSessionType. Populated during toSystem. + // SessionType determines the value of envXDGSessionType. + // + // Populated during toSystem. SessionType uintptr } diff --git a/internal/outcome/spwayland.go b/internal/outcome/spwayland.go index 7eccce2..5506661 100644 --- a/internal/outcome/spwayland.go +++ b/internal/outcome/spwayland.go @@ -12,9 +12,12 @@ import ( func init() { gob.Register(new(spWaylandOp)) } // spWaylandOp exports the Wayland display server to the container. +// // Runs after spRuntimeOp. type spWaylandOp struct { - // Path to host wayland socket. Populated during toSystem if DirectWayland is true. + // Path to host wayland socket. + // + // Populated during toSystem if DirectWayland is true. SocketPath *check.Absolute } diff --git a/internal/outcome/spx11.go b/internal/outcome/spx11.go index fd2b273..70ab242 100644 --- a/internal/outcome/spx11.go +++ b/internal/outcome/spx11.go @@ -50,7 +50,10 @@ func (s *spX11Op) toSystem(state *outcomeStateSys) error { if socketPath != nil { if _, err := state.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} + return &hst.AppError{Step: fmt.Sprintf( + "access X11 socket %q", + socketPath, + ), Err: err} } } else { state.sys.UpdatePermType(hst.EX11, socketPath, acl.Read, acl.Write, acl.Execute)