Article Listing 1 oct2006.tar

Processing User Input: Timeout Mechanisms

Ed Schaefer and John Spurgeon

Waiting for a user to respond to a program that is requesting input can be an issue. How do you provide a timeout mechanism in your shell scripts? In this column, we consider three methods. We touch on the read command's -t option; we discuss manipulating the terminal driver with the stty command; and we present our own script, called timeout_countdown, that can kill another process if the user doesn't respond within a certain amount of time.

Using the read Command

The bash and ksh93 shells provide a timeout option for the read command. The following stub waits 5 seconds before exiting the script:

any_key=""
echo "wait 5 seconds for input"
read -t5 any_key 
if [[ -z "$any_key" ]] 
then 
   echo "User neglected to enter value 
    within 5 seconds"
   exit 1 
fi 
echo "any_key is: $any_key"
            
Manipulating the Terminal

If you're using the Bourne shell or ksh88, the comp.unix.shell FAQ provides a solution that manipulates the terminal driver. In a sub-process, save the terminal mode, using the stty command, turn off canonical processing and wait. Then, grab one line of input and restore the saved terminal settings:

#!/bin/ksh 


any_key=""
echo "wait 5 seconds for input"


    { 
      oldtty=$(stty -g) 
      stty -icanon min 0 time 50 
      any_key=$(head -n 1) 
      stty "$oldtty"
    } 


if [[ -z $any_key ]] 
then 
   echo "User neglected to enter value within 5 seconds"
   exit 1 
fi 
echo "any_key: is $any_key"
            
While this solution is elegant, it does not handle special characters well. Since canonical mode is off, the special characters are processed as individual keystrokes. For example, the back space key doesn't function as a back space, and the interrupt key -- after waiting the prescribed time out -- terminates the parent shell. This logs you out if the parent shell was your original login shell.

You can always turn off signals by changing the stty command, which disables processing special characters:

stty -icanon min 0 time 50 -isig
Using the timeout_countdown Script

For cases where the read command's -t option isn't supported, we developed the timeout_countdown script (Listing 1). Before executing a command that prompts the user for input, start the timeout_countdown script in the background. If the user doesn't enter input within the required timeframe, then the timeout_countdown kills its parent or possibly some other specified process. If the user enters input within the timeframe, another call to timeout_countdown with the stop argument prevents the process from being killed; it's like disabling a time bomb. The following script demonstrates the use of timeout_countdown:

#!/bin/ksh 
# timeout.ss: timeout_countdown demo  

echo "Enter input within 5 seconds or else!" 

timeout_countdown -t 5 start &
read any_key 
timeout_countdown stop 


echo "any_key is: $any_key"
# end script 
     
With the start argument, timeout_countdown by default finds the parent process id with the ps -p command and saves its own process id in a temporary file. If the user doesn't respond, the script sleeps for the specified amount of time (5 seconds in this case), kills the timeout.ss process, and deletes the temporary file.

If the user responds to the read command's prompt before the timeout period, then timeout_countdown is executed again with the stop argument. Calling timeout_countdown with the stop argument retrieves the process id of the initial timeout_countdown process and kills it, thus preventing timeout.ss from being killed.

When called with the stop argument, the timeout_countdown script performs a sanity check before killing a process (viz., before disabling the corresponding timeout_countdown time bomb). The script uses the process id to retrieve the name of the command it's about to kill and verifies that it appears to be an instance of a timeout_countdown process.

The -t option changes the 300 second default time frame, and the -d option changes the default /tmp temporary file directory. Finally, by default, timeout_countdown determines its parent's process id. You can overrule the default by passing another process id with the -p option.

Resource

Comp.unix.shell newsgroup FAQ, Part 2 -- http://home.comcast.net/~j.p.h/cus-faq-2

John Spurgeon is a software developer and systems administrator for Intel's Factory Information Control Systems, IFICS, in Aloha, Oregon. Outside of work, he enjoys turfgrass management, triathlons, ultra-marathon cycling, and spending time with his family.

Ed Schaefer is a frequent contributor to Sys Admin. He is a software developer and DBA for Intel's Factory Information Control Systems, IFICS, in Aloha, Oregon. Ed also hosts the monthly Shell Corner column on UnixReview.com. He can be reached at: shellcorner@comcast.net.