[comp.unix.questions] Proper exit handling

Kemp@DOCKMASTER.NCSC.MIL (03/27/89)

   Does anyone know of a bulletproof method of installing an exit
handling routine that is guaranteed to be called when a process
terminates?

   I have a C program running under SunOS 4.0.1 which sets the terminal
input mode to the equivalent of cbreak and noecho.  When the user types
any key, the terminal input mode is restored to its normal state and the
process exits.  (The program plays files through a D/A converter, but
that is not particularly relevant to this discussion.)

   I start by trapping a few terminal related signals:

          signal(SIGHUP, onterm);
          signal(SIGINT, onterm);
          signal(SIGTERM, onterm);
          signal(SIGQUIT, SIG_IGN);
          signal(SIGTSTP, SIG_IGN);

   The handler just sets a global flag and returns; the main program
checks the flag and takes appropriate action:

 void onterm(sig)
 int sig;
 {
          done = 1;
          if (sig == SIGINT) done++;
 }


   This works fine under normal circumstances, but if something unusual
happens which kills the process (bus error, arithmetic exception, or any
of a million other things), the terminal will be left in hosed mode.  I
could trap the 20 signals that cause a process to die, but that is a
gross and ugly hack, and besides what about SIGSTOP and SIGKILL, which
cannot be caught?

   What is needed is an exit handler that will always be called when the
process dies, whatever the reason.  If anyone knows The Right Way to do
this, please post it.  I'm sure everyone here is interested in writing
quality code that works first time, every time!

   Thanks,
    Dave Kemp (Kemp@dockmaster.arpa)

p.s.  Can anyone tell me where in TFM to find documentation on SIGTERM
(the software termination signal)?  It would be nice if this were sent
by exit() TO the terminating process, but I assume that it is just
another of the signals that causes the process to terminate.  SIGNAL(3)
doesn't say anything about it except to list its value.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/27/89)

In article <18832@adm.BRL.MIL> Kemp@DOCKMASTER.NCSC.MIL writes:
>   Does anyone know of a bulletproof method of installing an exit
>handling routine that is guaranteed to be called when a process
>terminates?

Try running the "real" process as the child of another "monitor"
process.  The monitor would, of course, save the initial terminal
state, fork/exec the child, wait for the child to terminate, then
reset the terminal state and finally report the child exit status.

gregg@ihlpb.ATT.COM (Wonderly) (03/28/89)

From article <18832@adm.BRL.MIL>, by Kemp@DOCKMASTER.NCSC.MIL:
>    Does anyone know of a bulletproof method of installing an exit
> handling routine that is guaranteed to be called when a process
> terminates?
> ... 
>    The handler just sets a global flag and returns; the main program
> checks the flag and takes appropriate action:
> 
>  void onterm(sig)
>  int sig;
>  {
>           done = 1;
>           if (sig == SIGINT) done++;

You might add

            signal (sig, SIG_IGN);

here in case the user bangs on the INTR/QUIT keys several times when the
system is busy.

>  }
> 
> p.s.  Can anyone tell me where in TFM to find documentation on SIGTERM
> (the software termination signal)?  It would be nice if this were sent
> by exit() TO the terminating process, but I assume that it is just
> another of the signals that causes the process to terminate.  SIGNAL(3)
> doesn't say anything about it except to list its value.

SIGTERM is the default signal for kill(1) and, more interestingly, it is
the signal sent to all processes, by the kernal, when a shutdown is
requested.  It means what it says, TERMinate.  The SIGTERM handler in
daemon processes like cron(1), uucp(1), and other behind the scenes
programs cleans up everything that should not be present for the program
to start back up normally.  This is typically lock files like the tty
lock files that uucp(1) creates.  SIGHUP of course is delivered to processes
when the terminal that they are associated with 'loses carrier'.  This
differentiation while not necessary in most cases may be and since these
are two distinct events, it is convienent to convey them as such.

I typically cover the kinds of problems that you are talking about with
a segment of code such as this

	{
		int i;
		extern int handler();

		for (i=1; i < NSIG; ++i)
			signal (i, handler);

	#ifdef	SIGCLD
		signal (SIGCLD, SIG_DFL);
	#endif

	#ifndef	SIGCHLD
		signal (SIGCHLD, SIG_DFL);
	#endif

	}

For the other special signals which are in BSD (SIGTTIN etc) you may or may
not need special settings for those.

If you have problems with an application core dumping because of coding
errors, those are usually the times when you would like to use a handler
such as

	handler(sig)
		int sig;
	{
		int pid;

		switch (sig) {
			case SIGBUS:
			case SIGSEGV:
			case SIGFPE:
			case SIGEMT:
			case SIGILL:
				if ((pid = fork()) == 0) {
					chdir ("/tmp");
					setuid (getuid());
					setgid (getgid());
					return;
				}
				wait (0);
				/*  FALL THROUGH to clean up code.  */

			default:
				/*  Do something to clean up terminal settings and such */

		}
	}

Which will (if instructions can be restarted) drop a core in /tmp which
might help to debug your problem, while still cleaning up the terminal
and other things.  I use this frequently and find it very useful.

-- 
Gregg Wonderly                             DOMAIN: gregg@ihlpb.att.com
AT&T Bell Laboratories                     UUCP:   att!ihlpb!gregg

rbj@dsys.icst.nbs.gov (Root Boy Jim) (05/04/89)

? Posted-Date:  27 Mar 89 08:43 EST
? Date:  Mon, 27 Mar 89 08:42 EST
? From: Kemp@DOCKMASTER.DCA.MIL

?    Does anyone know of a bulletproof method of installing an exit
? handling routine that is guaranteed to be called when a process
? terminates?

[discussion of trapping signals deleted]

?    This works fine under normal circumstances, but if something unusual
? happens which kills the process (bus error, arithmetic exception, or any
? of a million other things), the terminal will be left in hosed mode.  I
? could trap the 20 signals that cause a process to die, but that is a
? gross and ugly hack, and besides what about SIGSTOP and SIGKILL, which
? cannot be caught?

Well as you said, there is nothing you can do about SIGSTOP or SIGKILL,
but I can map your list of 20 signals down to one (perhaps 2): SIGCHLD.
Save the original terminal state, fork a child, wait for it, when it
dies, reset the terminal state. Things can get more complicated if the
program is expected to be suspended with ^Z. In that case, the parent
should probably ignore SIGTSTP and let the child muck with the settings.

?    Thanks,
?     Dave Kemp (Kemp@dockmaster.arpa)

? p.s.  Can anyone tell me where in TFM to find documentation on SIGTERM
? (the software termination signal)?  It would be nice if this were sent
? by exit() TO the terminating process, but I assume that it is just
? another of the signals that causes the process to terminate.  SIGNAL(3)
? doesn't say anything about it except to list its value.

It's just another signal you can send, with the conventional meaning
of "Nothing's wrong, but we don't need your services anymore. Don't
go away mad, just go away." A good example can be found in init(8).

	Root Boy Jim is what I am
	Are you what you are or what?