master xplshn/aruu / cmd / posix / sh / trap.c
  1/*-
  2 * SPDX-License-Identifier: BSD-3-Clause
  3 *
  4 * Copyright (c) 1991, 1993
  5 *	The Regents of the University of California.  All rights reserved.
  6 *
  7 * This code is derived from software contributed to Berkeley by
  8 * Kenneth Almquist.
  9 *
 10 * Redistribution and use in source and binary forms, with or without
 11 * modification, are permitted provided that the following conditions
 12 * are met:
 13 * 1. Redistributions of source code must retain the above copyright
 14 *    notice, this list of conditions and the following disclaimer.
 15 * 2. Redistributions in binary form must reproduce the above copyright
 16 *    notice, this list of conditions and the following disclaimer in the
 17 *    documentation and/or other materials provided with the distribution.
 18 * 3. Neither the name of the University nor the names of its contributors
 19 *    may be used to endorse or promote products derived from this software
 20 *    without specific prior written permission.
 21 *
 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32 * SUCH DAMAGE.
 33 */
 34
 35#include "shell.h"
 36#include "main.h"
 37#include "nodes.h"	/* for other headers */
 38#include "eval.h"
 39#include "jobs.h"
 40#include "show.h"
 41#include "options.h"
 42#include "syntax.h"
 43#include "output.h"
 44#include "memalloc.h"
 45#include "error.h"
 46#include "trap.h"
 47#include "mystring.h"
 48#include "builtins.h"
 49#ifndef NO_HISTORY
 50#include "lineedit.h"
 51#endif
 52
 53#include <signal.h>
 54#include <stdlib.h>
 55#include <unistd.h>
 56
 57/*
 58 * Sigmode records the current value of the signal handlers for the various
 59 * modes.  A value of zero means that the current handler is not known.
 60 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
 61 */
 62
 63#define S_DFL 1			/* default signal handling (SIG_DFL) */
 64#define S_CATCH 2		/* signal is caught */
 65#define S_IGN 3			/* signal is ignored (SIG_IGN) */
 66#define S_HARD_IGN 4		/* signal is ignored permanently */
 67#define S_RESET 5		/* temporary - to reset a hard ignored sig */
 68
 69
 70static char sigmode[NSIG];	/* current value of signal */
 71volatile sig_atomic_t pendingsig;	/* indicates some signal received */
 72volatile sig_atomic_t pendingsig_waitcmd;	/* indicates wait builtin should be interrupted */
 73static int in_dotrap;			/* do we execute in a trap handler? */
 74static char *volatile trap[NSIG];	/* trap handler commands */
 75static volatile sig_atomic_t gotsig[NSIG];
 76				/* indicates specified signal received */
 77static int ignore_sigchld;	/* Used while handling SIGCHLD traps. */
 78static int last_trapsig;
 79
 80static int exiting;		/* exitshell() has been called */
 81static int exiting_exitstatus;	/* value passed to exitshell() */
 82
 83static int getsigaction(int, sig_t *);
 84
 85
 86/*
 87 * Map a string to a signal number.
 88 *
 89 * Note: the signal number may exceed NSIG.
 90 */
 91static int
 92sigstring_to_signum(char *sig)
 93{
 94
 95	if (is_number(sig)) {
 96		int signo;
 97
 98		signo = atoi(sig);
 99		return ((signo >= 0 && signo < NSIG) ? signo : (-1));
100	} else if (strcasecmp(sig, "EXIT") == 0) {
101		return (0);
102	} else {
103		int n;
104
105		if (strncasecmp(sig, "SIG", 3) == 0)
106			sig += 3;
107		for (n = 1; n < sys_nsig; n++)
108			if (sys_signame[n] &&
109			    strcasecmp(sys_signame[n], sig) == 0)
110				return (n);
111	}
112	return (-1);
113}
114
115
116/*
117 * Print a list of valid signal names.
118 */
119static void
120printsignals(void)
121{
122	int n, outlen;
123
124	outlen = 0;
125	for (n = 1; n < sys_nsig; n++) {
126		if (sys_signame[n]) {
127			out1fmt("%s", sys_signame[n]);
128			outlen += strlen(sys_signame[n]);
129		} else {
130			out1fmt("%d", n);
131			outlen += 3;	/* good enough */
132		}
133		++outlen;
134		if (outlen > 71 || n == sys_nsig - 1) {
135			out1str("\n");
136			outlen = 0;
137		} else {
138			out1c(' ');
139		}
140	}
141}
142
143
144/*
145 * The trap builtin.
146 */
147int
148trapcmd(int argc __unused, char **argv)
149{
150	char *action;
151	int signo;
152	int errors = 0;
153	int i;
154
155	while ((i = nextopt("l")) != '\0') {
156		switch (i) {
157		case 'l':
158			printsignals();
159			return (0);
160		}
161	}
162	argv = argptr;
163
164	if (*argv == NULL) {
165		for (signo = 0 ; signo < sys_nsig ; signo++) {
166			if (signo < NSIG && trap[signo] != NULL) {
167				out1str("trap -- ");
168				out1qstr(trap[signo]);
169				if (signo == 0) {
170					out1str(" EXIT\n");
171				} else if (sys_signame[signo]) {
172					out1fmt(" %s\n", sys_signame[signo]);
173				} else {
174					out1fmt(" %d\n", signo);
175				}
176			}
177		}
178		return 0;
179	}
180	action = NULL;
181	if (*argv && !is_number(*argv)) {
182		if (strcmp(*argv, "-") == 0)
183			argv++;
184		else {
185			action = *argv;
186			argv++;
187		}
188	}
189	for (; *argv; argv++) {
190		if ((signo = sigstring_to_signum(*argv)) == -1) {
191			warning("bad signal %s", *argv);
192			errors = 1;
193			continue;
194		}
195		INTOFF;
196		if (action)
197			action = savestr(action);
198		if (trap[signo])
199			ckfree(trap[signo]);
200		trap[signo] = action;
201		if (signo != 0)
202			setsignal(signo);
203		INTON;
204	}
205	return errors;
206}
207
208
209/*
210 * Clear traps on a fork.
211 */
212void
213clear_traps(void)
214{
215	char *volatile *tp;
216
217	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
218		if (*tp && **tp) {	/* trap not NULL or SIG_IGN */
219			INTOFF;
220			ckfree(*tp);
221			*tp = NULL;
222			if (tp != &trap[0])
223				setsignal(tp - trap);
224			INTON;
225		}
226	}
227}
228
229
230/*
231 * Check if we have any traps enabled.
232 */
233int
234have_traps(void)
235{
236	char *volatile *tp;
237
238	for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) {
239		if (*tp && **tp)	/* trap not NULL or SIG_IGN */
240			return 1;
241	}
242	return 0;
243}
244
245/*
246 * Set the signal handler for the specified signal.  The routine figures
247 * out what it should be set to.
248 */
249void
250setsignal(int signo)
251{
252	int action;
253	sig_t sigact = SIG_DFL;
254	struct sigaction sa;
255	char *t;
256
257	if ((t = trap[signo]) == NULL)
258		action = S_DFL;
259	else if (*t != '\0')
260		action = S_CATCH;
261	else
262		action = S_IGN;
263	if (action == S_DFL) {
264		switch (signo) {
265		case SIGINT:
266			action = S_CATCH;
267			break;
268		case SIGQUIT:
269#ifdef DEBUG
270			if (debug)
271				break;
272#endif
273			action = S_CATCH;
274			break;
275		case SIGTERM:
276			if (rootshell && iflag)
277				action = S_IGN;
278			break;
279#if JOBS
280		case SIGTSTP:
281		case SIGTTOU:
282			if (rootshell && mflag)
283				action = S_IGN;
284			break;
285#endif
286		}
287	}
288
289	t = &sigmode[signo];
290	if (*t == 0) {
291		/*
292		 * current setting unknown
293		 */
294		if (!getsigaction(signo, &sigact)) {
295			/*
296			 * Pretend it worked; maybe we should give a warning
297			 * here, but other shells don't. We don't alter
298			 * sigmode, so that we retry every time.
299			 */
300			return;
301		}
302		if (sigact == SIG_IGN) {
303			if (mflag && (signo == SIGTSTP ||
304			     signo == SIGTTIN || signo == SIGTTOU)) {
305				*t = S_IGN;	/* don't hard ignore these */
306			} else
307				*t = S_HARD_IGN;
308		} else {
309			*t = S_RESET;	/* force to be set */
310		}
311	}
312	if (*t == S_HARD_IGN || *t == action)
313		return;
314	switch (action) {
315		case S_DFL:	sigact = SIG_DFL;	break;
316		case S_CATCH:  	sigact = onsig;		break;
317		case S_IGN:	sigact = SIG_IGN;	break;
318	}
319	*t = action;
320	sa.sa_handler = sigact;
321	sa.sa_flags = 0;
322	sigemptyset(&sa.sa_mask);
323	sigaction(signo, &sa, NULL);
324}
325
326
327/*
328 * Return the current setting for sig w/o changing it.
329 */
330static int
331getsigaction(int signo, sig_t *sigact)
332{
333	struct sigaction sa;
334
335	if (sigaction(signo, (struct sigaction *)0, &sa) == -1)
336		return 0;
337	*sigact = (sig_t) sa.sa_handler;
338	return 1;
339}
340
341
342/*
343 * Ignore a signal.
344 */
345void
346ignoresig(int signo)
347{
348
349	if (sigmode[signo] == 0)
350		setsignal(signo);
351	if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) {
352		signal(signo, SIG_IGN);
353		sigmode[signo] = S_IGN;
354	}
355}
356
357
358int
359issigchldtrapped(void)
360{
361
362	return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0');
363}
364
365
366/*
367 * Signal handler.
368 */
369void
370onsig(int signo)
371{
372
373	if (signo == SIGINT && trap[SIGINT] == NULL) {
374		if (suppressint)
375			SET_PENDING_INT;
376		else
377			onint();
378		return;
379	}
380
381	/* If we are currently in a wait builtin, prepare to break it */
382	if (signo == SIGINT || signo == SIGQUIT)
383		pendingsig_waitcmd = signo;
384
385	if (trap[signo] != NULL && trap[signo][0] != '\0' &&
386	    (signo != SIGCHLD || !ignore_sigchld)) {
387		gotsig[signo] = 1;
388		pendingsig = signo;
389		pendingsig_waitcmd = signo;
390	}
391}
392
393
394/*
395 * Called to execute a trap.  Perhaps we should avoid entering new trap
396 * handlers while we are executing a trap handler.
397 */
398void
399dotrap(void)
400{
401	struct stackmark smark;
402	int i;
403	int savestatus, prev_evalskip, prev_skipcount;
404
405	in_dotrap++;
406	for (;;) {
407		pendingsig = 0;
408		pendingsig_waitcmd = 0;
409		for (i = 1; i < NSIG; i++) {
410			if (gotsig[i]) {
411				gotsig[i] = 0;
412				if (trap[i]) {
413					/*
414					 * Ignore SIGCHLD to avoid infinite
415					 * recursion if the trap action does
416					 * a fork.
417					 */
418					if (i == SIGCHLD)
419						ignore_sigchld++;
420
421					/*
422					 * Backup current evalskip
423					 * state and reset it before
424					 * executing a trap, so that the
425					 * trap is not disturbed by an
426					 * ongoing break/continue/return
427					 * statement.
428					 */
429					prev_evalskip  = evalskip;
430					prev_skipcount = skipcount;
431					evalskip = 0;
432
433					last_trapsig = i;
434					savestatus = exitstatus;
435					setstackmark(&smark);
436					evalstring(stsavestr(trap[i]), 0);
437					popstackmark(&smark);
438
439					/*
440					 * If such a command was not
441					 * already in progress, allow a
442					 * break/continue/return in the
443					 * trap action to have an effect
444					 * outside of it.
445					 */
446					if (evalskip == 0 ||
447					    prev_evalskip != 0) {
448						evalskip  = prev_evalskip;
449						skipcount = prev_skipcount;
450						exitstatus = savestatus;
451					}
452
453					if (i == SIGCHLD)
454						ignore_sigchld--;
455				}
456				break;
457			}
458		}
459		if (i >= NSIG)
460			break;
461	}
462	in_dotrap--;
463}
464
465
466void
467trap_init(void)
468{
469	setsignal(SIGINT);
470	setsignal(SIGQUIT);
471}
472
473
474/*
475 * Controls whether the shell is interactive or not based on iflag.
476 */
477void
478setinteractive(void)
479{
480	setsignal(SIGTERM);
481}
482
483
484/*
485 * Called to exit the shell.
486 */
487void
488exitshell(int status)
489{
490	TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
491	exiting = 1;
492	exiting_exitstatus = status;
493	exitshell_savedstatus();
494}
495
496void
497exitshell_savedstatus(void)
498{
499	struct jmploc loc1, loc2;
500	char *p;
501	int sig = 0;
502	sigset_t sigs;
503
504	if (!exiting) {
505		if (in_dotrap && last_trapsig) {
506			sig = last_trapsig;
507			exiting_exitstatus = sig + 128;
508		} else
509			exiting_exitstatus = oexitstatus;
510	}
511	exitstatus = oexitstatus = exiting_exitstatus;
512	if (!setjmp(loc1.loc)) {
513		handler = &loc1;
514		if ((p = trap[0]) != NULL && *p != '\0') {
515			/*
516			 * Reset evalskip, or the trap on EXIT could be
517			 * interrupted if the last command was a "return".
518			 */
519			evalskip = 0;
520			trap[0] = NULL;
521			FORCEINTON;
522			evalstring(p, 0);
523		}
524	}
525	if (!setjmp(loc2.loc)) {
526		handler = &loc2;		/* probably unnecessary */
527		FORCEINTON;
528		flushall();
529#if JOBS
530		setjobctl(0);
531#endif
532#ifndef NO_HISTORY
533		histsave();
534#endif
535	}
536	if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
537	    sig != SIGTTOU) {
538		signal(sig, SIG_DFL);
539		sigemptyset(&sigs);
540		sigaddset(&sigs, sig);
541		sigprocmask(SIG_UNBLOCK, &sigs, NULL);
542		kill(getpid(), sig);
543		/* If the default action is to ignore, fall back to _exit(). */
544	}
545	_exit(exiting_exitstatus);
546}