From 751b5399a9fc96c188015379de37670039bd681e Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 10 Feb 2025 22:19:30 -0600 Subject: [PATCH] Implement trap and signal handling. --- toys/pending/sh.c | 124 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 93 insertions(+), 31 deletions(-) diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 597d1667..22ebebd3 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -360,6 +360,7 @@ GLOBALS( char *isexec, *wcpat, *traps[NSIG+2]; unsigned options, jobcnt; int hfd, pid, bangpid, recursion; + struct double_list *nextsig; jmp_buf forkchild; // Callable function array @@ -387,7 +388,7 @@ GLOBALS( long flags; char *str; } *vars; - long varslen, varscap, shift, lineno; + long varslen, varscap, shift, lineno, signal; struct sh_function *function; FILE *source; @@ -1405,6 +1406,16 @@ static void end_fcall(void) ff->pp->urd = 0; free_process(ff->pp); } + + // Unblock signal we just finished handling + if (TT.ff->signal) { + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, TT.ff->signal); + sigprocmask(SIG_UNBLOCK, &set, 0); + } + free(dlist_pop(&TT.ff)); } @@ -2775,6 +2786,48 @@ notfd: return pp; } +// Handler called with all signals blocked, so no special locking needed. +static void sig_fcall(int sig, siginfo_t *info, void *ucontext) +{ + // Tell run_lines() to eval trap, keep signal blocked until trap func ends + dlist_add(&TT.nextsig, (void *)(long)sig); + sigaddset(&((struct ucontext_t *)ucontext)->uc_sigmask, sig); +} + +// Set signal handler to exec string, or reset to default if NULL +static void signify(int sig, char *throw) +{ + void *ign = (sig==SIGPIPE || (sig==SIGINT && dashi())) ? SIG_IGN : SIG_DFL; + struct sigaction act = {0}; + struct sh_fcall *ff; + + if (throw && !*throw) throw = 0, ign = SIG_IGN; + + // If we're replacing a running trap handler, garbe collect in fcall pop. + for (ff = TT.ff; ff && ff!=TT.ff->prev; ff = ff->next) if (ff->signal==sig) { + push_arg(&ff->delete, TT.traps[sig]); + TT.traps[sig] = 0; + break; + } + free(TT.traps[sig]); + TT.traps[sig] = throw; + + // Set signal handler (not for synthetic signals like EXIT) + if (sig && sigsignal = (long)dl->data]; + free(dl); + TT.ff->source = fmemopen(ss, strlen(ss), "r"); + } if (!TT.ff->pl) { if (TT.ff->source) break; + i = TT.ff->signal; end_fcall(); // TODO can we move advance logic to start of loop to avoid straddle? - goto advance; + if (!i || !TT.ff || !TT.ff->pl) goto advance; } ctl = TT.ff->pl->end->arg->v[TT.ff->pl->end->arg->c]; @@ -4258,13 +4326,11 @@ static void subshell_setup(void) sprintf(buf, "%u", atoi(ss+6)+1); setvarval("SHLVL", buf)->flags |= VAR_EXPORT; } - if (dashi()) { - if (!getvar("PS1")) setvarval("PS1", "$ "); // "\\s-\\v$ " - // TODO Set up signal handlers and grab control of this tty. - // ^C SIGINT ^\ SIGQUIT ^Z SIGTSTP SIGTTIN SIGTTOU SIGCHLD - // setsid(), setpgid(), tcsetpgrp()... - xsignal(SIGINT, SIG_IGN); - } + if (dashi() && !getvar("PS1")) setvarval("PS1", "$ "); // "\\s-\\v$ " + // TODO Set up signal handlers and grab control of this tty. + // ^C SIGINT ^\ SIGQUIT ^Z SIGTSTP SIGTTIN SIGTTOU SIGCHLD + // setsid(), setpgid(), tcsetpgrp()... + signify(SIGINT, 0); // Add additional input sources (in reverse order so they pop off stack right) @@ -4284,7 +4350,7 @@ void sh_main(void) //dprintf(2, "%d main", getpid()); for (unsigned uu = 0; toys.argv[uu]; uu++) dprintf(2, " %s", toys.argv[uu]); dprintf(2, "\n"); - signal(SIGPIPE, SIG_IGN); + signify(SIGPIPE, 0); TT.options = OPT_B; TT.pid = getpid(); srandom(TT.SECONDS = millitime()); @@ -4534,36 +4600,32 @@ void set_main(void) void trap_main(void) { int ii, jj; - void *sig = *toys.optargs, *old; + char *sig = *toys.optargs; struct signame sn[] = {{0, "EXIT"}, {NSIG, "DEBUG"}, {NSIG+1, "RETURN"}}; // Display data when asked - if (FLAG(l)) list_signals(); + if (FLAG(l)) return list_signals(); else if (FLAG(p) || !toys.optc) { for (ii = 0; ii1; toys.optargs[ii]; ii++) { - if (1>(jj = sig_to_num(*toys.optargs))) { - while (++jj1; toys.optargs[ii]; ii++) { + if (1>(jj = sig_to_num(toys.optargs[ii]))) { + while (++jj