Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Services 2.0 #723

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open

User Services 2.0 #723

wants to merge 12 commits into from

Conversation

navi-desu
Copy link
Member

with a proper branch now

add two api functions, `rc_service_dir` and `rc_sysconf_dir`, both are
generate paths (and sub-paths) for resources, and meant to replace the
hardcoded variables like `RC_SVCDIR`.

those functions differ by dynamically switching between the system path,
or the user path, set in their home folder or runtime directory.

this lays out the intial support for user services.

Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
adds a new multiplexed script for starting user sessions. it also sets
up XDG_RUNTIME_DIR.

Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
use the realpath of the target to locate which sysconf dir it's in,
using it as a base to load the config file. the service path is then
normalized from the symlink without dereferencing it.

Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
it's created in /run/openrc/dynamic and linked to the service in /etc at
login.

Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
some services might expect to be in home, and may behave unexpectedly
for the user, e.g. any program started via dbus, and this matches
systemd-user behaviour.

Signed-off-by: Anna (navi) Figueiredo Gomes <[email protected]>
@WhyNotHugo
Copy link

Previous discussion: #573

@cnt0
Copy link

cnt0 commented Sep 9, 2024

Hello, I didn't review this PR (yet?) so I have no idea what's going on inside at all, however, I'd like to describe my setup so maybe you'll find my thoughts useful and implement it here.

So, I came to conclusion that there are in fact 2 kinds of user services:

  1. DE-independent, the ones I'd like to launch in any case, for any DE and even if there's just shell. Example: pipewire
  2. DE-dependent, the ones relying on environment variables, sockets and the like set by your DE. Example: various panels, widgets and the like. yambar, wlsunset.

For DE-dependent services, there's a problem that if DE process is not their ancestor then you'll need to pass al the required environment variables by hand. Systemd do exactly that by using command systemctl import-environment, but IMO this is very annoying and error-prone cause there's no way to know which variable you'll need right now or in the future. Not to mention synchronization. The other way is to define such variables in e.g. ~/.profile and the source this file. This is inconvenient as well, because you won't be able to define DE-specific variables this way.

So DE-dependent services must have DE as their ancestor. This way we will exterminate the problem of environment variables for good.

The 2nd problem of DE-dependent services is that they can depend on DE-independent ones. For example, my yambar panel has a volume widget which obtains this value from pipewire, so it naturally depends on pipewire.

So I'm launching 2 s6-svscan instances: one with DE-independent services via turnstile, another with DE-dependent services from within sway config. So they have access to all the required environment variables, they can depend on each other

$ cat ~/.config/s6/sway/yambar/run
#!/bin/bash -e
s6-svwait ~/.config/s6/user/pipewire
yambar

and in general, everything is very nice, simple and works very well.

So IMO for openrc user session to be useful, it should (ideally) be able to be started multiple times and provide a public API to wait for services or (systemd-like) have a mechanism of injecting environment variables like systemctl import-environment.

@WhyNotHugo
Copy link

Regarding compositor-dependant services, there are a few other approaches worth mentioning (and many other which I don't think would add any value here):

Start the service manager as a child of the compositor

This is by far the simplest approach, and I doubt there's anything simpler than this. Run the service manager as a child of the compositor. E.g.: via exec in sway's config. The service manager will inherit all necessary variables.

The main difference is that if I pick zsh as a shell instead of sway when logging in, user services won't start. For me, this is perfectly fine. If I'm logging into zsh, it's usually on a second tty to debug something that I've broken for my main session.

If you expect pipewire or some other service when logging into a console, you can configure .zprofile to start the service manager with a different runlevel. For this use case, I'd say this approach starts growing some complexity and is not as ideal.

Run the compositor as a service

Running the compositor itself as a service is a pretty straightforward of ordering dependencies. You need some mechanism to save relevant environment variables into a file, and signal readiness. Usually a tiny helper executed by the compositor is enough.

You dependant service scripts (e.g.: yambar) would depend on the compositor service and load those previously saved files with the environment variables.

There are no obvious downsides to this approach, and it results in much tighter integration between relevant parts (without any unnecessary coupling). Restarting the compositor is possible, and dependant services are properly restarted.

This works especially well with s6, where s6-svscan will be the main process invokes during login and that lives for the entire session. You'll end up with a service tree that's a pretty good reflection of your mental model of services.

This approach is likely to have issues if the service manager is started by root and not part of the user's session. In such a scenario, because the compositor is not part of the session, I believe that seatd won't grant the compositor access input/output devices.

Exporting compositor variables into a file

When the compositor starts, the relevant environment variables are stored in a file. The run scripts for dependant services load these environment variables. This part is the same as above.

In this case, the compositor doesn't run as a service, and is started via some other mechanism. When the compositor is ready, it starts a second runlevel. Services which depend on the compositor are tied to this runlevel.

@navi-desu
Copy link
Member Author

the "recommended" way here would be to switch to a runlevel while inside your compositor's session. openrc does not have a deamon of any kind to import environments to, but when starting a service it does pick up the current environment (and filters it)

so, my current workflow with that is
add rc_env_allow="WAYLAND_DISPLAY" to ~/.config/openrc/rc.conf
log in, pam will launch pam_openrc which launches the default runlevel, starting the DE-independent services (pipewire, dbus)
on my sway config, i exec openrc --user gui, which now will have access to the WAYLAND_DISPLAY and thus launch dunst and alike

openrc just using the current environment is both more convenient but also a bit more error prone than systemd's approach, but i think it works quite well for us

i do not recommend you run your compositor as a service at all. if you use pam_openrc, it would start during the pam auth sequence, not in a proper session, and as soon as you quit it, the login process would continue and then drop you into a shell or similar

and even if you don't use pam, and instead launch openrc manually, you're also starting the compositor inside openrc's environment, meaning filtered envs, so things from .profile/.bash_profile/.zshenv won't be picked up, among other things

@WhyNotHugo
Copy link

Passing (filtered) environment variables when starting the runlevel seems like a pretty sensible solution.

I don't think a service manager needs an "import environment" anyway. If services need environment variables, they should be provisioned via their run script.

i do not recommend you run your compositor as a service at all. if you use pam_openrc, it would start during the pam auth sequence, not in a proper session, and as soon as you quit it, the login process would continue and then drop you into a shell or similar

This is a limitation of having root enforce execution of the service manager instead of users executing it themselves. The core of the issue here is involving pam in the execution of the user's services themselves.

@navi-desu
Copy link
Member Author

This is a limitation of having root enforce execution of the service manager instead of users executing it themselves. The core of the issue here is involving pam in the execution of the user's services themselves.

that is true for pam, but even non pam, readiness status for compositors doesn't work well, and there's many moments it can get messes up by being inside openrc's service running enviroment

it could work, but idk if it's worth it considering possible weirdnesses (having to background the compositor's process and all that)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants