internal/app/spruntime: emulate pam_systemd type
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Hakurei (push) Successful in 44s
Test / Hakurei (race detector) (push) Successful in 44s
Test / Hpkg (push) Successful in 42s
Test / Sandbox (push) Successful in 1m42s
Test / Sandbox (race detector) (push) Successful in 2m29s
Test / Flake checks (push) Successful in 1m22s

This sets XDG_SESSION_TYPE to the corresponding values specified in pam_systemd(8) according to enablements.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-10-18 04:25:40 +09:00
parent 030ad2a73b
commit d4284c109d
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
9 changed files with 186 additions and 27 deletions

View File

@ -268,7 +268,7 @@ func TestApp(t *testing.T) {
"WAYLAND_DISPLAY=wayland-0", "WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/65534", "XDG_RUNTIME_DIR=/run/user/65534",
"XDG_SESSION_CLASS=user", "XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=wayland",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/"), bits.BindWritable). Root(m("/"), bits.BindWritable).
@ -420,7 +420,7 @@ func TestApp(t *testing.T) {
"WAYLAND_DISPLAY=wayland-0", "WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1971", "XDG_RUNTIME_DIR=/run/user/1971",
"XDG_SESSION_CLASS=user", "XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=tty", "XDG_SESSION_TYPE=wayland",
}, },
Ops: new(container.Ops). Ops: new(container.Ops).
Proc(m("/proc/")). Proc(m("/proc/")).

View File

@ -275,7 +275,7 @@ func (state *outcomeStateSys) toSystem() error {
// TODO(ophestra): move this late for #8 and #9 // TODO(ophestra): move this late for #8 and #9
&spFilesystemOp{}, &spFilesystemOp{},
spRuntimeOp{}, &spRuntimeOp{},
spTmpdirOp{}, spTmpdirOp{},
spAccountOp{}, spAccountOp{},

View File

@ -6,43 +6,114 @@ import (
"hakurei.app/container/bits" "hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/acl" "hakurei.app/system/acl"
) )
func init() { gob.Register(spRuntimeOp{}) } const (
/*
Path to a user-private user-writable directory that is bound
to the user login time on the machine. It is automatically
created the first time a user logs in and removed on the
user's final logout. If a user logs in twice at the same time,
both sessions will see the same $XDG_RUNTIME_DIR and the same
contents. If a user logs in once, then logs out again, and
logs in again, the directory contents will have been lost in
between, but applications should not rely on this behavior and
must be able to deal with stale files. To store
session-private data in this directory, the user should
include the value of $XDG_SESSION_ID in the filename. This
directory shall be used for runtime file system objects such
as AF_UNIX sockets, FIFOs, PID files and similar. It is
guaranteed that this directory is local and offers the
greatest possible file system feature set the operating system
provides. For further details, see the XDG Base Directory
Specification[3]. $XDG_RUNTIME_DIR is not set if the current
user is not the original user of the session.
*/
envXDGRuntimeDir = "XDG_RUNTIME_DIR"
/*
The session class. This may be used instead of class= on the
module parameter line, and is usually preferred.
*/
envXDGSessionClass = "XDG_SESSION_CLASS"
/*
A regular interactive user session. This is the default class
for sessions for which a TTY or X display is known at session
registration time.
*/
xdgSessionClassUser = "user"
/*
The session type. This may be used instead of type= on the
module parameter line, and is usually preferred.
One of "unspecified", "tty", "x11", "wayland", "mir", or "web".
*/
envXDGSessionType = "XDG_SESSION_TYPE"
)
func init() { gob.Register(new(spRuntimeOp)) }
const (
sessionTypeUnspec = iota
sessionTypeTTY
sessionTypeX11
sessionTypeWayland
)
// spRuntimeOp sets up XDG_RUNTIME_DIR inside the container. // spRuntimeOp sets up XDG_RUNTIME_DIR inside the container.
type spRuntimeOp struct{} type spRuntimeOp struct {
// SessionType determines the value of envXDGSessionType. Populated during toSystem.
SessionType uintptr
}
func (s spRuntimeOp) toSystem(state *outcomeStateSys) error { func (s *spRuntimeOp) toSystem(state *outcomeStateSys) error {
runtimeDir, runtimeDirInst := s.commonPaths(state.outcomeState) runtimeDir, runtimeDirInst := s.commonPaths(state.outcomeState)
state.sys.Ensure(runtimeDir, 0700) state.sys.Ensure(runtimeDir, 0700)
state.sys.UpdatePermType(system.User, runtimeDir, acl.Execute) state.sys.UpdatePermType(system.User, runtimeDir, acl.Execute)
state.sys.Ensure(runtimeDirInst, 0700) state.sys.Ensure(runtimeDirInst, 0700)
state.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute) state.sys.UpdatePermType(system.User, runtimeDirInst, acl.Read, acl.Write, acl.Execute)
if state.et&hst.EWayland != 0 {
s.SessionType = sessionTypeWayland
} else if state.et&hst.EX11 != 0 {
s.SessionType = sessionTypeX11
} else {
s.SessionType = sessionTypeTTY
}
return nil return nil
} }
func (s spRuntimeOp) toContainer(state *outcomeStateParams) error { func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error {
const (
xdgRuntimeDir = "XDG_RUNTIME_DIR"
xdgSessionClass = "XDG_SESSION_CLASS"
xdgSessionType = "XDG_SESSION_TYPE"
)
state.runtimeDir = fhs.AbsRunUser.Append(state.mapuid.String()) state.runtimeDir = fhs.AbsRunUser.Append(state.mapuid.String())
state.env[xdgRuntimeDir] = state.runtimeDir.String() state.env[envXDGRuntimeDir] = state.runtimeDir.String()
state.env[xdgSessionClass] = "user" state.env[envXDGSessionClass] = xdgSessionClassUser
state.env[xdgSessionType] = "tty"
switch s.SessionType {
case sessionTypeUnspec:
state.env[envXDGSessionType] = "unspecified"
case sessionTypeTTY:
state.env[envXDGSessionType] = "tty"
case sessionTypeX11:
state.env[envXDGSessionType] = "x11"
case sessionTypeWayland:
state.env[envXDGSessionType] = "wayland"
}
_, runtimeDirInst := s.commonPaths(state.outcomeState) _, runtimeDirInst := s.commonPaths(state.outcomeState)
state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755) state.params.
state.params.Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable) Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable)
return nil return nil
} }
func (s spRuntimeOp) commonPaths(state *outcomeState) (runtimeDir, runtimeDirInst *check.Absolute) { func (s *spRuntimeOp) commonPaths(state *outcomeState) (runtimeDir, runtimeDirInst *check.Absolute) {
runtimeDir = state.sc.SharePath.Append("runtime") runtimeDir = state.sc.SharePath.Append("runtime")
runtimeDirInst = runtimeDir.Append(state.identity.String()) runtimeDirInst = runtimeDir.Append(state.identity.String())
return return

View File

@ -17,9 +17,47 @@ func TestSpRuntimeOp(t *testing.T) {
config := hst.Template() config := hst.Template()
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"success", func(bool, bool) outcomeOp { {"success zero", func(isShim bool, clearUnexported bool) outcomeOp {
return spRuntimeOp{} if !isShim {
}, hst.Template, nil, []stub.Call{ return new(spRuntimeOp)
}
op := &spRuntimeOp{sessionTypeTTY}
if clearUnexported {
op.SessionType = sessionTypeUnspec
}
return op
}, func() *hst.Config {
c := hst.Template()
*c.Enablements = 0
return c
}, nil, []stub.Call{
// this op configures the system state and does not make calls during toSystem
}, newI().
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute), nil, nil, insertsOps(nil), []stub.Call{
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable),
}, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user",
"XDG_SESSION_TYPE": "unspecified",
}, nil), nil},
{"success tty", func(isShim, _ bool) outcomeOp {
if !isShim {
return new(spRuntimeOp)
}
return &spRuntimeOp{sessionTypeTTY}
}, func() *hst.Config {
c := hst.Template()
*c.Enablements = 0
return c
}, nil, []stub.Call{
// this op configures the system state and does not make calls during toSystem // this op configures the system state and does not make calls during toSystem
}, newI(). }, newI().
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700). Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700).
@ -36,5 +74,55 @@ func TestSpRuntimeOp(t *testing.T) {
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
"XDG_SESSION_TYPE": "tty", "XDG_SESSION_TYPE": "tty",
}, nil), nil}, }, nil), nil},
{"success x11", func(isShim, _ bool) outcomeOp {
if !isShim {
return new(spRuntimeOp)
}
return &spRuntimeOp{sessionTypeX11}
}, func() *hst.Config {
c := hst.Template()
*c.Enablements = hst.Enablements(hst.EX11)
return c
}, nil, []stub.Call{
// this op configures the system state and does not make calls during toSystem
}, newI().
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute), nil, nil, insertsOps(nil), []stub.Call{
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable),
}, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user",
"XDG_SESSION_TYPE": "x11",
}, nil), nil},
{"success", func(isShim, _ bool) outcomeOp {
if !isShim {
return new(spRuntimeOp)
}
return &spRuntimeOp{sessionTypeWayland}
}, hst.Template, nil, []stub.Call{
// this op configures the system state and does not make calls during toSystem
}, newI().
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), 0700).
UpdatePermType(system.User, m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute), nil, nil, insertsOps(nil), []stub.Call{
// this op configures the container state and does not make calls during toContainer
}, &container.Params{
Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable),
}, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user",
"XDG_SESSION_TYPE": "wayland",
}, nil), nil},
}) })
} }

View File

@ -46,7 +46,7 @@ in
"WAYLAND_DISPLAY=wayland-0" "WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/65534" "XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user" "XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty" "XDG_SESSION_TYPE=wayland"
]; ];
fs = fs "dead" { fs = fs "dead" {

View File

@ -54,7 +54,7 @@ in
"WAYLAND_DISPLAY=wayland-0" "WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/1000" "XDG_RUNTIME_DIR=/run/user/1000"
"XDG_SESSION_CLASS=user" "XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty" "XDG_SESSION_TYPE=wayland"
]; ];
fs = fs "dead" { fs = fs "dead" {

View File

@ -54,7 +54,7 @@ in
"WAYLAND_DISPLAY=wayland-0" "WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/65534" "XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user" "XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty" "XDG_SESSION_TYPE=wayland"
]; ];
fs = fs "dead" { fs = fs "dead" {

View File

@ -54,7 +54,7 @@ in
"WAYLAND_DISPLAY=wayland-0" "WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/65534" "XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user" "XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty" "XDG_SESSION_TYPE=wayland"
]; ];
fs = fs "dead" { fs = fs "dead" {

View File

@ -55,7 +55,7 @@ in
"WAYLAND_DISPLAY=wayland-0" "WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/65534" "XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user" "XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty" "XDG_SESSION_TYPE=wayland"
]; ];
fs = fs "dead" { fs = fs "dead" {