From ab74c86edb30fa04fda5fe7fa01e404334f7c2b0 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 17 Feb 2016 10:40:55 +0000 Subject: 37999: Sticky behaviour of EXIT traps. They now have POSIX or non-POSIX behaviour based on the setting of POSIX_TRAPS where the trap was defined, rather than where the trap would (or would not) be executed. Tweaks possible. --- Src/signals.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'Src/signals.c') diff --git a/Src/signals.c b/Src/signals.c index aa0b5aaa7..32452ae4f 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -55,6 +55,15 @@ mod_export Eprog siglists[VSIGCOUNT]; /**/ mod_export int nsigtrapped; +/* + * Flag that exit trap has been set in POSIX mode. + * The setter's expectation is therefore that it is run + * on programme exit, not function exit. + */ + +/**/ +static int exit_trap_posix; + /* Variables used by signal queueing */ /**/ @@ -755,7 +764,7 @@ killjb(Job jn, int sig) * at once, so just use a linked list. */ struct savetrap { - int sig, flags, local; + int sig, flags, local, posix; void *list; }; @@ -774,6 +783,7 @@ dosavetrap(int sig, int level) st = (struct savetrap *)zalloc(sizeof(*st)); st->sig = sig; st->local = level; + st->posix = (sig == SIGEXIT) ? exit_trap_posix : 0; if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) { /* * Get the old function: this assumes we haven't added @@ -873,6 +883,10 @@ settrap(int sig, Eprog l, int flags) * works just the same. */ sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags; + if (sig == SIGEXIT) { + /* Make POSIX behaviour of EXIT trap sticky */ + exit_trap_posix = isset(POSIXTRAPS); + } unqueue_signals(); return 0; } @@ -908,7 +922,7 @@ removetrap(int sig) * already one at the current locallevel we just overwrite it. */ if (!dontsavetrap && - (sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) && + (sig == SIGEXIT ? !exit_trap_posix : isset(LOCALTRAPS)) && locallevel && (!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) dosavetrap(sig, locallevel); @@ -935,6 +949,8 @@ removetrap(int sig) #endif sig != SIGCHLD) signal_default(sig); + if (sig == SIGEXIT) + exit_trap_posix = 0; /* * At this point we free the appropriate structs. If we don't @@ -978,7 +994,7 @@ starttrapscope(void) * so give it the next higher one. dosavetrap() is called * automatically where necessary. */ - if (sigtrapped[SIGEXIT] && !isset(POSIXTRAPS)) { + if (sigtrapped[SIGEXIT] && !exit_trap_posix) { locallevel++; unsettrap(SIGEXIT); locallevel--; @@ -1005,7 +1021,7 @@ endtrapscope(void) * Don't do this inside another trap. */ if (!intrap && - !isset(POSIXTRAPS) && (exittr = sigtrapped[SIGEXIT])) { + !exit_trap_posix && (exittr = sigtrapped[SIGEXIT])) { if (exittr & ZSIG_FUNC) { exitfn = removehashnode(shfunctab, "TRAPEXIT"); } else { @@ -1031,7 +1047,9 @@ endtrapscope(void) if (st->flags & ZSIG_FUNC) settrap(sig, NULL, ZSIG_FUNC); else - settrap(sig, (Eprog) st->list, 0); + settrap(sig, (Eprog) st->list, 0); + if (sig == SIGEXIT) + exit_trap_posix = st->posix; dontsavetrap--; /* * counting of nsigtrapped should presumably be handled @@ -1042,16 +1060,26 @@ endtrapscope(void) if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC) shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam, (Shfunc) st->list); - } else if (sigtrapped[sig]) - unsettrap(sig); + } else if (sigtrapped[sig]) { + /* + * Don't restore the old state if someone has set a + * POSIX-style exit trap --- allow this to propagate. + */ + if (sig != SIGEXIT || !exit_trap_posix) + unsettrap(sig); + } zfree(st, sizeof(*st)); } } if (exittr) { - if (!isset(POSIXTRAPS)) - dotrapargs(SIGEXIT, &exittr, exitfn); + /* + * We already made sure this wasn't set as a POSIX exit trap. + * We respect the user's intention when the trap in question + * was set. + */ + dotrapargs(SIGEXIT, &exittr, exitfn); if (exittr & ZSIG_FUNC) shfunctab->freenode((HashNode)exitfn); else -- cgit 1.4.1