[net.unix-wizards] Printing in signal handlers

ksbszabo@wateng.UUCP (Kevin Szabo) (03/19/85)

In article <312@calgary.UUCP> radford@calgary.UUCP (Radford Neal) writes:
>I think the moral of all this is:
>     - Don't use signal handlers if you can avoid it, especially for anything
>       other than printing an error message and exiting.
>
>     - If you have to use signal handlers, you better think REALLY carefully
>       about what could happen, or you may write a program which fails 
>       inexplicably once every few weeks.

Your second moral should be applied BEFORE you apply the first moral.
For instance, you can really confuse STDIO if you were to do the following
in a signal handler:

BadSignal( n )
{
	fprintf(stderr,"Huh? Caught unexpected signal %s!\n", list_of_sigs(n) );
}
Stdio could have been in the middle of a malloc for flsbuf() or something.
You could have gotten a core dump if you were unlucky enough...


The problem is that practically none of STDIO is re-entrant. The only
thing you can trust in a signal handler is a direct call to the kernel.

Thus any printing in a signal handler should probably look like:
(I might have the arguments in the wrong order to write()  )

BadSignal( n )
{
	extern	caught_signal;
	char	*msg;

	msg = "Huh? ...";
	while( *msg ) {
		if ( write( fileno(stderr), msg, 1 ) != 1 ) abort();
		msg++;
	}
	....spit out the rest of the message....

	/* tell the rest of the program that a signal occurred. We'll
	 * handle it outside of the signal handler because it is safer.
	 * Multiple signals will get lost, but they usually do under UNIX
	 * anyway */

	caught_signal = n;
}

Note: The abort() is to get a core dump in case you have done something
funny to stderr. You better not be catching SIGILL (or whatever abort 
causes) otherwise on some systems (4.2 BSD) you will just get an infinite
loop.

So maybe the first moral of the story is: don't do any more than set
an external in a signal handler, otherwise be prepared to look long
and hard at what you are doing.

						Kevin
-- 
Kevin Szabo  watmath!wateng!ksbszabo (U of Waterloo VLSI Group, Waterloo Ont.)

geoff@utcs.UUCP (Geoff Collyer) (03/21/85)

In article <2175@wateng.UUCP> ksbszabo@wateng.UUCP (Kevin Szabo) writes:
>The problem is that practically none of STDIO is re-entrant. The only
>thing you can trust in a signal handler is a direct call to the kernel.

I think the real problem is that UNIX allows one to catch signals at all.
Given that we don't have cheap processes a la Thoth, catching signals is a
necessary evil but signal handlers should be kept simple and in particular
should avoid using longjmp (if you have ever read and understood the source,
you won't want to use longjmp).  At most, setting a global flag often
suffices; this also may allow one to omit calls to signal to enable
and disable signal catching around critical sections by simply noting
the state of the flag at appropriate places.

I can't speak for all UNIXes, but the system call interface routines
in the PDP-11 v7 C library often aren't re-entrant either: they copy
their arguments into static storage, then issue the system call pointing
at the static storage.  If you interrupt such a routine (say signal(2))
while it is copying its arguments and in the signal catcher you invoke
the same system call, then return from the signal catcher without calling
longjmp, the first instance of signal will continue to execute, though
some of its static storage was trashed by the second call in the signal
handler.  So if you are going to make those direct calls to the kernel
in your signal handler, you'd better do it from assembler or verify
that your C library system call interfaces are re-entrant.

This is something that I believe Whitesmith's got right:
last time I looked they had a standard routine for issuing system calls
that left the arguments on the stack and made the system call point
at the stacked arguments.

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (03/23/85)

My recommendation is to do little more than set a flag in the signal
catcher and return.  Test this flag at appropriate places in the
main program control loop.  This is much safer than trying to do
anything useful from inside a signal catcher.