BugFix: restore correct handling to SIGNALS in pigpio

Prior code attempted to assign a generic handler to catch ALL signals;
however SIGKILL and SIGSTOP cannot be caught; also the default action for
SIGTTOU, SIGTTIN, SIGTSTP and SIGCONT should perhaps be "ignored" NOT
"terminate", these signals are associated with Job Control and the first
three WILL halt execution until receipt of the forth SIGCONT.  As a result
the previous code will result in program termination when it is run in a
debugger such as gdb and the program is halted at a breakpoint - which is
not helpful when debugging!

The previous signal handler uses a macro "DBG(...)" however that uses
the printf(...) family of functions - **this is not safe as the signal
handler can be executed asynchronously (at any time and at any point in the
main program flow) and printf() is not re-entrant!** Indeed a big warning
ought to be put into the documentation for setgpioSetSignalFunc(...) and
gpioSetSignalFuncEx(...) that the user provided function **must be safe to
run asynchronously** - for guidance it should ONLY contain functions in the
"safe-functions" list in POSIX.1-2004 (also known as POSIX.1-2001 Technical
Corrigendum 2) which is reproduced in the signal(7) man page under the
section headed "Async-signal-safe functions" with some revisions from
POSIX.1-2008 .

The range of signals that CAN be handled ranges from SIGHUP (1) to SIGRTMAX
however SIGRTMAX is NOT a compile time constant but a runtime macro that
typically expands to 64.  There is also a gap between SIGUNUSED (a.k.a.
SIGSYS, typically 31) and SIGRTMIN (either 34 or 35 depending on glibc
version and the real-time extensions system which uses two or three
"reserved" signals internally) the man page for signal(7) explicitly
states:
"Because the range of available real-time signals varies according to the
 glibc threading implementation (and this variation can occur at run time
 according to the available kernel and glibc), and indeed the range of real
 -time signals varies across UNIX systems, programs should never refer to
 real-time signals using hard-coded numbers, but instead should always
 refer to real-time signals using the notation SIGRTMIN+n, and include
 suitable (run-time) checks that SIGRTMIN+n does not exceed SIGRTMAX."

Because the signal handler can be executed whilst the signal handler for a
particular signal is being changed it is necessary to protect against it
using something that may be being changed in the main program loop.  The
fix that this commit proposes is to maintain a shadow copy of the array of
handlers and to use an atomic flag variable to toggle between using the
main and shadow copy.

This commit restores a default "ignore" action for those signals that
typically are so treated.  For the mentioned ones here a short debugging
message is also created that warns of the data lose that will occur from
pausing the execution of the pigpio library code.  This typically happens
if a breakpoint is reached in the debugger or if <Cntl>-z is pressed during
the running of a executable.  This commit also reverts to dumping core for
signals that do that by default - this does it by restoring the default
signal action for an otherwise unhandled signal in the generic handler that
is supposed to do so and then returns to the point in the code that invokes
it - a second signal should then occur which WILL dump the core and
terminate. This is a change to previous behaviour in that NO attempt is
made to clean up {by running gpioTerminate() or exit(...)} - which would
possibly corrupt the data that the dump would otherwise contain (as well as
moving the program location away from the point where the signal was raised
for!)

For otherwise unhandled signals that do not terminate pigpio the problems
with the DBG macro and its use of printf type functions is overcome by
using individual atomic flags for each such signal - this does mean that
a helper function (void)processSignals() is required. This MUST be included
in the main loop of pigpiod and other applications using the pigpio library
to produce the actions that were previously **unsafely** done in the signal
handler code.  This is the debug level adjustments caused by SIGUSR1 and
SIGUSR2 and the debug messages that most of the others produced - those
have been extended to include the extra signals this commit recognises as
falling in this category.  To prevent "undefined behaviour" it is necessary
to change away from using sleep() in the main wait loop of pigpiod as
it is subject to that issue if SIGALARM handling is modified which we allow
fortunately nanosleep() does not suffer from the same problem.

Signed-off-by: Stephen Lyons <slysven@virginmedia.com>
This commit is contained in:
Stephen Lyons 2016-04-09 18:22:37 +01:00
parent abc878e806
commit a301cd38d6
3 changed files with 390 additions and 44 deletions

324
pigpio.c
View File

@ -1207,7 +1207,33 @@ static spiInfo_t spiInfo [PI_SPI_SLOTS];
static gpioScript_t gpioScript [PI_MAX_SCRIPTS]; static gpioScript_t gpioScript [PI_MAX_SCRIPTS];
static gpioSignal_t gpioSignal [PI_MAX_SIGNUM+1]; static gpioSignal_t gpioSignal [PI_SIG_ARRAY_SIZE];
static gpioSignal_t gpioSignalBackup [PI_SIG_ARRAY_SIZE];
/*
* These are fixed sized arrays but the needed size is technically run-time
* dependant so could be undersized if things change in the future.
*/
volatile static sig_atomic_t isGpioSignalArrayBeingEdited = SIG_ATOMIC_MIN;
/*
* Set to SIG_ATOMIC_MAX whilst being edited to cause signal handler
* to use backup set.
*/
volatile static sig_atomic_t signal_usr1_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_usr2_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_pipe_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_winch_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_pwr_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_urg_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_tstp_received = SIG_ATOMIC_MIN;
volatile static sig_atomic_t signal_cont_received = SIG_ATOMIC_MIN;
/*
* Flags for various signals that we want to report on receiving or
* use, for asynchronous safety we respond to these flags in main loop after they
* are set in the handler, rather than carrying out the wanted action from
* the handler.
*/
static gpioTimer_t gpioTimer [PI_MAX_TIMER+1]; static gpioTimer_t gpioTimer [PI_MAX_TIMER+1];
@ -5123,17 +5149,51 @@ static void dmaInitCbs(void)
static void sigHandler(int signum) static void sigHandler(int signum)
{ {
if ((signum >= PI_MIN_SIGNUM) && (signum <= PI_MAX_SIGNUM)) struct sigaction defaultAction;
memset(&defaultAction, 0, sizeof(defaultAction));
/*
* If needed to restore action to SIG_DFL for dump core cases
*/
int errno_saved = errno;
/*
* Must preserve errno to protect against changing it here (or in a user handler
* called from here)!
*/
if ((signum >= PI_MIN_SIGNUM) && (signum <= PI_MAX_USABLE_SIGNUM))
{ {
if (gpioSignal[signum].func) /*
* This code can be executed asyncronously and to protect against race conditions where
* the handler array is being modifed whilst this function wants to use it, we use an
* atomic variable as a flag to require this handler to use the backup copy until the
* editing is complete.
*/
int useBackup = isGpioSignalArrayBeingEdited;
if ( ( useBackup != SIG_ATOMIC_MIN && (gpioSignalBackup[signum - PI_MIN_SIGNUM].func) )
|| ( useBackup == SIG_ATOMIC_MIN && (gpioSignal[signum - PI_MIN_SIGNUM].func) ) )
{ {
if (gpioSignal[signum].ex) if ( useBackup != SIG_ATOMIC_MIN )
{ {
(gpioSignal[signum].func)(signum, gpioSignal[signum].userdata); if (gpioSignalBackup[signum - PI_MIN_SIGNUM].ex)
{
(gpioSignalBackup[signum - PI_MIN_SIGNUM].func)(signum, gpioSignalBackup[signum - PI_MIN_SIGNUM].userdata);
} }
else else
{ {
(gpioSignal[signum].func)(signum); (gpioSignalBackup[signum - PI_MIN_SIGNUM].func)(signum);
}
}
else
{
if (gpioSignal[signum - PI_MIN_SIGNUM].ex)
{
(gpioSignal[signum - PI_MIN_SIGNUM].func)(signum, gpioSignal[signum - PI_MIN_SIGNUM].userdata);
}
else
{
(gpioSignal[signum - PI_MIN_SIGNUM].func)(signum);
}
} }
} }
else else
@ -5141,29 +5201,81 @@ static void sigHandler(int signum)
switch(signum) switch(signum)
{ {
case SIGUSR1: case SIGUSR1:
signal_usr1_received = SIG_ATOMIC_MAX;
if (gpioCfg.dbgLevel > DBG_MIN_LEVEL) --gpioCfg.dbgLevel;
else gpioCfg.dbgLevel = DBG_MIN_LEVEL;
DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
break; break;
case SIGUSR2: case SIGUSR2:
if (gpioCfg.dbgLevel < DBG_MAX_LEVEL) ++gpioCfg.dbgLevel; signal_usr2_received = SIG_ATOMIC_MAX;
else gpioCfg.dbgLevel = DBG_MAX_LEVEL;
DBG(DBG_USER, "Debug level %d\n", gpioCfg.dbgLevel);
break; break;
case SIGPIPE: case SIGPIPE:
case SIGWINCH: signal_pipe_received = SIG_ATOMIC_MAX;
DBG(DBG_USER, "signal %d ignored", signum);
break; break;
case SIGCHLD: case SIGWINCH:
/* Used to notify threads of events */ signal_winch_received = SIG_ATOMIC_MAX;
break;
case SIGPWR:
signal_pwr_received = SIG_ATOMIC_MAX;
break;
case SIGURG:
signal_urg_received = SIG_ATOMIC_MAX;
break;
case SIGTSTP: /* Used by gdb (and other things) to pause execution */
signal_tstp_received = SIG_ATOMIC_MAX;
break;
case SIGCONT: /*
* Used by system (and gdb debugger) to resume
* execution - however we may have lost data/infomation
* from hardware during the pausing.
*/
signal_cont_received = SIG_ATOMIC_MAX;
break;
/* Used by system when it stops process when it tries to: */
case SIGTTOU: /* ==> write to a */
case SIGTTIN: /* ==> read from a */
/* TTY when not the foreground process */
case SIGCHLD: /* Used to notify threads of events */
break;
case SIGQUIT:
case SIGILL:
case SIGABRT:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
case SIGSYS:
case SIGTRAP:
case SIGXCPU:
case SIGXFSZ:
defaultAction.sa_handler = SIG_DFL;
/* May be redundant as we are setting SIG_DFL anyhow */
defaultAction.sa_flags = SA_RESETHAND;
/* Restoration of default (dump core) action */
sigaction(signum, &defaultAction, NULL);
/*
* Return to point where signal arose and cause it to be reraised
* - this time to dump core!
*/
return;
break; break;
default: default:
DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum); DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum);
/*
* The above may not be totally safe as it uses printf(), but as exit() is then
* called let us hope we are safe - though if we have had, say a SIGSEG, the following
* gpioTerminate() could still fail horribly!
*/
gpioTerminate(); gpioTerminate();
exit(-1); exit(-1);
} }
@ -5173,29 +5285,138 @@ static void sigHandler(int signum)
{ {
/* exit */ /* exit */
DBG(DBG_ALWAYS, "Unhandled signal %d, terminating\n", signum); DBG(DBG_ALWAYS, "Impossible signal %d, terminating\n", signum);
gpioTerminate(); gpioTerminate();
exit(-1); exit(-1);
} }
errno = errno_saved;
/*
* Restore errno for those cases where the signal is NOT fatal to the program
* then normal execution ought to resume right from where it left off...
*/
} }
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static void sigSetHandler(void) static void sigSetHandler(void)
{ {
int i; size_t i;
struct sigaction new; struct sigaction new;
for (i=PI_MIN_SIGNUM; i<=PI_MAX_SIGNUM; i++) for (i=0; i<=PI_SIG_ARRAY_SIZE; i++)
{ {
switch (i + PI_MIN_SIGNUM)
{
case SIGKILL: // CANNOT REPLACE THIS
case SIGSTOP: // CANNOT REPLACE THIS
continue;
default:
if( (i + PI_MIN_SIGNUM) > SIGUNUSED && (i + PI_MIN_SIGNUM) < SIGRTMIN ) {
/*
* THESE SIGNAL NUMBERS DO NOT "EXIST" (OUTSIDE OF KERNEL)
* They (either two or three in total) are used
* internally for Real Time Signals and MUST NOT
* BE USED - see manpage for signal(7)
*/
continue;
}
memset(&new, 0, sizeof(new)); memset(&new, 0, sizeof(new));
new.sa_handler = sigHandler; new.sa_handler = sigHandler;
sigaction(i, &new, NULL); sigaction(i, &new, NULL);
} }
}
} }
/* ----------------------------------------------------------------------- */
void processSignals(void)
{
/*
* Take a local copy of these two as they will cancel each other out and
* processing BOTH of them could get racy if they get modifed part way
* through this code.
*/
int local_signal_usr1 = signal_usr1_received;
int local_signal_usr2 = signal_usr2_received;
int oldDbgLevel = gpioCfg.dbgLevel;
if (local_signal_usr1 != SIG_ATOMIC_MIN && local_signal_usr2 == SIG_ATOMIC_MIN)
{
signal_usr1_received = SIG_ATOMIC_MIN;
/* We know it has been set so resetting it is not going to cause a race. */
if (gpioCfg.dbgLevel > DBG_MIN_LEVEL)
{
--gpioCfg.dbgLevel;
}
else
{
gpioCfg.dbgLevel = DBG_MIN_LEVEL;
}
}
else if (local_signal_usr1 == SIG_ATOMIC_MIN && local_signal_usr2 != SIG_ATOMIC_MIN)
{
signal_usr2_received = SIG_ATOMIC_MIN;
/* We know it has been set so resetting it is not going to cause a race. */
if (gpioCfg.dbgLevel < DBG_MAX_LEVEL)
{
++gpioCfg.dbgLevel;
}
else
{
gpioCfg.dbgLevel = DBG_MAX_LEVEL;
}
}
if ( ( local_signal_usr1 != SIG_ATOMIC_MIN || local_signal_usr2 == SIG_ATOMIC_MIN )
&& ( oldDbgLevel != gpioCfg.dbgLevel ) )
{
DBG(DBG_USER, "Debug level changed to %d\n", gpioCfg.dbgLevel);
}
/* Work through the remaining signals that we ignore apart from reporting them. */
if ( signal_pipe_received != SIG_ATOMIC_MIN )
{
signal_pipe_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d ignored", SIGPIPE);
}
if ( signal_winch_received != SIG_ATOMIC_MIN )
{
signal_winch_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d ignored", SIGWINCH);
}
if ( signal_pwr_received != SIG_ATOMIC_MIN )
{
signal_pwr_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d ignored", SIGPWR);
}
if ( signal_urg_received != SIG_ATOMIC_MIN )
{
signal_urg_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d ignored", SIGURG);
}
if ( signal_tstp_received != SIG_ATOMIC_MIN )
{
signal_tstp_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d was ignored - it is possible data was lost whilst execution was stopped after this signal", SIGTSTP);
}
if ( signal_cont_received != SIG_ATOMIC_MIN )
{
signal_cont_received = SIG_ATOMIC_MIN;
DBG(DBG_USER, "Signal %d was ignored - it is possible data was lost whilst execution was stopped before this signal", SIGCONT);
}
}
/* ----------------------------------------------------------------------- */
/* /*
freq mics net freq mics net
0 1000 1000 900 0 1000 1000 900
@ -7262,7 +7483,7 @@ static void initDMAgo(volatile uint32_t *dmaAddr, uint32_t cbAddr)
static void initClearGlobals(void) static void initClearGlobals(void)
{ {
int i; size_t i;
DBG(DBG_STARTUP, ""); DBG(DBG_STARTUP, "");
@ -7322,12 +7543,20 @@ static void initClearGlobals(void)
gpioNotify[i].state = PI_NOTIFY_CLOSED; gpioNotify[i].state = PI_NOTIFY_CLOSED;
} }
for (i=0; i<=PI_MAX_SIGNUM; i++) for (i=0; i<=PI_MAX_USABLE_SIGNUM; i++)
{
gpioSignalBackup[i].func = gpioSignal[i].func;
gpioSignalBackup[i].ex = gpioSignal[i].ex;
gpioSignalBackup[i].userdata = gpioSignal[i].userdata;
}
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MAX; /* Force signal handler to use backup array */
for (i=0; i<=PI_MAX_USABLE_SIGNUM; i++)
{ {
gpioSignal[i].func = NULL; gpioSignal[i].func = NULL;
gpioSignal[i].ex = 0; gpioSignal[i].ex = 0;
gpioSignal[i].userdata = NULL; gpioSignal[i].userdata = NULL;
} }
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MIN; /* Restore signal handler to use normal array */
for (i=0; i<=PI_MAX_TIMER; i++) for (i=0; i<=PI_MAX_TIMER; i++)
{ {
@ -7599,6 +7828,7 @@ int initInitialise(void)
} }
#ifndef EMBEDDED_IN_VM #ifndef EMBEDDED_IN_VM
if ( SIGRTMAX > PI_MAX_SIGNUM ) DBG(DBG_ALWAYS, "Signal handler table undersized, please report following values, needed: %i, current: %i", (SIGRTMAX), PI_MAX_SIGNUM);
sigSetHandler(); sigSetHandler();
#endif #endif
@ -11153,17 +11383,32 @@ int gpioDeleteScript(unsigned script_id)
int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f) int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
{ {
size_t i;
DBG(DBG_USER, "signum=%d function=%08X", signum, (uint32_t)f); DBG(DBG_USER, "signum=%d function=%08X", signum, (uint32_t)f);
CHECK_INITED; CHECK_INITED;
if (signum > PI_MAX_SIGNUM) if ( signum < PI_MIN_SIGNUM /* Invalid */
|| signum > PI_MAX_USABLE_SIGNUM /* Invalid */
|| signum == SIGKILL /* Cannot be handled */
|| signum == SIGSTOP /* Cannot be handled */
|| ( signum > SIGUNUSED && signum < SIGRTMIN ) ) /* Kernel reserved */
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum); SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 0; for(i=0; i<PI_SIG_ARRAY_SIZE; ++i)
gpioSignal[signum].userdata = NULL; {
gpioSignalBackup[i].func = gpioSignal[i].func;
gpioSignalBackup[i].ex = gpioSignal[i].ex;
gpioSignalBackup[i].userdata = gpioSignal[i].userdata;
}
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MAX; /* Force signal handler to use backup array */
gpioSignal[signum].func = f; gpioSignal[signum - PI_MIN_SIGNUM].ex = 0;
gpioSignal[signum - PI_MIN_SIGNUM].userdata = NULL;
gpioSignal[signum - PI_MIN_SIGNUM].func = f;
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MIN; /* Restore signal handler to using modified normal array */
return 0; return 0;
} }
@ -11174,18 +11419,33 @@ int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f, int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f,
void *userdata) void *userdata)
{ {
size_t i;
DBG(DBG_USER, "signum=%d function=%08X userdata=%08X", DBG(DBG_USER, "signum=%d function=%08X userdata=%08X",
signum, (uint32_t)f, (uint32_t)userdata); signum, (uint32_t)f, (uint32_t)userdata);
CHECK_INITED; CHECK_INITED;
if (signum > PI_MAX_SIGNUM) if ( signum < PI_MIN_SIGNUM /* Invalid */
|| signum > PI_MAX_USABLE_SIGNUM /* Invalid */
|| signum == SIGKILL /* Cannot be handled */
|| signum == SIGSTOP /* Cannot be handled */
|| ( signum > SIGUNUSED && signum < SIGRTMIN ) ) /* Kernel reserved */
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum); SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 1; for(i=0; i<PI_SIG_ARRAY_SIZE; ++i)
gpioSignal[signum].userdata = userdata; {
gpioSignalBackup[i].func = gpioSignal[i].func;
gpioSignalBackup[i].ex = gpioSignal[i].ex;
gpioSignalBackup[i].userdata = gpioSignal[i].userdata;
}
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MAX; /* Force signal handler to use backup array */
gpioSignal[signum].func = f; gpioSignal[signum - PI_MIN_SIGNUM].ex = 1;
gpioSignal[signum - PI_MIN_SIGNUM].userdata = userdata;
gpioSignal[signum - PI_MIN_SIGNUM].func = f;
isGpioSignalArrayBeingEdited = SIG_ATOMIC_MIN; /* Restore signal handler to using normal array */
return 0; return 0;
} }

View File

@ -696,10 +696,32 @@ typedef void *(gpioThreadFunc_t) (void *);
#define PI_SCRIPT_WAITING 3 #define PI_SCRIPT_WAITING 3
#define PI_SCRIPT_FAILED 4 #define PI_SCRIPT_FAILED 4
/* signum: 0-63 */ /* signum: 1 to ~64 */
#define PI_MIN_SIGNUM 0 #define PI_MIN_SIGNUM (SIGHUP)
#define PI_MAX_SIGNUM 63 /*
* The lowest signal number is 1 NOT 0, zero is a test case that checks whether
* a signal can be sent to a process, but it is not a signal that can be sent
* (so it cannot be "handled")!
*/
#define PI_MAX_SIGNUM 64
/*
* We should use SIGRTMAX but it is a macro, NOT a constant, that expands to a
* system dependent value at run-time, typically it will be 64 but it need not
* be so. If it ever changes to be more this will need to be adjusted
* upwards, until then this sets a highest usable limit (and is used to size)
* the fixed array size at compile time - if SIGRTMAX is lower then the
* following is reduced accordingly at runtime.
*/
#define PI_SIG_ARRAY_SIZE (PI_MAX_SIGNUM - PI_MIN_SIGNUM +1)
#define PI_MAX_USABLE_SIGNUM ((PI_MAX_SIGNUM) < (SIGRTMAX) ? (PI_MAX_SIGNUM) : (SIGRTMAX))
/*
* Use PI_MIN_SIGNUM to PI_USABLE_MAX_SIGNUM to identify range at runtime but
* PI_SIG_ARRAY_SIZE to size array of signal handlers at
* compile time.
*/
/* timetype: 0-1 */ /* timetype: 0-1 */
@ -3263,20 +3285,41 @@ int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f);
Registers a function to be called (a callback) when a signal occurs. Registers a function to be called (a callback) when a signal occurs.
. . . .
signum: 0-63 signum: PI_MIN_SIGNUM - PI_MAX_USABLE_SIGNUM (1 to 64 is typical
but theoretically could change in future)
f: the callback function f: the callback function
. . . .
Returns 0 if OK, otherwise PI_BAD_SIGNUM. Returns 0 if OK, otherwise PI_BAD_SIGNUM. SIGKILL and SIGSTOP signals
cannot have a handler registered and a few numbers between the
range of the highest POSIX one (typically 31) and the lowest
REAL-TIME one (typically 34 or 35 depending on system libraries)
are used internally by the kernel for real-time signals and also
cannot have a function registered.
The function is passed the signal number. The function is passed the signal number.
One function may be registered per signal. One function may be registered per signal.
Only one of gpioSetSignalFunc or gpioSetSignalFuncEx can be
registered per signal.
The callback may be cancelled by passing NULL. The callback may be cancelled by passing NULL.
By default all signals are treated as fatal and cause the library By default all BUT the signals listed below are treated as fatal
to call gpioTerminate and then exit. and cause the library to call gpioTerminate and then exit:
SIGUSR1 - Decrease Debug level
SIGUSR2 - Increase Debug level
SIGPIPE - Ignored
SIGWINCH- Ignored
SIGPWR - Ignored
SIGURG - Ignored
SIGTTIN - Ignored (but pauses execution - can cause data loss!)
SIGTTOU - Ignored (but pauses execution - can cause data loss!)
SIGTSTP - Ignored (but pauses execution - can cause data loss!)
SIGCONT - Ignored (but resumes execution)
SIGCHLD - Ignored
D*/ D*/
@ -3287,7 +3330,8 @@ int gpioSetSignalFuncEx(
Registers a function to be called (a callback) when a signal occurs. Registers a function to be called (a callback) when a signal occurs.
. . . .
signum: 0-63 signum: PI_MIN_SIGNUM - PI_MAX_USABLE_SIGNUM (1 to 64 is typical
but theoretically could change in future)
f: the callback function f: the callback function
userdata: a pointer to arbitrary user data userdata: a pointer to arbitrary user data
. . . .
@ -3302,6 +3346,31 @@ registered per signal.
See gpioSetSignalFunc for further details. See gpioSetSignalFunc for further details.
D*/ D*/
/*F*/
void processSignals(void);
/*D
Internal helper that MUST be included in the main application loop. It
is needed to respond to (possibly asynchronous) signals that are not
fatal to the application but are not handled by user provided functions
registered with gpioSetSignalFunct or gpioSetSignalFunctEx.
Currently this is needed to handle SIGUSR1 and SIGUSR2 which adjust the
logging/debugging information up and down one step for each signal.
It also logs receipt of SIGPIPE, SIGWINCH, SIGURG, SIGPWR and SIGCONT
(and a preceeding SIGTSTP if that was the cause of execution being paused)
- by default these are otherwise ignored.
Note that SIGTTOU and SIGTTIN are also ignored by default but, as they are
associated with the application not being currently the foreground process
but trying to write to or read from the terminal as if it was, it is not
wise to produce any debug output for them.
If the application has been paused by SIGTSTOP, SIGTTOU, SIGTTIN or the
impossible to catch SIGSTOP, and then resumed by recept of SIGCONT it is
possible that the parts of the pigpio that monitor activity of the GPIO
system may have lost data whilst paused.
D*/
/*F*/ /*F*/
uint32_t gpioRead_Bits_0_31(void); uint32_t gpioRead_Bits_0_31(void);

View File

@ -252,6 +252,7 @@ int main(int argc, char **argv)
{ {
pid_t pid; pid_t pid;
int flags; int flags;
struct timespec mainLoopSleep;
/* Fork off the parent process */ /* Fork off the parent process */
@ -335,11 +336,27 @@ int main(int argc, char **argv)
/* sleep forever */ /* sleep forever */
mainLoopSleep.tv_sec = 5;
mainLoopSleep.tv_nsec = 0;
while (1) while (1)
{ {
/* cat /dev/pigerr to view daemon errors */ /* cat /dev/pigerr to view daemon errors */
sleep(5); nanosleep( &mainLoopSleep, NULL );
/*
* Have moved non-terminal signal reporting out of handler so must
* now process the reports/actions that are needed. Currently this
* is adjusting debug log level (SIGUSR1 & SIGUSR2) and reports for
* SIGPIPE, SIGWINCH, SIGPWR, SIGURG, SIGCONT & SIGTSTP (though the
* last can be seen only after the SIGCONT that resumes from it).
*
* Note that nanosleep(...) calls will stop waiting if a signal is
* raised. We now use nanosleep() rather than the original sleep()
* because that is subject to "undefined behavour" if SIGALARM handling
* is modifed (which we do in pigpio).
*/
processSignals();
fflush(errFifo); fflush(errFifo);
} }