An account's manager runs only while it is logged in or lingers. A user-scoped unit whose manager is not running is now "waiting" rather than failed, its record kept as it was; its removal is never fatal (kept recorded, retried) and an account that is gone is forgotten. Whether the manager runs is asked of user@<uid>.service in the machine's manager: asking the account's own, through --machine, logs it in. The user shape gains `linger`, set with loginctl, read back from logind's record, and given back on removal like the shell. Unit files the mesh writes under ~/.config/systemd/user or /etc/systemd/user make that unit the mesh's, and made, holds and found units are keyed by manager and name, so an account's unit and the machine's of one name are two units. A service moved between managers gives the old one back through the manager it was in. OpenRC refuses both.
50 lines
1.7 KiB
Go
50 lines
1.7 KiB
Go
package declaration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// novox/hq ADR 0177: a unit is in the system manager or an account's own; a user-scoped one names
|
|
// the account, a system one may not, and any other word is refused.
|
|
func TestAUserScopedUnitNamesItsAccountAndASystemOneMayNot(t *testing.T) {
|
|
cases := []struct{ scope, user, wants string }{
|
|
{"user", "ops", ""},
|
|
{"", "", ""},
|
|
{"system", "", ""},
|
|
{"user", "", "names the account"},
|
|
{"", "ops", "names no user"},
|
|
{"session", "ops", "scope \"session\""},
|
|
}
|
|
for _, c := range cases {
|
|
s := &Service{ID: "m.u", Type: TypeService, Unit: "u.service", State: "running", Scope: c.scope, User: c.user}
|
|
problems := s.validate("m.u", false)
|
|
got := strings.Join(problems, "; ")
|
|
if c.wants == "" && len(problems) != 0 {
|
|
t.Fatalf("scope %q user %q refused: %s", c.scope, c.user, got)
|
|
}
|
|
if c.wants != "" && !strings.Contains(got, c.wants) {
|
|
t.Fatalf("scope %q user %q: wanted a refusal saying %q, got %q", c.scope, c.user, c.wants, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0177: lingering is a field of the account, absent meaning nothing asserted.
|
|
func TestAnAccountMayBeDeclaredToLinger(t *testing.T) {
|
|
d, err := Parse([]byte(`{"declaration":1,"resources":[{"id":"m.login","type":"user","name":"ops","linger":true}]}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
u, ok := d.Resources[0].(*User)
|
|
if !ok || u.Linger == nil || !*u.Linger {
|
|
t.Fatalf("linger not read: %+v", d.Resources[0])
|
|
}
|
|
d, err = Parse([]byte(`{"declaration":1,"resources":[{"id":"m.login","type":"user","name":"ops"}]}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if u := d.Resources[0].(*User); u.Linger != nil {
|
|
t.Fatal("an account that said nothing about lingering asserts it")
|
|
}
|
|
}
|