Search code examples
linuxshellshash

"Interrupted system call" error on killing child process


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.


Solution

  • 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.