Skip to main content
Topic: dbus-controller-script: experimental dbus-broker-units for Artix (Read 1325 times) previous topic - next topic
0 Members and 2 Guests are viewing this topic.

dbus-controller-script: experimental dbus-broker-units for Artix

Nobody (except me and a random Italian guy I found on Github, apparently) asked for this, but here goes an experimental attempt to run dbus-broker as the main D-Bus implementation on Artix.



dbus-controller-script, a generic non systemd spawner for dbus-broker.

Why
dbus-broker doesn't technically depend on systemd. It's even packaged for Artix (world/dbus-broker).

It's almost completely useless, however, without a "launcher" program to set it up (give it a socket to listen on, tell it which services are available, etc.). The included launcher program (the one powering Arch's dbus-broker-units package and Fedora) is heavily reliant on not only systemd libraries, but on systemd's APIs and behaviors. Not even building it against libelogind helps, unless you patch out a few files, but the result is worse than running the stock dbus-daemon (as Artix currently does), especially for user services, since you lose the ability to have programs start services on-demand⁰.

The official developer answer is to build your own launcher if on-demand service starting is needed. This is an attempt to provide a sort of 'meta launcher' that can be used on any init system.

How
It's entirely configured through files present on its working directory and (extremely long) command lines. It's very much inspired by how s6 and runit's tools work, but it will work on any init system. Really. As I said in the readme, remove the chpst from the runit example file, and it becomes an init-agnostic launcher script for root/system bus.

I've provided examples for all init systems I'm familiar with in the contrib folder. For a thorough explanation, there's the main README.

Caveats in general
  • D-Bus policy is not loaded, because the developer's documentation reserves the right to change how the "launcher" and dbus-broker negotiate that at any moment. Not that relevant in desktop systems, though, since the permission-sensitive services rely on polkit anyhow.
  • This is not specialized on any given init system; it uses script snippets instead. Yes, it's technically slower and it makes the functionality extremely coarse, but it's the only way to interface with daemontools-style inits (s6/runit), and even init systems with very different approaches (like dinit) provide control commands that can be used.

Init system support:
  • System bus (root service)⁰:
    • OpenRC: untested and not packaged, but see the "edit the runit file" tip
    • Runit: tested on Gentoo³, not packaged
    • s6: tested and packaged on Artix
    • dinit: tested on Gentoo³, packaged for Artix
  • User services: tested and packaged only for Turnstile + dinit³

Artix-specific Caveats
  • Unless you don't rely that much on D-Bus anyway, you'll need to manually fix some packaged scripts. I'd recommend not trying this at all if you use a "normal" desktop environment (GNOME, MATE, XFCE, KDE, etc.) and are not willing to troubleshoot issues.
    • Especially for complex desktops, Artix relies way too much on automagical D-Bus service starting. I had to manually create some system services since they are not packaged. Without a polkitd (tl;dr run /usr/lib/polkit-1/polkitd) service, you won't be able to run rootless Xorg/Wayland and to use any DE's power switches. File managers also constantly complain about (e.g.) udisks2 not being able to be magically spawned.
    • The packaged service definitions for wpa_supplicant are missing the -u switch, which makes Network Manager unable to communicate with it. It should be added to the service definition until this is fixed.¹
  • For GUI sessions, some sort of mechanism must be in place to get the DISPLAY and WAYLAND_DISPLAY environment to the service manager. AFAIK there's currently a snippet that does this for Xorg, but not yet for any Wayland compositors.²
  • Other the PipeWire trio, basically no user services are packaged in Artix. The included dinit file works around this by generating dinit files upon boot, but ideally Artix would be like Chimera and provide packaged scripts for things like notification daemons, bluetooth managers, etc.


⁰Despite me complaining about missing on-demand service start, the included definitions for the root/system bus do not have on demand start (sorry for being a hypocrite :P). In addition to being hard to implement properly for s6, not all root/system bus services are packaged yet, e.g. polkit and udisks2. It would also require some help from the distro; for instance, some packages remove the SystemdService= entry from the D-Bus .service files, which, ironically, could help here by telling the activation script which "real" service is equivalent to the the name in D-Bus' org.domain.Application format. At least, it eliminates once and for all the possibility of dbus magically starting elogind before the init system.
¹I'd consider this a packaging bug because it means you need to disable the stock wpa_supplicant service and have NetworkManager have D-Bus magically spawn one with a -u switch.
²Turnstile's git master now includes a graphical.target service that waits until these environment variables are present. When that version hits Artix, it might be helpful to add it to as an waits-for =/depends-on = to your services.
³I wrote most of this project back when I was using a extremely "suckless" Gentoo system (no polkit, no elogind, no udev...). This is its first foray on a "big boy" distro running a "big boy" graphical environment (Artix KDE, to be specific). The s6rc-subdir setup under contrib is how I used to run user services there, but it is not easily packaged for Artix.

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #1
Very cool work on doing this. I'll try to explore this more one of these days.

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #2
Heh. Just the other day there was a private discussion regarding how would we handle dbus launchers in system services and user services. Great work, but at least for dinit-for-Artix (and from a personal standpoint), I'd like to address some points:
  • Especially for complex desktops, Artix relies way too much on automagical D-Bus service starting. I had to manually create some system services since they are not packaged. Without a polkitd (tl;dr run /usr/lib/polkit-1/polkitd) service, you won't be able to run rootless Xorg/Wayland and to use any DE's power switches. File managers also constantly complain about (e.g.) udisks2 not being able to be magically spawned.
I've been tempted to change some of our dbus service scripts so it executes dinitctl start <service>. At least on my device, I do patch /usr/share/dbus-1/system-services/org.freedesktop.login1.service to execute /bin/dinitctl start --quiet elogind like what Chimera does (IIRC). However, the problem is in packaging since Artix supports multiple inits (other distros which only uses Dinit should be fine).

w.r.t. polkit, I guess the problem is the same, packaging. We can probably make init packages for polkit, but the dependencies have to be reversed (i.e. polkit depends on init-polkit) since polkit is almost always launched by dbus instead of standalone, but I'm not well-versed enough in pacman and PKGBUILD and afraid there will be conflicts between inits.

  • Other the PipeWire trio, basically no user services are packaged in Artix. The included dinit file works around this by generating dinit files upon boot, but ideally Artix would be like Chimera and provide packaged scripts for things like notification daemons, bluetooth managers, etc.
I think this needs to be addressed on a case-by-case basis. I do provide mpd user service and syncthing now, but even in your screenshots there are some services which would be... unrealistic (for a lack of a better word) to add the init package (e.g. org.freedesktop.portal.Desktop). I'd like to keep this ad-hoc and your examples in contrib already work wonderfully here.

Either way, one of these days I'll have a go with this. Thanks!
now only the dinit guy in artix

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #3
If I may @konimex, quick side question from a dinit and seatd user (no polkit, no elogind), what could ever happen if auto-starting polkit or elogind system wise (I don't run user services) and they are not present?  --quiet supresses output except for errors, and I guess there would be errors.  Would there be breakages, or the inner service call wouldn't gate anything from where it was called?

@capezotte, if not using turnstile I guess one could launch the dbus service on contrib, and then call exec dbus-run-session <wm_of_choice> on xinitrc I guess, right?  That if one could be able to get rid of the dbus current system auto-launching stuff.  Or you foresee this only working with an user service provided by turnstile or dinit-user-spawn as examples?  I'm planning to still avoid user services triggered on user login.

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #4
I forgot to clarify the elogind "patch" is for my personal system. That being said,
what could ever happen if auto-starting polkit or elogind system wise (I don't run user services) and they are not present?  --quiet supresses output except for errors, and I guess there would be errors.  Would there be breakages, or the inner service call wouldn't gate anything from where it was called?
I think dbus would just fail the bus and won't attempt to launch it until it is polled again by some other binary.
now only the dinit guy in artix

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #5
I see @konimex.  Thanks a lot !

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #6
I've been tempted to change some of our dbus service scripts so it executes dinitctl start <service>. At least on my device, I do patch /usr/share/dbus-1/system-services/org.freedesktop.login1.service to execute /bin/dinitctl start --quiet elogind like what Chimera does (IIRC). However, the problem is in packaging since Artix supports multiple inits (other distros which only uses Dinit should be fine).

In principle, they could rely on a small script/executable on a standard path (say, under /usr/lib) that runs/execs dinitctl start --quiet $1/s6-rc -v0 start $1, etc. depending on the running init system. This file could be a part of each init's rc package.

One problem I see with this, though, is that s6-rc doesn't like two attempts to start/stop services happening at the same time (either it fails, or it risks deadlocks). Both my fork's contrib/s6rc-subdir and the original implementation's dbus-controller-s6 effectively run a parallel instance of s6 without dependency management (more like a miniature runit) specifically for D-Bus services to work around this.

Quote
w.r.t. polkit, I guess the problem is the same, packaging. We can probably make init packages for polkit, but the dependencies have to be reversed (i.e. polkit depends on init-polkit) since polkit is almost always launched by dbus instead of standalone, but I'm not well-versed enough in pacman and PKGBUILD and afraid there will be conflicts between inits.

I think the earliest and most important service that relies on Polkit on most people's systems is elogind. If I'm right, having polkit-$init install itself as a dependency for elogind-$init could help keep polkit always within control of the init system. s6 natively works with dependencies.d directories, so polkit-s6 could add itself to /etc/s6/sv/elogind/dependencies.d/. man dinit-service mentions a depends-on.d= directive, which seems to enable a similar packaging workflow. elogind-runit could, I dunno, [
! -d ../polkit ] || sv start polkit || exit 1
or something. Not sure if this is doable for OpenRC.

One caveat is that polkit-s6 would depend on elogind-s6, since s6-rc complains if it finds incomplete service definitions (the polkit-s6 package by itself would create an empty directory for elogind except for dependencies.d).

Quote
I think this needs to be addressed on a case-by-case basis. [...] I'd like to keep this ad-hoc and your examples in contrib already work wonderfully here.

After taking a better look at how many dbus service files in my system there are — especially for minor one-off things, like the DE system bus services that basically install fonts — yes, not relying on ad-hoc service generation is too much work. However, some sort of mechanism must be in place to recognize when a human written service is present. Still thinking of whether reusing the SystemdService entry is the best way.

I've just tweaked the contrib dinit package so it relies on a parametrized [email protected] service that reads the corresponding dbus-1/service/org.domain.Application.service file and executes it. This would forego the dependency on Turnstile for the current dinit user setup, be easier to package, and be easily adapted to a system bus version.

I'm not if other init systems would deal well with this, though, or if developers would consider this use parametrized services too, for want of a better word, "evil". This also loses dependency information, but that's not too bad since D-Bus services tend to depend mostly on other D-Bus services (and otherwise a handwritten service can be provided).

if not using turnstile I guess one could launch the dbus service on contrib, and then call exec dbus-run-session <wm_of_choice> on xinitrc I guess, right?  That if one could be able to get rid of the dbus current system auto-launching stuff.  Or you foresee this only working with an user service provided by turnstile or dinit-user-spawn as examples?  I'm planning to still avoid user services triggered on user login.

Remember there are two D-Bus processes on a desktop machine:

  • the system bus, used by services running as root or dedicated machine-generated users, such as cups, bluetoothd, elogind, polkit, avahi, etc., which is spawned by PID 1
  • the session bus, used by normal graphical programs, which is spawned by user service manager or by the scripts that set up GUI sessions (such as your .xinitrc, or DMs)

Replacing /etc/dinit.d/dbus on your system with contrib/dinit-systembus-noactivation/dbus will indeed prevent autostarting of system services outside of the init system, because it will replace the definition of the service that starts the system bus.

It won't do anything with desktop/user services, because dbus-run-session spawns a normal dbus-daemon that will autostart services. If you want to prevent that by using this project instead you'll need to create a replacement. It must be a script that, in short, exports the user D-Bus connection location, runs a command line containing dbus-controller-script on the background, runs your WM, then kills the background session bus after the WM exits.

Or just take the suggested Turnstile file and change the working-dir= to an empty folder to disable the helper scripts that reimplements autostarting, and use it with either user service solution. They'll do all three* things your script would need to do.

*dinit-user-spawn doesn't export the D-Bus connection location yet, but you can just add export DBUS_SESSION_BUS_ADDRESS="unix:path=$XDG_RUNTIME_DIR/bus" to the .xinitrc in the meantime.

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #7
One problem I see with this, though, is that s6-rc doesn't like two attempts to start/stop services happening at the same time (either it fails, or it risks deadlocks). Both my fork's contrib/s6rc-subdir and the original implementation's dbus-controller-s6 effectively run a parallel instance of s6 without dependency management (more like a miniature runit) specifically for D-Bus services to work around this.
So what happens if a D-Bus-invoked service conflicts with existing services if it's run in parallel?

I think the earliest and most important service that relies on Polkit on most people's systems is elogind. If I'm right, having polkit-$init install itself as a dependency for elogind-$init could help keep polkit always within control of the init system. s6 natively works with dependencies.d directories, so polkit-s6 could add itself to /etc/s6/sv/elogind/dependencies.d/. man dinit-service mentions a depends-on.d= directive, which seems to enable a similar packaging workflow. elogind-runit could, I dunno, [
! -d ../polkit ] || sv start polkit || exit 1
or something. Not sure if this is doable for OpenRC.
elogind can be run without polkit though, and in most cases, elogind is started before polkit (at least, 6 seconds earlier based on my last boot), otherwise polkit would be in [system]. Though we can add some services to depend on polkit such as rtkit or upower (though in its current form, it would not be launched by dbus).

After taking a better look at how many dbus service files in my system there are — especially for minor one-off things, like the DE system bus services that basically install fonts — yes, not relying on ad-hoc service generation is too much work. However, some sort of mechanism must be in place to recognize when a human written service is present. Still thinking of whether reusing the SystemdService entry is the best way.
If we want to maintain compatibility, I think reusing SystemdService= is the best way since we have no systemd anyway so it won't be used for anything else. Unless we can create ArtixService= without dbus-broker/dbus complaining that this is not in the DBus specification.

I'm not if other init systems would deal well with this, though, or if developers would consider this use parametrized services too, for want of a better word, "evil". This also loses dependency information, but that's not too bad since D-Bus services tend to depend mostly on other D-Bus services (and otherwise a handwritten service can be provided).
At the very least, this "evil" template should have a depend-on: dbus since it's invoked by dbus anyway, and if there are dependencies, it will be launched by dbus, if I'm correct.
now only the dinit guy in artix

Re: dbus-controller-script: experimental dbus-broker-units for Artix

Reply #8
So what happens if a D-Bus-invoked service conflicts with existing services if it's run in parallel?

If a service running outside the nested (I now realize "parallel" was a poor choice of words) s6 supplies the org.domain.Application, a request to start a conflicting service can't happen (for this same reason, I think conflicts within the generated services are unlikely). If it's not running, a conflict happens and the packaged/user chosen service will fail to start if called.

To avoid this, service-start and/or populate-services should try to find it, though this is not implement in the contrib sample, because of the issues with automated calls to s6-rc, and it'd require knowing what conventions the "outside" follows. That, or outside D-Bus services become oneshots communicating with the nested s6, but that squanders some of s6-rc's usefulness and makes status-querying scripts less useful unless they're aware of this arrangement.

I'd like to be wrong and underestimating s6-rc...

Quote
elogind can be run without polkit though, and in most cases, elogind is started before polkit (at least, 6 seconds earlier based on my last boot), otherwise polkit would be in [system].

That's why my idea was to have the dependency be added by the polkit-$init package, if possible, and the suggest change to runit doesn't try to start polkit if it's absent.

I think reversing the order won't be an issue. Works on my machine(tm), and given how D-Bus autostarting works, we'd expect Y being started by X to mean X depends (or can depend) on Y, not the other way around.

Quote
If we want to maintain compatibility, I think reusing SystemdService= is the best way since we have no systemd anyway so it won't be used for anything else.

Yeah,. Having to trim out .service isn't a huge deal, and it can be removed from the file if starting the service ad-hoc is preferrable.

Quote
At the very least, this "evil" template should have a depend-on: dbus since it's invoked by dbus anyway, and if there are dependencies, it will be launched by dbus, if I'm correct.

Good catch. I'll add that to the template.