Subject: Sane exit from a program on receipt of a signal
To: None <tech-userlevel@netbsd.org>
From: Luke Mewburn <lukem@NetBSD.org>
List: tech-userlevel
Date: 07/18/2007 12:09:49
--gneEPciiIl/aKvOT
Content-Type: multipart/mixed; boundary="8vCeF2GUdMpe9ZbK"
Content-Disposition: inline


--8vCeF2GUdMpe9ZbK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

A lot of the code in our tree installs signal handlers
for various reasons, and then uses exit(3) (or a variant)
to exit the application.

The problem with this is that it doesn't allow the parent
process (usually a shell) to determine if a program
exit was caused by a (probably user initiated) signal.

This is documented in more detail at:
	http://www.cons.org/cracauer/sigint.html

Rather than using exit(3) to terminate because of a user-initiated
signal, the recommended solution is to reset to the default
signal handler (SIG_DFL) and raise the signal.
E.g.;
	signal(sig, SIG_DFL);
	raise(sig);

The problem with that solution is if the signal mask for the
process is currently blocking sig (see sigprocmask(3)), which
is often the case within the currently executing signal handler,
then the raise(3) will be blocked.  Therefore, you have to
unblock sig in the signal mask before the raise(3).

(I noticed this during a sweep of the tree for user-code
to solve the original problem.)

I've written a helper function to encapsulate this extra
complexity, and attached it to this message.
It attempts to reset the signal handler for 'sig' to=20
the default, remove 'sig' from the sigprocmask(3), and
raise(3) 'sig'.  If the function returns, the caller
knows that the raise(3) didn't terminate the process.

Is it worth adding this "raise_default()" function to
the tree, say in util(3), to simplify re-use ?

If so, is the name acceptable?


cheers,
Luke.

--8vCeF2GUdMpe9ZbK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="raise_default.c"

void
raise_default(int sig)
{
	struct sigaction act;
	sigset_t mask;

	/* Reset to default signal handler, clear mask, raise signal */
	memset(&act, 0, sizeof(act));
	act.sa_handler = SIG_DFL;
	if ((sigemptyset(&act.sa_mask) == 0) &&
	    (sigemptyset(&mask) == 0) &&
	    (sigaddset(&mask, sig) == 0) &&
	    (sigaction(sig, &act, NULL) == 0) &&
	    (sigprocmask(SIG_UNBLOCK, &mask, 0) == 0))
		raise(sig);
}

--8vCeF2GUdMpe9ZbK--

--gneEPciiIl/aKvOT
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (NetBSD)

iD8DBQFGnXZtpBhtmn8zJHIRAnW1AJ0eQ0ik4o+xvIIwFsfpyNQWUU4yYgCgwQWP
c5P453/GyLO+zQk58NPz+0o=
=YwKZ
-----END PGP SIGNATURE-----

--gneEPciiIl/aKvOT--