A user unit waits for its account's manager, and lingering is the account's (hq ADR 0177)
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.
This commit is contained in:
@@ -374,6 +374,15 @@ type User struct {
|
||||
// Home directory. Absent means the system's default for a new user, and is not changed for
|
||||
// one that exists — moving somebody's home is not something a declaration should do quietly.
|
||||
Home string `json:"home,omitempty"`
|
||||
|
||||
// Linger is whether the account's own service manager runs with nobody logged in (novox/hq ADR
|
||||
// 0177). A user-scoped unit lives in that manager, and the manager runs only from the account's
|
||||
// first login to its last logout — so a server's user unit, where nobody ever logs in, never
|
||||
// runs without it, and a workstation's runs only while its person is there, which on a desktop
|
||||
// is what is wanted. Absent asserts nothing, as Shell's does: true has the account linger, false
|
||||
// has it not. On the account rather than on the unit, because it is the account's: two units of
|
||||
// one account cannot disagree about it, and undeclaring one of them must not stop the other.
|
||||
Linger *bool `json:"linger,omitempty"`
|
||||
}
|
||||
|
||||
// Network is a named network on this machine.
|
||||
|
||||
@@ -28,3 +28,22 @@ func TestAUserScopedUnitNamesItsAccountAndASystemOneMayNot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user