diff --git a/pigpio.c b/pigpio.c index fc57d68..2f16c21 100644 --- a/pigpio.c +++ b/pigpio.c @@ -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" - diff --git a/pigpio.h b/pigpio.h index 1b8e51c..1aac36d 100644 --- a/pigpio.h +++ b/pigpio.h @@ -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 diff --git a/x_pigpio.c b/x_pigpio.c index 8119d73..ed0ee38 100644 --- a/x_pigpio.c +++ b/x_pigpio.c @@ -22,6 +22,7 @@ sudo ./x_pigpio #include #include #include +#include #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();