r/artixlinux • u/username12312355 • 3d ago
s6 I made systemd --user replacement with s6
Links:
Installation guide
elogind-usersv
s6-user
I've been working on a way to have proper per-user services, like with systemd --user. The ones that start when you login, and stop when you logout, not just desktop autostart scripts that start/stop god knows when, and aren't even supervised. It's kind of a waste, having a fucking s6 in your system and not use it for actual process supervision.
The problems with the Artix s6 setup are:
s6-svscansupervises user services, elogind handles login/logout and the runtime directory (/run/user/$UID). elogind doesn't (and shouldn't) spawn s6-svscan or ensure it stays running.s6-frontendprovides thes6 --usercommand, but it still doesn't distinguish persistent user services from system services, only live ones.
There are 2 existing solutions to problem 1:
Turnstile- a login manager, but it can also run alongside elogind, with the runtime directory left to elogind. In that setup it does what I want:
- On login it spawns a per-user service manager, and its PAM blocks the login until the backend reports ready.
- It has a simple backend protocol, so any service manager works once you write a backend for it.
- It keeps the runtime directory alive for the manager's lifetime by holding an elogind session open (via
pam_elogindin turnstiled's PAM stack). So even after the user logs out of their last real session, elogind still sees a session and won't wipe/run/user/$UIDuntil the manager has gracefully finished, with a timeout.
That's basically everything I want from a per-user service manager launcher. The dealbreaker is: turnstile also counts user sessions itself, with its own PAM, so it becomes a second source of truth alongside elogind. If elogind or turnstile crashes, their counters desync, and you get either a stale session held by turnstile, or turnstile stopping the manager because it thinks nobody's logged in, while elogind still has an active session, leaving that user without a service manager.
Userspawn- depends on elogind, listens to its DBus, spawns and kills per-user service managers. Very minimal, but doesn't suit me, because it doesn't make the user wait until the service manager reports ready, and it doesn't gracefully shut the service manager down, it just kills it.
That's why I made elogind-usersv. It has the best of both worlds: it prevents login until the service manager reports ready (with PAM), it has a backend protocol similar to turnstile's for reporting ready and for graceful shutdown, and it relies on elogind for user session counting. It's service-manager agnostic, and only requires a simple backend.
Now the s6-frontend problem. Currently s6 --user command doesn't distinguish user paths from system paths, so s6-user is a temporary workaround to make it kinda usable. It's a thin wrapper that pins the user tree to persistent per-user paths and stops the s6 --user command from ever selecting the system /etc/s6/repo and system stores. It's non-resident: it normalizes HOME/XDG bases, reads its own TOML config, sets explicit repodir/bootdb/storelist, then execs the real s6. No supervision logic of its own. So s6-user is just like calling s6 --user, except it always addresses your per-user services instead of the system ones.
What I want from this
This is all work in progress(works on my machine though), and I'd love feedback and contributions, especially from people who know elogind/PAM and s6 internals. Bug reports, backend implementations for other service managers, testing on your own setup, all welcome.
The bigger goal behind these projects: make non-systemd distros able to run any software, even stuff that assumes systemd is there. The current milestone I'm working toward is getting the latest GNOME running on Artix with s6. If that interests you, or you've hit the same walls, get in touch or open an issue on the repos.
2
u/talksickwalkquick dinit 2d ago
If you're trying to keep elogind as the single source of truth for runtime directories (/run/user/$UID), have you considered bypassing Turnstile's independent session counting and triggering the per-user s6svs instance directly via a minimal pam_exec module on session open/close? That way elogind owns the session lifecycle entirely, and PAM handles launching/teardown strictly tied to the actual user session logons without maintaining a secondary session counter. I actually made my own pam modules just to troubleshoot an issue I was having with screen locking.
2
u/username12312355 2d ago
That’s pretty much what it does, though lifecycle is not driven by pam_exec open/close:
- There is no dependency on Turnstile, only elogind.
- elogind-usersv doesn't independently count sessions. It gets each user’s session set from elogind, counts it, and starts/stops the per-user manager on the zero/nonzero transition. That happens on PAM open and elogind's SessionNew/SessionRemoved signals.
- PAM open gates login until the manager is ready. PAM close is no-op(no reason to gate logout).
2
u/jolune 3d ago edited 2d ago
That sound really nice. I'm on NixOS now, but maybe I could try this on sixos on a VM.
It's a lot of work though. And my config is full of flakes.
Anyway, good luck to you. Excellent work.