Egor Okhterov
Egor Okhterov

Reputation: 548

dd utility is not ready to receive kill signal

I have a script which decompresses .gz file and copies it into block device. I use dd to get an information about the progress (I cannot use other utilities, like pv to do that). Here is a part of the script:

#!/bin/sh

set -e
trap 'true' USR1

# ...

gunzip -c "${SVM_IMAGE_FILE_PATH}" | dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" &
DD_PID=$!

echo "Decompression process has started: $$ -> ${DD_PID}" >> "${DECOMPRESSION_LOG_FILE_PATH}"

# Wait for dd process to become ready to receive 'kill -s USR1' signals
sleep 1

while (ps -p "${DD_PID}") >/dev/null 2>&1
do
    set +e
        kill -s USR1 $DD_PID 2>/dev/null

        LAST_LINE=`tail -n 1 "${DD_OUTPUT_FILE_PATH}"`
        case "${LAST_LINE}" in
            *byte*)
                BYTES_COPIED=`echo ${LAST_LINE} | cut -d ' ' -f1`
                PROGRESS=$(( $(( $BYTES_COPIED * 100 )) / $DECOMPRESSED_FILE_SIZE ))
                set_status "${PROGRESS}% processed"
            ;;
        esac

        # ...
    set -e

    sleep 1

done

As you can see there is a strage sleep 1 before the while loop. I want to get rid of it, but I don't know how. If I delete this sleep 1, the dd process will be killed right after the first invocation of kill -s USR1 $DD_PID. Maybe that doesn't kill dd, but it somehow affects gunzip process — I don't know what is really happening.

So, is there a way to get rid of that sleep 1?

Upvotes: 1

Views: 3103

Answers (1)

that other guy
that other guy

Reputation: 123410

There are two issues:

  1. DD_PID is the pid of the shell that runs your pipeline, not of dd.
  2. SIGUSR1 will kill a process if it's not ready to receive the signal.

The shell may choose to execute dd in the same process as an optimization, but this is not something you can depend on (especially not when using /bin/sh.

You can also not depend on dd racing to set up a signal trap, so it's better to set it up yourself.

Since you're on Linux, consider just using the GNU dd flag status=progress for an automatic status bar.

Alternatively, change the shebang to #!/bin/bash and use:

trap '' USR1  # Ignore USR1 in inherited processes by default
dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" \
     < <(gunzip -c "${SVM_IMAGE_FILE_PATH}") & 
dd_pid=$!     # Is now 'dd's pid and not a shell's.

Or to be sh compliant:

fifo="/tmp/foo"
mkfifo "$fifo"
gunzip -c "${SVM_IMAGE_FILE_PATH}" | {
  trap '' USR1
  dd iflag=fullblock of="${TARGET_DEVICE_PATH}" bs=64M 2>"${DD_OUTPUT_FILE_PATH}" &
  echo "$!" > "$fifo"
  wait
} &
read dd_pid < "$fifo"
rm "$fifo"

Or with dash, change the shebang to #!/bin/dash and change trap 'true' USR1 to trap '' USR1.

Upvotes: 1

Related Questions