I have the following shell script:
#!/usr/bin/env sh
PIPE_PATH="/tmp/mypipe"
test -p "${PIPE_PATH}" || mkfifo "${PIPE_PATH}"
processCommandsFromPipe() {
local pipe="${PIPE_PATH}"
while true; do
while read command; do
nohup setsid sh -c "${command}" >/dev/null 2>&1 &
commandPID=$!
echo "PID: ${commandPID}"
sleep 1
done < "${pipe}"
if [ ! -p "${pipe}" ]; then
exit 0
fi
done
}
processCommandsFromPipe &
PIPE_PROCESS=$!
cleanup() {
echo CLEANUP
trap - EXIT
if kill -0 "${PIPE_PROCESS}" >/dev/null 2>&1; then
kill "${PIPE_PROCESS}"
fi
rm -f "${PIPE_PATH}"
exit
}
read
cleanup
It opens a pipe, reads from it and spawns a background process of whatever command is written to it.
After starting the script, I write a command to the pipe in another shell:
echo "cat /dev/zero" > /tmp/mypipe
The script outputs the PID correctly:
PID: 95522
Then I kill the process:
kill 95522
And now my script shows an error:
bin/test.sh: line 11: can't open /tmp/mypipe: Interrupted system call
The script continues normally, however. I can still write other commands to the pipe and they get spawned correctly.
Can anybody explain why this message appears? I did not touch the pipe, I just killed the spawned sub-process. In my understanding the loop should not be affected by this. How can I get rid of the message?
I am using busbox's ash
as my sh
, by the way. If I execute the script with bash
, the error is not shown.
while read command; do ... done < "${pipe}"
While waiting for a command, the script is blocked on opening a pipe to be redirected as stdin for read
function. See man 7 fifo:
Normally, opening the FIFO blocks until the other end is opened also.
When previously spawned process is terminated, SIGCHLD signal is sent to the parent process (shell running script). Signal interrupts blocking call unless SA_RESTART flag is set in sigaction. In busybox ash it is not set.
The error is printed by openredirect() function.
Workaround to avoid this error message may be to run child process from subshell:
while read command; do
(
nohup setsid sh -c "${command}" >/dev/null 2>&1 &
commandPID=$!
echo "PID: ${commandPID}"
)
sleep 1
done < "${pipe}"
In this case subshell terminates immediately after spawning command process, and the script waits for subshell termination. Subshell's child process (actual command) is inherited by init. When command process is terminated, SIGCHLD is sent to the init
process instead of the shell waiting for pipe to be opened.