September 2000

Interrupt Handling In Perl

Program interrupt handling is generally a pretty easy topic, especially in languages that have been doing it - literally - for decades (C for example). This article will look at interrupt handling in Perl or rather additional interrupt handling in Perl. First I will cover the whys and why nots and then follow up with some code samples.

What Do You Mean Additional?

The perl interpreter has built in interrupt handling, additionally, the environment one is using a Perl program in most likely has it as well (e.g. a shell). So when I say interrupt handling, I mean additional specialized handling or just nice to know general handling.

Why Use Additional Interrupt Handling?

For the most part, debugging. A lot of programmers already do interrupt handling or have built in methods to handle errors in the environment such as a web server, but for many command line driven programs, you may wish to trap signals and log them for debugging and troubleshooting purposes. A case where I saw it come in very handy was when I took a look at Jeremy Fischer's program DHCPReg where a little interrupt handling might be helpful. The program gets a variety of different information about DHCP leases. It is command line driven, so, seeing that there might be the possibility of keyboard interrupts or other weirdness, I contributed the idea of using some rudimentary interrupt handling. Jeremy went all out and made a Perl module called Interrupt.pm so he could easily reuse the simple interrupt handlers.

Finally, another good reason to use interrupt handlers (in any program really) is in case there is some cleanup involved such as deleting temporary files, closing open handles etc.

Why Not?

Basically if there is no need or there is another mechanism in place for doing so, a classic example is web servers. Most web servers have built in error handling for cgi program errors and adjustable error logging facilities.

Onto to the Samples!

The first thing we need to do is identify the signals we wish to trap and tell them (should they be trapped) a routine name to use. For our first example, we will use a sub called - you guessed it - interrupt:

1. $SIG{'INT' } = 'interrupt';  $SIG{'QUIT'} = 'interrupt';
2. $SIG{'HUP' } = 'interrupt';  $SIG{'TRAP'} = 'interrupt';
3. $SIG{'ABRT'} = 'interrupt';  $SIG{'STOP'} = 'interrupt';

That should be pretty simple, the $SIG{'*'} are jumping to the interrupt sub function. So now how about a look at the actual function:

1. sub interrupt {
2.      my($signal)=@_;
3.      print "Caught Interrupt\: $signal \n";
4.      print "Now Exiting\n";
5.      exit(1);
6. }
  1. The sub function beginning.
  2. Here we assign the in-bound string to $signal.
  3. Next a nice little print message that will show us the name of the signal caught.
  4. Finally we exit out.
  5. close the sub function.

Again, pretty simple stuff but very helpful for troubleshooting. The function, however, does not do much and it could use some help such as adding the die to it. Let's take it a bit further and add some better functionality to it, we will assume here that we have an open file-handle called LOG:

1. sub interrupt {
2.    my($signal)=@_;
3.    &log("Interrupt: Caught signal $signal exiting\n");
4.    &clean_die("Caught signal $signal exiting\n");
5.    exit(1);
6.}
  1. beginning of sub function.
  2. once again we put the signal name into $signal
  3. Here we call the sub function log to put some information about what happened into the log-file (see below).
  4. Now we call the sub function clean_die to take care of business (see below).
  5. We go ahead and exit the program.
  6. end of the sub function.

Now lets take a look at the called sub functions to see what they do:

1. sub log {
2.     local($string)=$_[0];
3.     if($log){
4.         print LOG &date,": $string\n";
5.         return 1;
6.     }
7.     return 0;
8. }
  1. beginning of the sub function.
  2. once again we take the in-bound information and assign it, here it is $string.
  3. If our log-file exists:
  4. we go ahead and append our information to it.
  5. we notify the caller all went well . . .
  6. close the if
  7. uh-oh - our log-file was not found.
  8. end of sub function.
1. sub clean_die {
2.     my($die_string) = $_[0];
3.     &log("Clean_die: $die_string");
4.     close(LOG) if ($log);
5.     die $die_string;
6. }
  1. beginning of the sub function.
  2. rename the in-bound string to $die_string
  3. call sub function log and log the event.
  4. close the log-file file handle if it exists.
  5. let the string die
  6. end sub function.

As you can see these sub functions are pretty simple and being that it is Perl, very flexible. One could easily make all of these one sub function or get really extravagant with them - that is up to the programmer. You may see something about these examples you don't like or may be incorrect - feel free to roll your own.

Additional Flexibility

Piled on top of just altering the examples I have shown, a completely different approach can be taken for handling interrupt signals, you could use different sub functions for each type of signal for example. Then your initial assignment of them might look something like:

1. $SIG{'INT' } = 'int ';       $SIG{'QUIT'} = 'quit';
2. $SIG{'HUP' } = 'hup ';       $SIG{'TRAP'} = 'trap';
3. $SIG{'ABRT'} = 'abrt';       $SIG{'STOP'} = 'stop';

Or break them into groups that get handled different etc. - the sky is the limit really. My only word of advice is if you choose to use a method like the ones discussed in this article, try to keep it simple. If all you need is rudimentary handling then I suggest picking up the module mentioned in the beginning of the text.