Search code examples
shellcronfish

Executing notify-send from fish script as cronjob


I am trying to call notify-send from a fish-script as a cronjob. Eventhough the script is being called by cron, the notification does not pop up on my display. I am not sure where it is failing, if notify-send is being executed at all, if it is a shell problem or some other problem. Executing the script in the terminal produces the expected (i.e. popup window) results

in crontab -e -u $USER:

  SHELL=/bin/fish

  * * * * * memcheck >> /tmp/cron.memcheck.log

running tail --follow /tmp/cron.memcheck.log shows that the script is being called, since it is echoing the debug output into the log file, but it tails to launch notify-send.

This is my (noobish) script:

# Defined in /home/mio/.config/fish/functions/memcheck.fish @ line 2
function memcheck
    set MEM_USED (free | string replace '3914132' '' | string match 'Mem: [ ]{1,}[0-9]{1,}' --regex | string match '\d$
    #echo $MEM_USED
    set MEM_CAP 3914132
    set MEM_FREE (math $MEM_CAP - $MEM_USED)
    echo $MEM_FREE
    if test $MEM_FREE -lt 8700700
        echo "WARNING: memory usage out of control. 21:10"
        set DISPLAY :0.0
        echo $DISPLAY
        echo $USER
        /usr/bin/notify-send "Memory Usage" $MEM_FREE --urgency=critical
    end
end

I've read that in some instances notify-send cannot find the display and that setting $DISPLAY to :0.0 might do the trick. if I echo $DISPLAY in my terminal I get :0.0. Also echoing the $USER gives me my user name, which I expected since I ran cronjob -u mio -e and didn't edit /etc/crontab directly. Thanks for the time.


Solution

  • if I echo $DISPLAY in my terminal I get :0.0

    Yes, but your cronjob doesn't run in your terminal.

    In Unix, environment variables are passed from parent processes to their children when they're started.

    The fish inside your terminal is a child of that terminal, which has $DISPLAY set to contact X.

    But your cronjobs are run by your cron daemon, which is typically a child of your init process, which in turn doesn't have any parent. So it inherits the environment of init.


    Set $DISPLAY in your script. This isn't pretty (and I can't say I like the approach of having a cronjob that sends notifications to begin with), but it should work, at least if you have the typical setup with one X server.

    Note that fish is entirely irrelevant in this case - it would happen no matter what you picked as shell.

    Some plausible alternatives (though I've not looked into them far):

    • Run a watch job in a terminal or via your DE's autostart mechanism. This just reruns things every X seconds, but has $DISPLAY
    • Use systemd's timer stuff, in particular as a user. There's a command to "upload" an environment variable to systemd, so it can then use it in timers.