Improve signal handling. (#532)

This commit is contained in:
Guy McSwain 2023-01-09 20:57:41 -06:00 committed by GitHub
parent 27b5ea9c58
commit 3e0a5085f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 330 additions and 15 deletions

230
pigpio.c
View File

@ -984,6 +984,10 @@ typedef struct
callbk_t func;
unsigned ex;
void *userdata;
struct sigaction old;
unsigned set;
unsigned dfl;
unsigned skip; // preset || SIG_IGN
} gpioSignal_t;
typedef struct
@ -1232,6 +1236,7 @@ static volatile uint32_t hw_clk_max_freq = PI_HW_CLK_MAX_FREQ;
static int libInitialised = 0;
/* initialise every gpioInitialise */
static struct timespec libStarted;
@ -1313,6 +1318,7 @@ static spiInfo_t spiInfo [PI_SPI_SLOTS];
static gpioScript_t gpioScript [PI_MAX_SCRIPTS];
static gpioSignal_t gpioSignal [PI_MAX_SIGNUM+1];
static struct sigaction defaultSigaction;
static gpioTimer_t gpioTimer [PI_MAX_TIMER+1];
@ -1515,6 +1521,12 @@ int gpioWaveTxStart(unsigned wave_mode); /* deprecated */
static void closeOrphanedNotifications(int slot, int fd);
static void signalInstaller(void);
static int setSignalInstaller(unsigned signum);
static void defaultSigHandler(int signum);
static void customSigHandler(int signum);
static void initKillDMA(volatile uint32_t *dmaAddr);
/* ======================================================================= */
@ -5582,6 +5594,40 @@ static void dmaInitCbs(void)
/* ======================================================================= */
static void defaultSigHandler(int signum)
{
/* kill DMA channels & unlink pid file */
if (dmaReg != MAP_FAILED)
{
initKillDMA(dmaIn);
initKillDMA(dmaOut);
dmaReg = MAP_FAILED; // OS will munmap on termination
}
if (fdLock != -1)
{
close(fdLock);
unlink(PI_LOCKFILE);
fdLock = -1;
}
/* raise signal again, this time using *system* default action */
sigaction(signum, &gpioSignal[signum].old, NULL);
raise(signum);
}
static void customSigHandler(int signum)
{
if (signum == SIGUSR1)
gpioCfg.dbgLevel = (gpioCfg.dbgLevel > DBG_MIN_LEVEL) ?
--gpioCfg.dbgLevel :
DBG_MIN_LEVEL;
if (signum == SIGUSR2)
gpioCfg.dbgLevel = (gpioCfg.dbgLevel < DBG_MAX_LEVEL) ?
++gpioCfg.dbgLevel :
DBG_MAX_LEVEL;
}
static void sigHandler(int signum)
{
@ -5643,21 +5689,105 @@ static void sigHandler(int signum)
/* ----------------------------------------------------------------------- */
static void sigSetHandler(void)
static void signalInstaller(void)
{
int i;
struct sigaction new;
int signum;
struct sigaction new, tmp;
gpioSignal_t *sig;
for (i=PI_MIN_SIGNUM; i<=PI_MAX_SIGNUM; i++)
if (gpioCfg.internals & PI_CFG_NOSIGHANDLER)
{
DBG(DBG_USER, "Signal installer disabled, PI_CFG_NOSIGHANDLER")
return;
}
memset(&new, 0, sizeof(new));
new.sa_handler = sigHandler;
sigemptyset(&new.sa_mask);
new.sa_flags = 0;
sigaction(i, &new, NULL);
for (signum=PI_MIN_SIGNUM; signum<=PI_MAX_SIGNUM; signum++)
{
sig = &gpioSignal[signum];
/* skip if invalid */
if (sigaction(signum, NULL, &tmp) < 0)
{
sig->skip = 1;
DBG(DBG_INTERNAL, "Signal %d: invalid\n", signum);
continue;
}
/* skip if not system default handler */
if (tmp.sa_handler != SIG_DFL)
{
sig->skip = 1;
DBG(DBG_INTERNAL, "Signal %d: !SIG_DFL\n", signum);
continue;
}
switch(signum)
{
/* job control signals - allow gpioSetSignalFunc*/
case SIGCONT:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
break;
/* ignore signals - skip gpioSetSignalFunc */
case SIGPIPE:
case SIGWINCH:
case SIGCHLD:
new.sa_handler = SIG_IGN;
sigaction(signum, &new, &sig->old);
sig->set = 1;
sig->skip = 1; // protect from cancellation
DBG(DBG_INTERNAL, "Signal %d: skip=1, ignored\n", signum);
break;
/* install custom handler */
case SIGUSR1:
case SIGUSR2:
sigaddset(&new.sa_mask, SIGUSR1);
sigaddset(&new.sa_mask, SIGUSR2);
new.sa_handler = customSigHandler;
sigaction(signum, &new, &sig->old);
sig->set = 1;
// restore defaultSigaction if canceled
sig->dfl = 1;
DBG(DBG_INTERNAL, "Signal %d: set=1, customSigHandler installed\n", signum);
break;
default:
sigaction(signum, &defaultSigaction, &sig->old);
sig->dfl = 1;
DBG(DBG_INTERNAL, "Signal %d: dfl=1, defaultSigHandler installed\n", signum);
}
}
}
void signalUninstaller(void)
{
int signum;
gpioSignal_t *sig;
for (signum=PI_MIN_SIGNUM; signum<=PI_MAX_SIGNUM; signum++)
{
sig = &gpioSignal[signum];
if (sig->set || sig->dfl)
{
sigaction(signum, &sig->old, NULL);
sig->set = 0;
sig->dfl = 0;
DBG(DBG_INTERNAL, "Signal %d: Restored, system default handler\n", signum);
}
}
}
/* ----------------------------------------------------------------------- */
/*
freq mics net
0 1000 1000 900
@ -8021,8 +8151,15 @@ static void initClearGlobals(void)
gpioSignal[i].func = NULL;
gpioSignal[i].ex = 0;
gpioSignal[i].userdata = NULL;
gpioSignal[i].set = 0;
gpioSignal[i].dfl = 0;
memset(&gpioSignal[i].old, 0, sizeof(gpioSignal[i].old));
}
defaultSigaction.sa_handler = defaultSigHandler;
defaultSigaction.sa_flags = 0;
sigfillset(&defaultSigaction.sa_mask);
for (i=0; i<=PI_MAX_TIMER; i++)
{
gpioTimer[i].running = 0;
@ -8324,7 +8461,7 @@ int initInitialise(void)
#ifndef EMBEDDED_IN_VM
if (!(gpioCfg.internals & PI_CFG_NOSIGHANDLER))
sigSetHandler();
signalInstaller();
#endif
if (initPeripherals() < 0) return PI_INIT_FAILED;
@ -8767,8 +8904,10 @@ void gpioTerminate(void)
fprintf(stderr,
"\n#####################################################\n\n\n");
}
#endif
signalUninstaller();
initReleaseResources();
fflush(NULL);
@ -12845,24 +12984,79 @@ int gpioDeleteScript(unsigned script_id)
}
/* ----------------------------------------------------------------------- */
static int setSignalInstaller(unsigned signum)
{
gpioSignal_t *sig = &gpioSignal[signum];
struct sigaction new;
if (sig->skip)
return PI_SIG_SKIPPED;
/* set: save old signal action and set new */
if (sig->func)
{
if (sig->set == 0)
{
DBG(DBG_INTERNAL, "Set new handler for signal %d" ,signum);
memset(&new, 0, sizeof(new));
new.sa_handler = sigHandler;
sigfillset(&new.sa_mask);
sig->set = 1;
if (sig->dfl == 0)
return sigaction(signum, &new, &sig->old);
else
return sigaction(signum, &new, NULL);
}
return PI_SIGNUM_SET;
}
/* cancel and restore old signal action */
else
{
if (sig->set)
{
DBG(DBG_INTERNAL, "Cancel and restore handler for signal %d" ,signum);
sig->set = 0;
if (sig->dfl == 0)
return sigaction(signum, &sig->old, NULL);
else
return sigaction(signum, &defaultSigaction, NULL);
}
}
return PI_SIG_UNK_ACTION;
}
/* ----------------------------------------------------------------------- */
int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
{
int rv;
DBG(DBG_USER, "signum=%d function=%08"PRIXPTR, signum, (uintptr_t)f);
CHECK_INITED;
if (gpioCfg.internals & PI_CFG_NOSIGHANDLER)
return PI_NOSIGHANDLER;
if (signum > PI_MAX_SIGNUM)
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 0;
gpioSignal[signum].userdata = NULL;
gpioSignal[signum].func = f;
return 0;
rv = setSignalInstaller(signum);
return (rv == -1) ? PI_SIG_UNK_ACTION : rv;
}
@ -12871,20 +13065,25 @@ int gpioSetSignalFunc(unsigned signum, gpioSignalFunc_t f)
int gpioSetSignalFuncEx(unsigned signum, gpioSignalFuncEx_t f,
void *userdata)
{
int rv;
DBG(DBG_USER, "signum=%d function=%08"PRIXPTR" userdata=%08"PRIXPTR,
signum, (uintptr_t)f, (uintptr_t)userdata);
CHECK_INITED;
if (gpioCfg.internals & PI_CFG_NOSIGHANDLER)
return PI_NOSIGHANDLER;
if (signum > PI_MAX_SIGNUM)
SOFT_ERROR(PI_BAD_SIGNUM, "bad signum (%d)", signum);
gpioSignal[signum].ex = 1;
gpioSignal[signum].userdata = userdata;
gpioSignal[signum].func = f;
return 0;
rv = setSignalInstaller(signum);
return (rv == -1) ? PI_SIG_UNK_ACTION : rv;
}
@ -14025,7 +14224,9 @@ int gpioCfgNetAddr(int numSockAddr, uint32_t *sockAddr)
uint32_t gpioCfgGetInternals(void)
{
return gpioCfg.internals;
return ((gpioCfg.internals & 0xffffff00)
| gpioCfg.alertFreq << 4
| gpioCfg.dbgLevel);
}
int gpioCfgSetInternals(uint32_t cfgVal)
@ -14041,4 +14242,3 @@ int gpioCfgSetInternals(uint32_t cfgVal)
/* include any user customisations */
#include "custom.cext"

View File

@ -6528,6 +6528,11 @@ after this command is issued.
#define PI_CMD_INTERRUPTED -144 // Used by Python
#define PI_NOT_ON_BCM2711 -145 // not available on BCM2711
#define PI_ONLY_ON_BCM2711 -146 // only available on BCM2711
#define PI_NOSIGHANDLER -147 // signal handling is disabled (gpioCfg.internals)
#define PI_SIGNUM_INVALID -148 // invalid signal number requested (signal.h)
#define PI_SIGNUM_SET -149 // handler for signal is already installed
#define PI_SIG_UNK_ACTION -150 // default signal handler is not installed
#define PI_SIG_SKIPPED -151 // set/cancel signal handler is not allowed
#define PI_PIGIF_ERR_0 -2000
#define PI_PIGIF_ERR_99 -2099

View File

@ -22,6 +22,7 @@ sudo ./x_pigpio
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include "pigpio.h"
@ -900,6 +901,114 @@ void tc()
CHECK(12, 99, e, 0, 0, "spiClose");
}
int signum;
enum signalType {Invalid, Default, Ignore, Catch};
typedef struct
{
enum signalType type;
struct sigaction action;
} sigArr_t;
sigArr_t sigArr[PI_MAX_SIGNUM+1];
struct sigaction query, preset;
void voidHandler(int signum) {}
void td(void)
{
/* Categorize all handlers */
for (signum = 0; signum<=PI_MAX_SIGNUM; signum++)
{
if (sigaction(signum, NULL, &sigArr[signum].action) < 0)
sigArr[signum].type = Invalid;
else if (sigArr[signum].action.sa_handler == SIG_DFL)
sigArr[signum].type = Default;
else if (sigArr[signum].action.sa_handler == SIG_IGN)
sigArr[signum].type = Ignore;
else sigArr[signum].type = Catch; // Preset, Set or Dfl
}
/* Check signals affected by default installer. */
CHECK(13,1, sigArr[SIGCHLD].type, Ignore, 0, "SIGCHLD ignored");
CHECK(13,2, sigArr[SIGPIPE].type, Ignore, 0, "SIGPIPE Ignored");
CHECK(13,3, sigArr[SIGWINCH].type, Ignore, 0, "SIGWINCH ignored");
CHECK(13,4, sigArr[SIGUSR1].type, Catch, 0, "SIGUSR1 installed");
CHECK(13,5, sigArr[SIGUSR2].type, Catch, 0, "SIGUSR2 installed");
/* Set and restore handler on cancellation. */
CHECK(13,6, gpioSetSignalFunc(SIGSEGV, voidHandler), 0, 0, "set signal");
CHECK(13,7, gpioSetSignalFunc(SIGSEGV, NULL), 0, 0, "cancel signal");
CHECK(13,8, sigaction(SIGSEGV, NULL, &query), 0, 0, "query sigaction");
CHECK(13,9, (intptr_t)query.sa_handler, (intptr_t)sigArr[SIGSEGV].action.sa_handler, 0, "gpioSetSignalFunc");
/* check custom handler function */
raise(SIGUSR2); // increment dgbLevel
time_sleep(0.1);
CHECK(13,10, gpioCfgGetInternals() & 0x0f, 1, 0, "SIGUSR2");
raise(SIGUSR1); // decrement dgbLevel
time_sleep(0.1);
CHECK(13,11, gpioCfgGetInternals() & 0x0f, 0, 0, "SIGUSR1");
/* check gpioTermination restores all signal handlers to their pre-initialise state. */
gpioTerminate();
unsigned catchNotRestored = 0;
unsigned ignoreNotRestored = 0;
for (signum = 0; signum<=PI_MAX_SIGNUM; signum++)
{
if (sigArr[signum].type == Catch)
if (sigaction(signum, NULL, &query) == 0)
if (query.sa_handler != SIG_DFL)
{
fprintf(stderr, "signal %d not restored\n", signum);
catchNotRestored++;
}
if (sigArr[signum].type == Ignore)
if (sigaction(signum, NULL, &query) == 0)
if (query.sa_handler != SIG_DFL)
{
fprintf(stderr, "signal %d not restored\n", signum);
ignoreNotRestored++;
}
}
CHECK(13,12, catchNotRestored, 0, 0, "caught signals not restored");
CHECK(13,13, ignoreNotRestored, 0, 0, "ignored signals not restored");
/* check handlers prior to gpioInitialise are preserved */
preset = sigArr[SIGINT].action;
memset(&preset, 0, sizeof(preset));
preset.sa_handler = voidHandler;
CHECK(13,14, sigaction(SIGINT, &preset, &query), 0, 0, "preset action");
CHECK(13,15, gpioInitialise(), 79, 0, "gpioInitialise"); // FIX version dependency
CHECK(13,16, gpioSetSignalFunc(SIGINT, voidHandler), PI_SIG_SKIPPED, 0, "preset override");
// can not set ignored signals
CHECK(13,17, gpioSetSignalFunc(SIGPIPE, voidHandler), PI_SIG_SKIPPED, 0, "set ignore");
/* Test signal handlers disabled */
gpioTerminate();
gpioCfgSetInternals(gpioCfgGetInternals() | PI_CFG_NOSIGHANDLER);
gpioInitialise();
sigaction(SIGHUP, NULL, &query);
CHECK(13,18, (intptr_t)query.sa_handler, (intptr_t)SIG_DFL, 0, "default disabled");
CHECK(13,19, gpioSetSignalFunc(SIGHUP, NULL), PI_NOSIGHANDLER, 0, "set disabled");
}
int main(int argc, char *argv[])
{
int i, t, c, status;
@ -944,6 +1053,7 @@ int main(int argc, char *argv[])
if (strchr(test, 'a')) ta();
if (strchr(test, 'b')) tb();
if (strchr(test, 'c')) tc();
if (strchr(test, 'd')) td();
gpioTerminate();