This seems like something that's been asked before but I can't find it anywhere.
I have a systemd service that runs on a timer, updating my packages. I want to get emailed using ssmtp
every time the service throws an error.
If I run this in the terminal as root, it works.
# /root/bin/update 2> >(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail [email protected])
Updating package lists... Done!
Updating packages... Done!
Removing old packages... Done!
Cleaning up... Done!
Updating dotfiles... FAILED!
Updating bpytop... Done!
Updating global npm packages... Done!
That part that failed sent STDERR which was caught by the pseudo file >()
. The printf "...$(cat -)"
appends a subject to the beginning of the stream. I get an email with the stderr as body. Pretty slick, huh?
This is what I want the service to do. Print the stdout to journald as default, but redirect stderr to a command of my choice. I tried this first:
ExecStart=/root/bin/update 2> >(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail [email protected])
But the errors are still sent to journald and not ssmtp. I think systemd intercepts stderr and sends it wherever is specified by StandardError=
. The StandardError=
option allows the stderr to be sent to a file descriptor or file... so I tried stuff like this:
ExecStart=/root/bin/update
StandardError=file:>(printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail [email protected])
and
StandardError=fd:printf "Subject: ERROR from update.service\n\n$(cat -)" | sendmail [email protected]
Didn't work. It might be possible to send it the StandardError to a named pipe which points to sendmail. I fooled around with trying to get this to work and finally gave up and came here.
EDIT: I also thought about writing a bash function in the update script that sends email, then redirecting stderr from each command in the script to that function. I'm looking for a solution at the systemd level though, instead of hardcoding the STDERRs' destination in the script itself.