Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

systemd unit file problems with tr

Tags:

systemd

I'm trying to execute a shell script with systemd.

If I run my script from the bash everything works fine. But if I run the same script via systemd it never finishes. The command where it seems to hang is:

random="$(LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w128 | head -n1)"

If I'm replacing this line with random="1234" it also runs with systemd. I guess the 'hanging' command is the tr - its process never finishes.

And this is the systemd unit file I'm using:

[Unit]
Description=my script

[Service]
Type=forking
Restart=on-failure
ExecStart=/mypath/script.sh start
ExecStop=/bin/kill $MAINPID
ExecStopPost=/bin/rm -f /mypath/RUNNING_PID

[Install]
WantedBy=multi-user.target
like image 751
Kris Avatar asked Jun 04 '17 20:06

Kris


1 Answers

Edit: Made the explanation clearer and added new information.

Short answer: Set IgnoreSIGPIPE=false under [Service] in the .service file. From the systemd.exec manual:

   IgnoreSIGPIPE=
       Takes a boolean argument. If true, causes
       SIGPIPE to be ignored in the executed process.
       Defaults to true because SIGPIPE generally is
       useful only in shell pipelines.

Long explanation:

random="$(LC_ALL=C tr -cd '[:alnum:]' < /dev/urandom | fold -w128 | head -n1)"

When the head command exits after the first newline received from fold, it's open file descriptors are closed. When the fold command later tries to write to the pipe, it will receive a SIGPIPE signal. The default action for this signal is a termination of the process. This should normally lead to the termination of the fold command, and likewise the subsequent termination of the tr command.

However, when the pipeline is run under systemd, systemd sets the default action for SIGPIPE to SIG_IGN, which makes the processes in the pipeline ignore the signal. While the fold command ignores the signal, it will still receive an EPIPE error when it writes to the broken pipe. But the fold command does not check the return value of any of the fwrite calls, at least not in coreutils-8.26. This leads the fold command to continue reading from std in and oblivious to the error, write to std out. In doing so, fold keeps the pipe from tr open. Since also tr ignores the SIGPIPE, and the pipe to fold is open, it just continues reading from /dev/urandom and writing filtered bytes to the pipe forever.

like image 156
Tom Bjerck Avatar answered Oct 04 '22 23:10

Tom Bjerck