mirror of https://github.com/joan2937/pigpio
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:
parent
abc878e806
commit
a301cd38d6
324
pigpio.c
324
pigpio.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
85
pigpio.h
85
pigpio.h
|
@ -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);
|
||||||
|
|
19
pigpiod.c
19
pigpiod.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue