Sams Teach Yourself Shell Programming in 24 Hours
(Publisher: Macmillan Computer Publishing)
Author(s): Sriranga Veeraraghavan
ISBN: 0672314819
Publication Date: 01/01/99

Previous Table of Contents Next


Setting Up a Timer

In many scripts, there are critical sections where commands that require a large amount of time to complete are executed. On rare occasions, these commands might not finish processing. In order to deal with this situation, you need to set up a timer within the script. When the timer expires, you should terminate the program and inform the user about the abnormal exit.

In this section you will walk through a simple script that demonstrates the major aspects of setting up a timer using the ALARM signal and a signal handler.

The main body of this script needs to perform the following actions:

1.  Set a handler for the ALARM signal.
2.  Set the timer.
3.  Execute the program.
4.  Wait for the program to finish executing.
5.  Unset the timer.

If the timer expires before the program finishes executing, the handler for the ALARM signal needs to terminate the program.

The main body looks like the following:

# main()

trap AlarmHandler 14

SetTimer 15

$PROG &
CHPROCIDS="$CHPROCIDS $!"
wait $!
UnsetTimer

echo "All Done."
exit 0

The only thing in the main body that was not explicitly mentioned previously is the CHPROCIDS variable. This variable is required to maintain a list of all the child processes of the script, because the possible child process are

  The timer
  The program

AlarmHandler

The first thing that you do is to install a handler for the ALARM signal. This handler is the function AlarmHandler:

AlarmHandler() {
    echo "Got SIGALARM, cmd took too long."
    KillSubProcs
    exit 14
}

This is a simple function that prints a message to the screen, calls the function KillSubProcs, and then exits with an exit code of 14 to indicate that the alarm was triggered.

The KillSubProcs function is a simple function that kills all the child processes of the script as stored in the variable CHPROCIDS:

KillSubProcs() {
    kill ${CHPROCIDS:-$!}
    if [ $? -eq 0 ] ; then
        echo "Sub-processes killed." ;
    fi
}

SetTimer

The next task that you perform is to set the timer using the SetTimer function:

SetTimer() {
    DEF_TOUT=${1:-10};
    if [ $DEF_TOUT -ne 0 ] ; then
        sleep $DEF_TOUT && kill -s 14 $$ &
        CHPROCIDS="$CHPROCIDS $!"
        TIMERPROC=$!
    fi
}

This function takes a single argument that indicates the number of sections that it should set a timer for. The default is 10 seconds.

As you can see, setting the timer is fairly trivial. All you do is issue a sleep command with a timeout and then use the kill command to send the shell’s process ID (stored in $$) an ALARM signal. In order to continue processing, you place this timer command in the background.

Because the timer is a background process, you need to update the list of child processes with its ID. You also save the process ID of the timer in the variable TIMERPROC so that you can later unset the timer.

UnsetTimer

The last function is the UnsetTimer function that unsets the timer set by the SetTimer function. Unsetting the timer basically means killing its process:

UnsetTimer() {
    kill $TIMERPROC
}

The Complete Timer Script

The complete timer script follows:

#! /bin/sh

AlarmHandler() {
    echo "Got SIGALARM, cmd took too long."
    KillSubProcs
    exit 14
}

KillSubProcs() {
    kill ${CHPROCIDS:-$!}
    if [ $? -eq 0 ] ; then echo "Sub-processes killed." ; fi
}

SetTimer() {
    DEF_TOUT=${1:-10};
    if [ $DEF_TOUT -ne 0 ] ; then
        sleep $DEF_TOUT && kill -s 14 $$ &
        CHPROCIDS="$CHPROCIDS $!"
        TIMERPROC=$!
    fi
}

UnsetTimer() {
    kill $TIMERPROC
}
# main()

trap AlarmHandler 14

SetTimer 15
$PROG &
CHPROCIDS="$CHPROCIDS $!"
wait $!
UnsetTimer
echo "All Done."
exit 0

Conclusion

In this chapter, I introduced the concept of signals. Signals inform a program or script that an important event has occurred.

You learned about the most common signals encountered in shell programming. I also listed several methods of obtaining a complete list of all the signals understood by your system. In this section you also covered the concept of delivering signals and the default actions associated with a signal.

In the second section of this chapter I introduced the three main methods of handling a signal in a script. Now you know how to catch signals and handle them using signal handlers. You also know how to ignore signals. Finally I showed you how to use signals to set up a timer inside your scripts.

Questions

1.  The following is the main body of the “alive” script presented earlier in this chapter. Please change it so that receiving a SIGQUIT causes it to exit when the wait command returns.
# main()

trap CleanUp 2 3 15
trap Init 1

PROG=$1
Init

while : ;
do
    wait $!
    $PROG &
done
2.  Add a signal handler to the timer script to handle the INT signal.


Previous Table of Contents Next