44

I am using Arch Linux with KDE/Awesome WM. I am trying to get notify-send to work with cron.

I have tried setting DISPLAY/XAUTHORITY variables, and running notify-send with "sudo -u", all without result.

I am able to call notify-send interactively from the session and get notifications.

FWIW, the cron job is running fine which I verified by echoing stuff to a temporary file. It is just the "notify-send" that fails to work.

Code:

[matrix@morpheus ~]$ crontab -l
* * * * *  /home/matrix/scripts/notify.sh

[matrix@morpheus ~]$ cat /home/matrix/scripts/notify.sh
#!/bin/bash
export DISPLAY=127.0.0.1:0.0
export XAUTHORITY=/home/matrix/.Xauthority
echo "testing cron" >/tmp/crontest
sudo -u matrix /usr/bin/notify-send "hello"
echo "now tested notify-send" >>/tmp/crontest

[matrix@morpheus ~]$ cat /tmp/crontest
testing cron
now tested notify-send

[matrix@morpheus ~]$ 

As you can see the echo before & after notify-send worked.
Also I have tried setting DISPLAY=:0.0

UPDATE: I searched a bit more and found that DBUS_SESSION_BUS_ADDRESS needs to be set. And after hardcoding this using the value I got from my interactive session, the tiny little "hello" message started popping up on the screen every minute!

But the catch is this variable is not permanent per that post, so I'll have try the the named pipe solution suggested there.

[matrix@morpheus ~]$ cat scripts/notify.sh
#!/bin/bash
export DISPLAY=127.0.0.1:0.0
export XAUTHORITY=/home/matrix/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-BouFPQKgqg,guid=64b483d7678f2196e780849752e67d3c
echo "testing cron" >/tmp/crontest
/usr/bin/notify-send "hello"
echo "now tested notify-send" >>/tmp/crontest

Since cron doesn't seem to support notify-send (at least not directly) is there some other notification system that is more cron friendly that I can use?

  • This should work as far as I can see. Why don't you add a &>>/tmp/crontest to the notify send line and see if notify-send gives any error messages. – Graeme Jan 27 '14 at 19:28
  • Out of curiosity, did you try my solution? It seems much simpler and worked perfectly on my Debian. I'm asking just to know if it Debian specific or not – terdon Jan 27 '14 at 19:40
  • @terdon I tried your solution (just a quick test) and it seems to work on my Debian system. I'd like to know if it's generally applicable since it is indeed simpler. – Marco Jan 27 '14 at 19:48
  • @Marco I'm on LMDE (essentially Debian testing) and using Cinnamon as DE. Can't tell you if it works beyond those. – terdon Jan 27 '14 at 19:52
  • @Marco & terdon: Ubuntu guys are able to do so: http://ubuntuforums.org/showthread.php?t=1727148 – justsomeone Jan 27 '14 at 19:53
  • DBUS_SESSION_BUS_ADDRESS is definitely not required on Debian, you can do unset DBUS_SESSION_BUS_ADDRESS and notify-send still works. The arch configuration must somehow be different. – Graeme Jan 27 '14 at 20:05
  • @Graeme Not here. DBUS_SESSION_BUS_ADDRESS= notify-send Foo Bar doesn't display a notification. – Marco Jan 27 '14 at 21:13
  • @Marco Hmm, same with me. This is not the same as unset though. You can also use unset $(env | sed '/^PATH=/d; s/=.*//') to clear everything except path. If you then set DISPLAY and XAUTHORITY, you should see that these are the only ones required. Although it seems that DBUS_SESSION_BUS_ADDRESS must be valid if it exists. – Graeme Jan 27 '14 at 21:55
  • In my case without DBUS_SESSION_BUS_ADDRESS set I had Unity style OSD notification from crontab. I'm using gnome-shell so this was irritating because my default notifications was displayed in different style then those from crontab. Now is working! – sobi3ch Mar 11 '21 at 10:39
  • What about simply dbus-launch notify-send "message"? – miyalys Jun 06 '22 at 12:41

14 Answers14

42

You need to set the DBUS_SESSION_BUS_ADDRESS variable. By default cron does not have access to the variable. To remedy this put the following script somewhere and call it when the user logs in, for example using awesome and the run_once function mentioned on the wiki. Any method will do, since it does not harm if the function is called more often than required.

#!/bin/sh

touch $HOME/.dbus/Xdbus
chmod 600 $HOME/.dbus/Xdbus
env | grep DBUS_SESSION_BUS_ADDRESS > $HOME/.dbus/Xdbus
echo 'export DBUS_SESSION_BUS_ADDRESS' >> $HOME/.dbus/Xdbus

exit 0

This creates a file containing the required Dbus evironment variable. Then in the script called by cron you import the variable by sourcing the script:

if [ -r "$HOME/.dbus/Xdbus" ]; then
  . "$HOME/.dbus/Xdbus"
fi

Here is an answer that uses the same mechanism.

Marco
  • 33,548
  • 2
    Glad to see that I was almost near to the solution. Thanks Marco, that is neat! – justsomeone Jan 27 '14 at 19:38
  • 1
    Great, I reused your answer and added some more detailed instructions here: http://askubuntu.com/a/537721/34298 – rubo77 Oct 18 '14 at 07:09
  • Wouldn't this be a security risk? http://security.stackexchange.com/questions/71019/is-revealing-your-dbus-session-bus-address-a-vulnerability – rubo77 Oct 18 '14 at 07:18
  • @Gilles How could you do this in one line like you mentioned in chat? – rubo77 Oct 18 '14 at 07:34
  • I have tried so many other answers not including DBUS on ubuntu 15.10 and nothing worked. That one is simple and works flawlessly. – bastian Nov 04 '15 at 11:19
  • Suggestion: If using bash, you could declare -p DBUS_SESSION_BUS_ADDRESS instead of that env | grep. With standard sh, env -i DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" should work. These both avoid the possibility of getting a variable you didn't want because it's value includes BUS_SESSION_BUS_ADDRESS. BTW: declare -p should properly quote, unlike env (unlikely to be a problem with this variable, though). Another option is to just write the value to the file, then use read. Or just use echo to write the assignment. – derobert Dec 04 '15 at 21:22
  • @Marco +1 Its working but could you shortly explain why I have to export DBUS_SESSION_BUS_ADDRESS variable while it is already set as env variable ? – EdiD May 28 '16 at 08:31
  • See this answer for a notify_all function which works from root crontab in Ubuntu 16.04 – mivk Feb 12 '17 at 12:08
  • Note that I (on KDE on Arch) did also have to set $DISPLAY. Additionally, unlike most examples, my value was DISPLAY=:0, not :0.0. You can confirm this by echoing it from your normal user, and could put it in the same script that sets DBUS_SESSION_BUS_ADDRESS. – tobek Nov 17 '17 at 00:35
20

You need to set the variables in the crontab itself:

DISPLAY=:0.0
XAUTHORITY=/home/matrix/.Xauthority

# m h  dom mon dow   command 
* * * * *  /usr/bin/notify-send "hello"

No sudo needed, at least not on my system.

terdon
  • 242,166
  • 1
    Thanks terdon for your time. This seems to be a simple solution. Unfortunately, this didn't work for me, – justsomeone Jan 27 '14 at 19:46
  • @justsomeone huh, OK, might depend on the desktop environment then. – terdon Jan 27 '14 at 19:47
  • I think this has got something to do with distro or Desktop Environment. For Ubuntu users, the straight forward solutions seems to work fine from what I have seen in online forums. – justsomeone Jan 27 '14 at 19:50
  • @justsomeone I'm on Debian (LMDE) using Cinnamon as DE. Might have something to do with how X is started or with the notifications system used by the DE, dunno. – terdon Jan 27 '14 at 19:52
  • Confirmed it works on Ubuntu 14.04/14.10. With GNOME and Unity. – Jordon Bedwell Sep 29 '14 at 19:32
  • Does not work wich xubuntu 14.04. I wonder what's the difference to gnome/unity... – Daniel Alder Jul 13 '15 at 10:17
7

The safest way to get X session related environmental variables is to get them from the environment of a process of the user who is logged on to X. Here is an adaptation of the script that I use for exactly the same purpose (although DBUS_SESSION_BUS_ADDRESS doesn't seem to be a problem for me on Debian):

X=Xorg                   # works for the given X command
copy_envs="DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS"

tty=$(ps h -o tty -C $X | head -1) [ -z "$tty" ] && exit 1

calling who with LANG empty ensures a consistent date format

who_line=$(LANG= who -u | grep "^[^ ]+[ ]+$tty")

x_user=$(echo $who_line | cut -d ' ' -f 1) # the user associated with the tty pid=$(echo $who_line | cut -d ' ' -f 7) # the user's logon process

for env_name in $copy_envs do

if the variable is not set in the process environment, ensure it does not remain exported here

unset "$env_name"

use the same line as is in the environ file to export the variable

export "$(grep -az "^$env_name=" /proc/$pid/environ)" >/dev/null done

sudo -u "$x_user" notify-send "hello"

This sends to message to the first X user it finds, although you could add a loop to send it to all users.

Update

It seems that updates to the utmp format cause who to print a display instead of a tty in its second column. This actually makes things easier, previously it only printed the display in the comment at the end and I decided this wasn't safe to rely on for the original answer. If this is the case, try this:

X=Xorg                   # works for the given X command
copy_envs="DISPLAY XAUTHORITY DBUS_SESSION_BUS_ADDRESS"

calling who with LANG empty ensures a consistent date format

who_line=$(LANG= who -u | awk '$2 ~ ":[0-9]"')

x_user=$(echo $who_line | cut -d ' ' -f 1) # the user associated with the tty pid=$(echo $who_line | cut -d ' ' -f 7) # the user's logon process

for env_name in $copy_envs do

if the variable is not set in the process environment, ensure it does not remain exported here

unset "$env_name"

use the same line as is in the environ file to export the variable

export "$(grep -az "^$env_name=" /proc/$pid/environ)" >/dev/null done

sudo -u "$x_user" notify-send "hello"

Graeme
  • 34,027
7

This one-liner worked for me in Manjaro with Cronie:

# Note: "1000" would be your user id, the output of... "id -u <username>" 
10 * * * * pj DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Hello world!' 'This is an example notification.'

Without the very ugly DBUS_blah_blah it doesn't work at all. I also found journalctl -xb -u cronie helpful. FWIW, Cronie uses crond and should be backward compatible with Vixie cron.

I found the solution here https://wiki.archlinux.org/index.php/Desktop_notifications

Update: Still works in 2021. I add these tips to my /etc/crontab file.

# After installing cronie:
# systemctl start cronie
# systemctl enable cronie

After editing this file:

chmod 600 /etc/crontab

crontab /etc/crontab

Check that the changes are set:

crontab -l

PJ Brunet
  • 783
7

I use i3 on Ubuntu 18.04. My way to solve this is:

* * * * * XDG_RUNTIME_DIR=/run/user/$(id -u) notify-send Hey "this is dog!"

1

This is sufficient to make notify-send work for me in a cronjob on Ubuntu Trusty:

#!/bin/bash
export DISPLAY=$(who -u | awk  '/\s:[0-9]\s/ {print $2}')

It simply exports the DISPLAY for the user the cronjob is running as. It works for me without setting XAUTHORITY or DBUS_SESSION_BUS_ADDRESS.

  • 1
    Works on Ubuntu 16.04 as well. I actually have cron launching a Perl script, which system()s a bash script, which launches a different Perl script, which performs a system("notify-send ...").

    Adding the export command to the bash script modified the environment for that script, which the last Perl script then inherited and made available to the system("notify-send ..."). Good find blujay!

    – Tim Jan 09 '18 at 18:16
1

For those on Linux who are comfortable installing Python packages, I just released a notify-send-headless program which is working well for me. It searches /proc for the required username and environment variables and then runs notify-send with these variables (it will use sudo to switch to the required user if necessary).

xolox
  • 780
  • 4
  • 7
1

You could also make a script:

#!/usr/bin/env bash
runuser -l [yourusername] -c 'DISPLAY=:0 notify-send "hey there user"'

Then run it with sudo. However, since crontab -e runs all commands with the user that created it, the following should suffice when called without sudo:

#!/usr/bin/env bash
DISPLAY=:0 notify-send "hey there user"

At least it does for me. It all seems to be dependent on the environment configuration.

1

Use printenv for print environment variables from your normal terminal. And then paste all environment variables in starting of crontab file.

Monu
  • 155
  • 7
1

In my case, the issue was with using root user with notify-send. I realized this when I saw that sudo notify-send didn't work on the terminal but just notify-send (i.e. with current user) did. So instead of editing the usual /etc/crontab which is used by root, I created a custom cron script for myusername using the below command

sudo crontab -u myusername -e

And added the below command to trigger my notify-send script.

24 23 * * * bash /home/myusername/notify_send_script.sh

You can get your username with the command id -u -n. It's important to create a separate script instead of just the notify-send command as you'll also have to export the DBUS_SESSION_BUS_ADDRESS env variable. I created my /home/myusername/notify_send_script.sh as below thanks to the simple script in this answer

#!/bin/bash
userid=$(id -u)
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$userid/bus"
export DBUS_SESSION_BUS_ADDRESS
notify-send "Message title here" "Here goes the message"
abcoep
  • 111
0

I use this script in cron to post MPD now playing to twitter every hour

#!/bin/bash
export DISPLAY=":0.0"
msg=$(mpc current -h 192.168.1.33)
twitter set "#MPD Server nowplaying $msg :  http://cirrus.turtil.net:9001"
#ttytter -status="#MPD Server nowplaying $msg. http://cirrus.turtil.net:9001"

exit 

similar script using notify-send

#!/bin/bash
export DISPLAY=":0.0"
notify-send -i ~/.icons/48arch.png 'OS- Archlinux x86_64 : DWM Window Manager' 'Installed on Sun Apr 21 2013 at 18:17:22' 
exit

you may be experiencing problems as KDE uses its own notify-deamon IIRC.

Archemar
  • 31,554
cirrus
  • 1
0

For what its worth....

I had to use ALL of the following on Debian Jessie to get this to work...

export DISPLAY=:0.0
export HOME=/home/$user
source "$HOME/.dbus/session-bus/*-0"

Leaving out any one of these caused it to stop working.

BenJ
  • 1
  • That last line won't do anything as written here, because there will be no file literally called *-0 in your session-bus directory. You might have meant source "$HOME"/.dbus/session-bus/*-0. – Chris Davies Apr 05 '17 at 18:16
0

Using sudo :

sudo -u $currentxuser notify-send $message

Tip :

We can get the current x user by this command

ps auxw | grep -i screen | grep -v grep | cut -f 1 -d ' '

In addition...

currentxuser=$(ps auxw | grep -i screen | grep -v grep | cut -f 1 -d ' ')
echo $currentxuser

Good to know :

Cron running under root does not have access to x thus all gui commands will not be displayed, one simple solution is to add root to authorized x user for the current x user with this command

from the x user shell

xhost local:root

or

sudo -u $currentxuser xhost local:root
intika
  • 14,406
  • I don't know which command your "grep screen" catches - on my system it is a wrapper ... libscreenshotplugin.so and works therefore by accident. If you know, what you are searching, you may use -C to tell ps, what process to look for and -o to restrict the output like so: ps -C xfce4-session -o user=. Of course, xfce-session is specific to my xfce4-Desktop and needs to be changed for others. – user unknown Apr 08 '21 at 13:38
-1

Here is a less complex script than what Graeme provided. His script didn't work for me, $who_line was always empty. My script doesn't waste so much time with finding a process. Instead, it just tries all and pick the last useful value found. I'm running xubuntu 14.04 and have some lxc containers running which probably confuse this kind of scripts.

env="$(
  ps -C init -o uid,pid | while read u p; do
    [ "$u" = "`id -u`" ] || continue
    grep -az '^DBUS_SESSION_BUS_ADDRESS=' /proc/$p/environ | xargs -0
  done | tail -1
)"

export "$env"

notify-send "test"
  • This doesn't work for me on Trusty because the Xorg process's environment doesn't have DBUS_SESSION_BUS_ADDRESS. I can get it from my shells, but not from the Xorg process. –  Oct 03 '15 at 23:18