Signals and Scripts
John Lees
This article explains what UNIX signals are and why
you might want
to catch them in a shell script. I take a look at the
signal handling
abilities of the Bourne and Korn shells, the C shell,
and perl. Examples
show how you can use signal handling to make your system
administration
scripts friendlier.
Signals
When an important asynchronous event occurs, the operating
system
sends a signal to the affected process. Signals are
sometimes called
interrupts, because they interrupt the normal execution
of a process.
Some events that can cause signals to be sent are hanging
up your
connection, typing Control-C, dividing by zero, and
running out of
memory.
When you type Control-C, for example, the operating
system sends a
SIGINT signal to the process in the foreground. If you
are sitting
at the shell prompt or typing a command line, this process
is your
login shell; otherwise, it is the process you are running
(in a pipe,
usually the final process).
If the process receiving the signal is a shell interpreting
a script,
you can write the script to catch some signals and do
something appropriate
in response. For example, when the user types that Control-C,
the
default action of a shell is to stop executing the script,
which may
leave an untidy mess of temporary files behind.
You can write the script to remove temporary files and
perform
other cleanup actions. You can even choose to ignore
a Control-C,
though a Control-Z can stop the process. Then the process
can be killed.
Table 1 shows some signals that are found in most flavors
of UNIX.
These are the signals that can originate from something
a user does.
There are many other signals, corresponding to such
asynchronous events
as illegal instructions, memory errors, and so on. It
generally makes
no sense for a shell script to catch such signals.
Signals may be referenced by name or by number. In Bourne
and C shell
scripts you use the signal number; in perl, you use
the name but without
the "SIG". See /usr/include/sys/signal.h and
the man
pages for kill, signal, and termio for detailed
information on the signals supported by your operating
system.
You can do one of three things with these signals. Let
the shell take
its default action (terminate the script). Ignore the
signal. Catch
the signal and do something useful.
As an aside, when you fork a child process and then
wait for it to
terminate, you are using signal handling. The parent
process receives
a signal when a child process dies. The wait just puts
the parent
to sleep until this happens.
Scripts
There are some basic differences in the signal handling
capabilities
of the three script languages (the Bourne and Korn shells
have the
same facilities). The Bourne shell provides a facility
to execute
an arbitrary sequence of commands on any signal and
also on normal
exit (signal "0"). The latter is very handy
for doing miscellaneous
cleanup like removing temporary files. Different sequences
of commands
can be associated with different signals.
The C shell provides a transfer of control on signals,
but does not
implement the useful "normal exit" signal
of the Bourne shell.
One transfer point is used to handle all signals, and
there is no
way to resume processing after handling a signal. In
a C shell script
you can use this transfer to clean up temporary files
before exiting,
but that is pretty much the extent of what you can do
-- just one
more reason why the C shell is the last choice for writing
system
administration scripts. (Some other reasons are the
difficulty of
redirecting standard error and the lack of shell procedures.)
The rich and wonderful perl language lets you specify
a routine
to be executed for each signal. A common signal handler
can easily
tell which signal has occurred (the name of the signal
is supplied
to the handler routine as an argument). Version 5 of
perl provides
an "END" action much like that of awk that
can be
used to clean up at termination.
Examples
The first example is looking through the files in a
user's home directory
and compiling a list of files for likely removal. The
list is in the
common "ls -lR" format, which shows the maximum
amount of
information without generating excessively long path
names that tend
to wrap around on the monitor. The user is given several
options as
to how to proceed: quit, generate a file of removal
commands for later
execution (perhaps after editing, and to serve as a
record of what
is removed), or interactive removal right now. Figure
1 is an example
Bourne shell implementation; Figure 2 is the perl version.
This example makes a very simple match against filename
suffixes.
You may want to use a longer list of suffixes, plus
perhaps checking
for files older than, say, two years, of size zero,
larger than some
maximum size, and so on. The last time my server crashed
and all
the disks were fsck'd, I noticed that there were over
160,000
user files. I bet there is some deadwood in there! I'm
going to be
encouraging my users to use this script, you betcha.
Figure 3 is an example Bourne shell script that waits
thirty seconds.
If the user hits Control-C, he or she is informed of
the time remaining
to wait, but the wait goes on. Figure 4 is an attempt
at doing this
in the C shell. It illustrates the shortcomings of the
C shell in
this area.
Figure 5 shows how to use the BEGIN and END actions
in version 5 of
perl. Version 5 of perl is now available and I encourage
you to make
the switch. Scripts written in perl version 4 will work
with perl
5 with little or no modification.
Which Script Language?
The Bourne shell is undoubtedly the first choice for
portable system
administration scripts. This shell is found on all UNIX
systems
with little or no variation. If your scripts must run
everywhere,
use the Bourne shell.
Although the Korn shell offers many enhancements for
interactive use,
it is basically identical to the Bourne shell for writing
scripts.
The Korn shell is not commonly found on BSD systems.
Bash (The FSF's Bourne again shell) is acceptable and
is freely available,
but is of course not found on all systems. Because perl
is likely
to be available on any system that has Bash, I would
not consider
Bash, except that for true portability you should make
certain that
your Bourne shell scripts run under Bash.
The C shell is now quite common, but it is so weak that
I would not
use it for this purpose. The same comment applies to
variants such
as tcsh.
perl is the ideal choice for writing system administration
scripts,
but it is not yet a standard part of most operating
system distributions.
I have heard that Sun intends to distribute perl with
Solaris, but
I don't know when this will begin. I do expect that
eventually perl
will be found on any system that dares to call itself
UNIX.
perl programs are scripts in the sense that they remain
in source
form. A perl script is "compiled" each time
it is run, which
is why you are informed of syntax errors before any
action takes place.
With Bourne or C shell scripts, syntax errors are not
found until
that part of the script is executed. (This is why funny
things happen
when you edit your .login file. If you start a window
manager
from your .login file, your login shell may still be
interpreting
the .login file when you quit the window manager and
logout.
The shell keeps a pointer to where it should resume
interpreting.
If you edit the .login file, that pointer may now be
to the
middle of a line.)
Summary
Making your scripts signal-smart is a good idea. You
can leave fewer
junk files lying around and make life easier for the
users. Learn
perl. By the time you read this, the new improved perl
5 should be
widely available.
Bibliography
Schwartz, Randal. Learning Perl. Sebastopol, CA: O'Reilly
&
Associates, 1993. ISBN: 1-56592-042-2.
Wall, Larry, and Randal Schwartz. Programming perl.
Sebastopol,
CA: O'Reilly & Associates, 1991. ISBN: 0-937175-64-1.
About the Author
John Lees has an M.S. in computer science and has worked
during
the past twenty years about equally as a teacher, technical
writer,
programmer, and system administrator. His computer experience
began
in the days of front panels and paper tape, and he doesn't
have enough
fingers and toes to count the operating systems he has
used. His love/hate
relationship with UNIX dates to early 1985. Currently
Mr. Lees is
a systems analyst with the Department of Computer Science,
and manager
of the Pattern Recognition and Image Processing Laboratory,
at Michigan
State University. He is a member of ACM, Computer Professionals
for
Social Responsibility, the League for Programming Freedom,
the Society for
Technical Communication, and the TeX Users Group.
|