My first thought is incorrect signal processing but there is not enough information in your post to write test code to replicate your failure. But I can give you some places to look. Pardon me if I cover a few signal basics you already know for future readers.
First of all I do not know if you are using the legacy signal() or the new POSIX sigaction() signal routines to catch signals. sigset() is a useful in between from GNU.
Legacy Signals -- signal()
It's near impossible, if not impossible, to guarantee an air-tight signal processor using the original signal processor in all environments.
- On some UNIX systems entering the signal handler can reset the handler to the default condition. Subsequent signals are guaranteed to be lost unless the handler explicitly reset the signal.
- signal() handlers must not assume they get called once for each signal.
- Handlers must do a
while( ( pid = waitpid( -1, &signal, WNOHANG ) ) > 0 )
loop,
until no more signals are found as legacy signals set a bool condition
indicating at least one signal is outstanding.
The actual count is unknown.
- Handlers must allow for no signals being found if a prior while() loop processed
the signal.
- Allow for signals from unknown processes... if the program you start also starts
a grandchild process you may inherit that process if your child exits quickly.
Advice, hold your nose and flee from legacy signals.
Lack of a while() loop in a legacy handler and multiple SIGCHILDs, one from your sudo and one or more from unexpected grandchildren fired off by sudo. If only one SIGCHILD is handled when a grandchild signal comes in first, the expected program's signal will not be caught.
POSIX Signals -- sigaction()
POSIX signals can clean up all of the failures of legacy signals.
- Set a handler, without a restore (restore is NOT part of POSIX signals and is often,
at least in my mind, evil when you might get more than one signal to handle in the same way).
- sigaction() signals are sticky... they live until expressly changed (wonderful!).
None of this troublesome requirement of having to reset the signal handler again
in the handler.
- Set a mask to mask out the current signal when processing the signal. Paranoids will also mask any other signal passed to the same handler.
Lack of a mask can cause weird stuff like loosing track of a signal if you get a SIGCHILD while in a SIGCHILD handler.
GNU -- sigset()
GNU provides an useful in-between that has the same calling signatures as signal() but removes most of the problems. Some additional control functions are also available. Using sigset() is an easy fix for many signal problems.
Reminders
Think of signal handlers as threads in your program,
even if you are not otherwise using threads in the code.
In days of old you needed to do absolutely minimal processing in signal handlers... no calling of library code,
such as printf, that have side effects.
I still follow this when having to use legacy signal handlers and always use multithread cautions in newer handlers.
strace(1)
output ofsudo(8)
when run by your shell and when run by a standard system shell. Because tracing fiddles with the setuid permissions on executables, you'll need to attachstrace
aftersudo
has started but before it does much work;sudo -k
first will forcesudo
to re-prompt for a password, and while it is waiting, you can find its pid and runstrace -o /tmp/out -f -p <pid>
. – Burettesudo vi
via bash? It does not happen there. – Gahansudo
doesn't rely on CHLD exclusively, IIRC, but that certainly wouldn't help things. – Prototype