dunst-project / dunst

Lightweight and customizable notification daemon
https://dunst-project.org
Other
4.62k stars 342 forks source link

Figure out how to handle running dunst as a systemd service #314

Closed tsipinakis closed 7 years ago

tsipinakis commented 7 years ago

In a comment @nmschulte mentioned that the dbus service file fails to launch dunst if the service file is not installed.

This only happens if dbus is started with the --systemd-activation flag and it looks like in that case if the service file is not found it ignores the Exec line. We should either

As an additional sidenote that is loosely within the scope of this issue, @mathstuf linked an issue in the same thread that apparently systemd dbus units look in the system bus rather than the session bus as they should. We should

nmschulte commented 7 years ago

A short-term solution may be to: comment out the SystemdService line in the service file, perhaps with a relevant comment above it, and also add a note to the build process (make install target, or install-service target) about the optional (non-installed) dunst.systemd.service file (with a note that it needs to be renamed to match the assumption in the D-Bus service file).

Note, I simply installed this file to my PREFIX (~/.local/share/systemd/user/dunst.service) and there were not complications in having two D-Bus service files with the same name (file name and internal service name). I have also left the change to DefaultEnvironment in my ~/.config/systemd/user.conf (DefaultEnvironment="PATH=/home/nmshculte/.local/bin:$PATH"), but I don't believe this is necessary in this case.

mathstuf commented 7 years ago

Setting PATH shouldn't be necessary since systemd requires full paths to the executable in the Exec* lines.

nmschulte commented 7 years ago

While it's a bit heavy handed (read: the configuration affects all of systemd's user units, not just D-Bus/Dunst's environment), it's an important detail for those that run scripts with Dunst (Dunst supports running scripts keyed by notification attribute matching) and expect their PATH from e.g. ~/.profile (this is called... an login (or interactive?) session? I'm not sure the proper terminology.).

One could deal with PATH issues in each script too, but there's no saying that the Dunst configuration (an thus the scripts that care about PATH) can't be used for multiple Dunst instances (though... maybe this can't happen; I thought I saw a use-case for this in my testing, but I may have just misunderstood).

mathstuf commented 7 years ago

Ah. I find it better to instead have a target like environment.target where I have foo-env.service files which handle the systemctl --user set-environment calls when starting the service and unset-environment when stopping it. These services are Before= and BindsTo= environment.target. There's also an x11-environment.target for X11-specific settings. Then my dunst.service is Requires= and After= x11-environment.target to ensure it has the right environment.

It's a bit complicated, but it works and keeps things aware of even things like my ssh-agent from the bottom up instead of trying to set up SSH_AGENT_SOCK in every .service file which might care about it.

mathstuf commented 7 years ago

(My entire setup is managed by systemd including starting X, the window manager, etc., so I don't have a .profile or (meaningful) .xinitrc.)

smcv commented 7 years ago

This only happens if [dbus-daemon] is started with the --systemd-activation flag and it looks like in that case if the service file is not found it ignores the Exec line.

(I am an upstream D-Bus maintainer.)

Yes, that's how it works. On systems where dbus-daemon --session is started with --systemd-activation, each D-Bus session service is either a traditional D-Bus service (always run via Exec as though --systemd-activation had not been used, with no SystemdService) or a hybrid D-Bus/systemd service (always run by starting the SystemdService, never Exec).

You have to either install everything necessary to run dunst as a systemd user service (the unit file and the SystemdService line), or none of it. Providing the SystemdService line but not the unit file that it references is not correct, in the same way that it would not be valid to install a D-Bus service to /usr/local/share/dbus-1/services with Exec=/usr/local/bin/dunst-wrapper-script without also installing dunst-wrapper-script.

A short-term solution may be to: comment out the SystemdService line in the service file, perhaps with a relevant comment above it, and also add a note to the build process (make install target, or install-service target) about the optional (non-installed) dunst.systemd.service file (with a note that it needs to be renamed to match the assumption in the D-Bus service file).

This would be better than the current situation.

Better still, IMO, would be to install the systemd service. On non-systemd systems, and on systemd systems where the OS integrator has not chosen to start dbus-daemon --session --systemd-activation from systemd, it is useless but harmless (one extra installed file that nothing will read). On systems where dbus-daemon's systemd activation feature has been enabled, it results in dunst getting placed in its own cgroup for management and resource control, rather than being treated like part of dbus-daemon.

(If a distro like Debian or Fedora sets up your dbus-daemon and systemd, they are your OS integrator. If you do it yourself, you are the OS integrator. Either way, it needs to be done by someone who knows what they're doing.)

While it's a bit heavy handed (read: the configuration affects all of systemd's user units, not just D-Bus/Dunst's environment), [manipulating the activation environment is] an important detail for those that run scripts with Dunst

Yes, getting the right environment for systemd-activated things is important, but this is not Dunst's job. Arranging for the right environment variables to be pushed into systemd --user and dbus-daemon --session, including uploading them from ~/.profile if that is felt to be necessary, is a job for the OS integrator: it is something that needs to be done for all user-services.

WhyNotHugo commented 7 years ago

I honestly don't see that there's much more that can be done on the dunst side here. The provided dunst.service file work okay on a properly configured system (though only for X11!).

The only thing that needs to be done here is remove the Environment=DISPLAY=:0 line, since:

  1. This should be set by systemctl --user set-environment elsewhere.
  2. This assumption is not universally valid.
  3. This breaks dunst on wayland, and requires manually starting it (eg: not via systemd), or editing the file.

Other than that, the code provided by dunst will only fail if the OS Integrator has provided something inconsistent, and nothing can be done about that here/downstream.

smcv commented 7 years ago

I honestly don't see that there's much more that can be done on the dunst side here

Please do as the first comment said: either remove the SystemdService line from the D-Bus service file, or install the systemd unit at the same time you install the D-Bus service file.

(Or if you insist on it being compile-time-configurable, write out the SystemdService line if and only if you are going to install the systemd unit - but I personally think that's unnecessary complexity, particularly in a project not using Autoconf or similar. Installing the systemd unit unconditionally doesn't harm non-systemd users other than by having an unused file present, in the same way that installing the man page doesn't harm people who don't read man pages.)

Other than that, the code provided by dunst will only fail if the OS Integrator has provided something inconsistent

At the moment dunst is not internally consistent: it always tells dbus-daemon to use systemd for activation if possible, but then does not install the necessary file for that systemd activation to work.

dbus-daemon does not check that the referenced SystemdService exists before delegating activation to systemd - it cannot, because it does not know which directories systemd is going to search.

WhyNotHugo commented 7 years ago

So, in short, the things that need to be done to close this are:

nmschulte commented 7 years ago

Always install the dunst.service file into /usr/lib/systemd/user/dunst.service (via make install)

this should account for PREFIX, too?

WhyNotHugo commented 7 years ago

Yes, you're correct, it should.

smcv commented 7 years ago

this should account for PREFIX, too?

Yes, but use ${prefix}/lib, not ${libdir}. systemd always looks for units in lib, even on systems where shared libraries etc. would go in a parallel directory like lib64 or lib/x86_64-linux-gnu.

tsipinakis commented 7 years ago

One of the concerns I have with setting up dunst as a systemd service by default is that according to my tests, if dunst crashes for any reason dbus will happily restart it when the next notification goes through while it causes a hang and timeout if dunst is configured as a systemd service and it's in failed state.

A solution might be to set Restart=on-failure but I am not sure about how effective that would be since it depends on a timeout and I can't seem to find a way to set it so that it only restarts from a dbus activation.

Setting a timeout is a valid option but if it continuously crashes for one reason on another(i.e. X server hasn't been started yet) can cause quite a bit of log spam.

WhyNotHugo commented 7 years ago

One of the concerns I have with setting up dunst as a systemd service by default is that according to my tests, if dunst crashes for any reason dbus will happily restart it when the next notification goes through while it causes a hang and timeout if dunst is configured as a systemd service and it's in failed state.

I just tested that, and that doesn't seem to be so:

Setting a timeout is a valid option but if it continuously crashes for one reason on another(i.e. X server hasn't been started yet) can cause quite a bit of log spam.

Doesn't the graphical.session.target thing mentioned here cover that?

tsipinakis commented 7 years ago

I just tested that, and that doesn't seem to be so

You're right, I assumed that systemd was the cause but apparently not. After a bit more testing it seems that what I mentioned happens because I set dunst to fail on startup to test this(by disabling the display server) and dbus doesn't acknowledge that the activation failed and causes all messages to that address to hang until it times out.

Doesn't the graphical.session.target thing mentioned here cover that?

According to the man page

This target is active whenever any graphical session is running. It is used to stop user services which only apply to a graphical (X, Wayland, etc.) session when the session is terminated.

So it's only for stopping it, it doesn't set a dependency on it. I am not sure what would be the correct way to set a hard dependency on the display server.

WhyNotHugo commented 7 years ago

So it's only for stopping it, it doesn't set a dependency on it. I am not sure what would be the correct way to set a hard dependency on the display server.

From man systemd.unit:

BindsTo= Configures requirement dependencies, very similar in style to Requires=. However, this dependency type is stronger: in addition to the effect of Requires= it declares that if the unit bound to is stopped, this unit will be stopped too. This means a unit bound to another unit that suddenly enters inactive state will be stopped too. Units can suddenly, unexpectedly enter inactive state for different reasons: the main process of a service unit might terminate on its own choice, the backing device of a device unit might be unplugged or the mount point of a mount unit might be unmounted without involvement of the system and service manager.

When used in conjunction with After= on the same unit the behaviour of BindsTo= is even stronger. In this case, the unit bound to strictly has to be in active state for this unit to also be in active state. This not only means a unit bound to another unit that suddenly enters inactive state, but also one that is bound to another unit that gets skipped due to a failed condition check (such as ConditionPathExists=, ConditionPathIsSymbolicLink=, ... — see below) will be stopped, should it be running. Hence, in many cases it is best to combine BindsTo= with After=.

TBH, I wouldn't worry too much about this though; if an application tries to send a notification when the display server is not running, it's expected to fail.

WhyNotHugo commented 7 years ago

Actually, come to think about it, BindsTo isn't such a good idea: if an application queries the notification server, it'll start the desktop environment. This is undesirable.

WhyNotHugo commented 7 years ago

Bah, scrub all that, we were discussing PartOf, not BindsTo, I got my shit mixed up.

I think PartOf is enough: stop the dunst when the graphical environment stops. Trying to send notifications when no graphical environment is running should fail, and I don't think that scenario requires extra consideration.

tsipinakis commented 7 years ago

Trying to send notifications when no graphical environment is running should fail, and I don't think that scenario requires extra consideration.

You're right, what I said before was with the erroneous assumption that systemd didn't restart failed units.

Please add the PartOf line to #324. I'll update the Makefile to install the systemd service file once that's merged.

tsipinakis commented 7 years ago

Closed via #324

magandrez commented 4 years ago

Hi,

I bumped into the above described problem in the following way:

I run latest dunst, which includes the changes made in #324

Thank you

WhyNotHugo commented 4 years ago

How is it in a failed state before any notification is sent?

It's activated by DBus when the first notification arrives.

WhyNotHugo commented 4 years ago

Actually, I just realised the service can be configured to auto-start on login, but it also starts before the graphic session.

There's probably two fixes around this:

magandrez commented 4 years ago

Hmmmm, I went for the first workaround but did not worked at all.

Failed state remains with journalctl stating

Mar 19 18:18:41 hecate systemd[861]: Starting Dunst notification daemon...
Mar 19 18:18:41 hecate dunst[880]: CRITICAL: Cannot open X11 display.
Mar 19 18:18:41 hecate systemd[861]: dunst.service: Main process exited, code=exited, status=1/FAILURE

Should I post --full?

magandrez commented 4 years ago

To expand on the above: that’s the failed state after reboot and login.

WhyNotHugo commented 4 years ago

What's the output of systemctl --user status dunst?

magandrez commented 4 years ago

The following after boot and log into the X session:

● dunst.service - Dunst notification daemon
     Loaded: loaded (/home/manuel/.config/systemd/user/dunst.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Thu 2020-03-19 19:55:19 EET; 1min 16s ago
       Docs: man:dunst(1)
    Process: 905 ExecStart=/usr/bin/dunst (code=exited, status=1/FAILURE)
   Main PID: 905 (code=exited, status=1/FAILURE)

Mar 19 19:55:19 hecate systemd[886]: Starting Dunst notification daemon...
Mar 19 19:55:19 hecate dunst[905]: CRITICAL: Cannot open X11 display.
Mar 19 19:55:19 hecate systemd[886]: dunst.service: Main process exited, code=exited, status=1/FAILURE
Mar 19 19:55:19 hecate systemd[886]: dunst.service: Failed with result 'exit-code'.
Mar 19 19:55:19 hecate systemd[886]: Failed to start Dunst notification daemon.
WhyNotHugo commented 4 years ago

It seems that somehow dunst have been set to auto-start on login, rather that with dbus-activation.

systemctl --user disable dunst should do the trick.

magandrez commented 4 years ago

Mmmm, it does not affect in any way. After disabling dunst, on reboot the system remains in failed state.

When issuing a notification, then (I assume) dbus starts the service and the system recovers. I assume it is nitpicking, at this point.

But I am still intrigued on the why.

WhyNotHugo commented 4 years ago

This is odd. Is it still autostarting early? What's the output of systemctl --user status dunst now? What about find .config/systemd -iname "*dunst*"?

magandrez commented 4 years ago

systemctl reports the following state for dunst

● dunst.service - Dunst notification daemon
     Loaded: loaded (/usr/lib/systemd/user/dunst.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Fri 2020-03-20 12:16:13 EET; 9s ago
       Docs: man:dunst(1)
    Process: 904 ExecStart=/usr/bin/dunst (code=exited, status=1/FAILURE)
   Main PID: 904 (code=exited, status=1/FAILURE)

Mar 20 12:16:13 hecate systemd[885]: Starting Dunst notification daemon...
Mar 20 12:16:13 hecate dunst[904]: CRITICAL: Cannot open X11 display.
Mar 20 12:16:13 hecate systemd[885]: dunst.service: Main process exited, code=exited, status=1/FAILURE
Mar 20 12:16:13 hecate systemd[885]: dunst.service: Failed with result 'exit-code'.
Mar 20 12:16:13 hecate systemd[885]: Failed to start Dunst notification daemon.

And find does not find anything under .config/systemd

magandrez commented 4 years ago

For completeness:

$ systemctl --user cat dunst.service 
# /usr/lib/systemd/user/dunst.service
[Unit]
Description=Dunst notification daemon
Documentation=man:dunst(1)
PartOf=graphical-session.target

[Service]
Type=dbus
BusName=org.freedesktop.Notifications
ExecStart=/usr/bin/dunst

[Install]
# WantedBy=default.target
WhyNotHugo commented 4 years ago

I'm a bit confused as to why it still says disabled. systemctl --user disable dunst did disable it, right?

What distribution is this on? Is it force-enabled system-wide? tree /etc/systemd/user/?

magandrez commented 4 years ago

Yeah, it is disabled, no link under .config/systemd/user

It is a netinst of Debian Testing.

$ tree /etc/systemd/user
/etc/systemd/user
├── default.target.wants
│   ├── dunst.service -> /usr/lib/systemd/user/dunst.service
│   └── pulseaudio.service -> /usr/lib/systemd/user/pulseaudio.service
└── sockets.target.wants
    ├── dirmngr.socket -> /usr/lib/systemd/user/dirmngr.socket
    ├── gpg-agent-browser.socket -> /usr/lib/systemd/user/gpg-agent-browser.socket
    ├── gpg-agent-extra.socket -> /usr/lib/systemd/user/gpg-agent-extra.socket
    ├── gpg-agent.socket -> /usr/lib/systemd/user/gpg-agent.socket
    ├── gpg-agent-ssh.socket -> /usr/lib/systemd/user/gpg-agent-ssh.socket
    ├── pk-debconf-helper.socket -> /usr/lib/systemd/user/pk-debconf-helper.socket
    └── pulseaudio.socket -> /usr/lib/systemd/user/pulseaudio.socket

2 directories, 9 files
smcv commented 4 years ago
/etc/systemd/user
├── default.target.wants
│   ├── dunst.service -> /usr/lib/systemd/user/dunst.service

The service was enabled system-wide by the Debian packaging, because at the time it was installed, it had WantedBy=default.target (which I think is a bug - it shouldn't be started before the GUI, and in any case shouldn't be started for users of other Notifications implementations, like GNOME Shell). Disabling it per-user does not disable it system-wide.

tsipinakis commented 4 years ago

You're right @smcv, thanks for tracking it down @WhyNotHugo @magandrez. I've now removed the WantedBy line with 43c6145

PRESFIL commented 4 years ago

I have a question. Now dunst is static. But dbus --session can't run it because there is no DISPLAY variable. How do I install it there? Is this a distribution setup problem or yours (dbus is not a graphical application, should there be a DISPLAY in principle?)? My by it works only on specific DM?

Manjaro
LightDM==1:1.30.0-4
i3-gaps==4.18.2-2
dusnt==1.5.0-1.0
dbus==1.12.20-1

p.s. I know that i can use systemctl --user start {}

fermulator commented 2 years ago

fwiw; after an upgrade to Ubuntu 20.04 from 18.04, hit this bug;

ii  dunst                                                       1.4.1-1                                             amd64        dmenu-ish notification-daemon