Processing User Input: Waiting for a Key Press
Ed Schaefer and John Spurgeon
Dealing with user input can be challenging. Should a program wait indefinitely
for the user to respond? How do you design a shell script to time out after a specified period of time?
In this article, we present a method for determining whether a program is waiting for user input.
In a subsequent article, we'll discuss ways to implement a timeout feature in your shell scripts.
If you are interacting with a program that is prompting for input, it's
easy to see what's going on; just look at the screen. But what if you are monitoring a process
and can't see the program's output? How do you know whether the program is doing work
or is paused waiting for a response?
We encountered this problem with a program called ontape. The command ontape-c is used to continuously back up our database engine's logical logs. If the logs are
not backed up, the engine will eventually freeze when all the logs have been used. We've taken
several steps to make sure this doesn't happen, including monitoring the ontape process.
In the case of ontape-c, we can't simply check whether the process is running, because it might
be waiting for the user to press return after inserting a tape into the tape drive. This scenario
motivated us to find a way to determine whether a command is waiting for user input.
Solaris provides a set of tools that can be used to exercise features
of the /proc file system. One of these tools, pstack, prints a stack trace for each lightweight process
in each process. By analyzing the output of the pstack command, it is possible to determine whether
a process is waiting for user input.
If your system doesn't have the pstack command, you might be able
to use truss instead. With the -p option, truss interprets the arguments to truss as a list of process
ids for existing processes, takes control of each process, and begins tracing it.
We decided to use pstack instead of truss, because the truss command
continues to run until the command being traced completes. In contrast, pstack prints a stack trace
including the most recently executed system call and then exits, even if the process being traced
is still running. It was easier for us to analyze the output of pstack than to handle a truss command
that can run indefinitely.
To illustrate the technique, we've included a small C program,
my_ontape (Listing 1), which simulates the ontape program. The program simply prompts the
user to press return and then sleeps indefinitely.
When the user has not pressed return, executing pstack with the process
id of my_ontape as an argument produces the following output:
2632: /usr/local/bin/my_ontape
dfb69900 read (0, dfbcc0b0, 400)
dfb91002 _filbuf (80608f0) + 86
dfb92fc4 fgets (80474a0, c8, 80608f0) + d0
0805070c main (1, 8047594, 804759c) + 2c
0805061c ???????? ()
The output of pstack is formatted such that the second line contains the most
recently executed system call. Certain values vary from one execution of the program to the next.
By observing the output, we concluded that the program is waiting for user input if the second line's
3rd, 8th, and 10th fields equal the string "read (0, 400)". The following command generates
a string that can be used to test for this condition:
/usr/proc/bin/pstack 2>&- \
$(pgrep -f "^/usr/local/bin/my_ontape") \
| head -2 | tail -1 | cut -d' ' -f3,8,10
The pgrep utility determines the process id of /usr/local/bin/my_ontape
and uses it as an argument to pstack. The head/tail plumbing isolates the second line. And the cut command grabs the required fields.
To test the pstack check, we incorporated the above code into my_ontape.ss
(Figure 2). If the script finds the required string it exits with true, otherwise false.
Keep in mind that our example illustrates the Solaris pstack command
run against a specific executable. The output of pstack may vary across implementations and from
process to process. It is important to study pstack in your particular context before developing
code based on assumptions about the output.
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.
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.
|