Subject: Re: Sane exit from a program on receipt of a signal
To: None <tech-userlevel@netbsd.org>
From: Christos Zoulas <christos@astron.com>
List: tech-userlevel
Date: 07/18/2007 20:27:35
In article <20070718020949.GD958@mewburn.net>,
Luke Mewburn  <lukem@NetBSD.org> wrote:
>-=-=-=-=-=-
>-=-=-=-=-=-
>
>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 
>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.
>
>-=-=-=-=-=-
>
>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);
>}

- It should return int with an error.
- I think that you want to make sure that you exit with that signal and
  not another. See: /usr/src/lib/libc/sys/stack_protector.c
- call it raise4sure() :-) or raise_default_unblock()?

christos