xref: /netbsd/bin/ksh/trap.c (revision bf9ec67e)
1 /*	$NetBSD: trap.c,v 1.4 2001/09/16 16:34:23 wiz Exp $	*/
2 
3 /*
4  * signal handling
5  */
6 
7 /* Kludge to avoid bogus re-declaration of sigtraps[] error on AIX 3.2.5 */
8 #define FROM_TRAP_C
9 #include "sh.h"
10 
11 /* Table is indexed by signal number
12  *
13  * The script siglist.sh generates siglist.out, which is a sorted, complete
14  * list of signals
15  */
16 Trap sigtraps[SIGNALS+1] = {
17 	{ SIGEXIT_, "EXIT", "Signal 0" },
18 #include "siglist.out"	/* generated by siglist.sh */
19 	{ SIGERR_,  "ERR",  "Error handler" },
20     };
21 
22 static struct sigaction Sigact_ign, Sigact_trap;
23 
24 void
25 inittraps()
26 {
27 #ifdef HAVE_SYS_SIGLIST
28 # ifndef SYS_SIGLIST_DECLARED
29 	extern char	*sys_siglist[];
30 # endif
31 	int	i;
32 
33 	/* Use system description, if available, for unknown signals... */
34 	for (i = 0; i < NSIG; i++)
35 		if (!sigtraps[i].name && sys_siglist[i] && sys_siglist[i][0])
36 			sigtraps[i].mess = sys_siglist[i];
37 #endif	/* HAVE_SYS_SIGLIST */
38 
39 	sigemptyset(&Sigact_ign.sa_mask);
40 	Sigact_ign.sa_flags = KSH_SA_FLAGS;
41 	Sigact_ign.sa_handler = SIG_IGN;
42 	Sigact_trap = Sigact_ign;
43 	Sigact_trap.sa_handler = trapsig;
44 
45 	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
46 	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
47 	sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
48 	sigtraps[SIGHUP].flags |= TF_FATAL;
49 	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
50 
51 	/* these are always caught so we can clean up any temproary files. */
52 	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
53 	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
54 	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
55 	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
56 }
57 
58 #ifdef KSH
59 static RETSIGTYPE alarm_catcher ARGS((int sig));
60 
61 void
62 alarm_init()
63 {
64 	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
65 	setsig(&sigtraps[SIGALRM], alarm_catcher,
66 		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
67 }
68 
69 static RETSIGTYPE
70 alarm_catcher(sig)
71 	int sig;
72 {
73 	if (ksh_tmout_state == TMOUT_READING) {
74 		int left = alarm(0);
75 
76 		if (left == 0) {
77 			ksh_tmout_state = TMOUT_LEAVING;
78 			intrsig = 1;
79 		} else
80 			alarm(left);
81 	}
82 	return RETSIGVAL;
83 }
84 #endif /* KSH */
85 
86 Trap *
87 gettrap(name, igncase)
88 	const char *name;
89 	int igncase;
90 {
91 	int i;
92 	register Trap *p;
93 
94 	if (digit(*name)) {
95 		int n;
96 
97 		if (getn(name, &n) && 0 <= n && n < SIGNALS)
98 			return &sigtraps[n];
99 		return NULL;
100 	}
101 	for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
102 		if (p->name && (igncase ? strcasecmp(p->name, name) == 0
103 					: strcmp(p->name, name) == 0))
104 			return p;
105 	return NULL;
106 }
107 
108 /*
109  * trap signal handler
110  */
111 RETSIGTYPE
112 trapsig(i)
113 	int i;
114 {
115 	Trap *p = &sigtraps[i];
116 
117 	trap = p->set = 1;
118 	if (p->flags & TF_DFL_INTR)
119 		intrsig = 1;
120 	if ((p->flags & TF_FATAL) && !p->trap) {
121 		fatal_trap = 1;
122 		intrsig = 1;
123 	}
124 	if (p->shtrap)
125 		(*p->shtrap)(i);
126 #ifdef V7_SIGNALS
127 	if (sigtraps[i].cursig == trapsig) /* this for SIGCHLD,SIGALRM */
128 		sigaction(i, &Sigact_trap, (struct sigaction *) 0);
129 #endif /* V7_SIGNALS */
130 	return RETSIGVAL;
131 }
132 
133 /* called when we want to allow the user to ^C out of something - won't
134  * work if user has trapped SIGINT.
135  */
136 void
137 intrcheck()
138 {
139 	if (intrsig)
140 		runtraps(TF_DFL_INTR|TF_FATAL);
141 }
142 
143 /* called after EINTR to check if a signal with normally causes process
144  * termination has been received.
145  */
146 int
147 fatal_trap_check()
148 {
149 	int i;
150 	Trap *p;
151 
152 	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
153 	for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
154 		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
155 			/* return value is used as an exit code */
156 			return 128 + p->signal;
157 	return 0;
158 }
159 
160 /* Returns the signal number of any pending traps: ie, a signal which has
161  * occurred for which a trap has been set or for which the TF_DFL_INTR flag
162  * is set.
163  */
164 int
165 trap_pending()
166 {
167 	int i;
168 	Trap *p;
169 
170 	for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
171 		if (p->set && ((p->trap && p->trap[0])
172 			       || ((p->flags & (TF_DFL_INTR|TF_FATAL))
173 				   && !p->trap)))
174 			return p->signal;
175 	return 0;
176 }
177 
178 /*
179  * run any pending traps.  If intr is set, only run traps that
180  * can interrupt commands.
181  */
182 void
183 runtraps(flag)
184 	int flag;
185 {
186 	int i;
187 	register Trap *p;
188 
189 #ifdef KSH
190 	if (ksh_tmout_state == TMOUT_LEAVING) {
191 		ksh_tmout_state = TMOUT_EXECUTING;
192 		warningf(FALSE, "timed out waiting for input");
193 		unwind(LEXIT);
194 	} else
195 		/* XXX: this means the alarm will have no effect if a trap
196 		 * is caught after the alarm() was started...not good.
197 		 */
198 		ksh_tmout_state = TMOUT_EXECUTING;
199 #endif /* KSH */
200 	if (!flag)
201 		trap = 0;
202 	if (flag & TF_DFL_INTR)
203 		intrsig = 0;
204 	if (flag & TF_FATAL)
205 		fatal_trap = 0;
206 	for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++)
207 		if (p->set && (!flag
208 			       || ((p->flags & flag) && p->trap == (char *) 0)))
209 			runtrap(p);
210 }
211 
212 void
213 runtrap(p)
214 	Trap *p;
215 {
216 	int	i = p->signal;
217 	char	*trapstr = p->trap;
218 	int	oexstat;
219 	int	UNINITIALIZED(old_changed);
220 
221 	p->set = 0;
222 	if (trapstr == (char *) 0) { /* SIG_DFL */
223 		if (p->flags & TF_FATAL) {
224 			/* eg, SIGHUP */
225 			exstat = 128 + i;
226 			unwind(LLEAVE);
227 		}
228 		if (p->flags & TF_DFL_INTR) {
229 			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
230 			exstat = 128 + i;
231 			unwind(LINTR);
232 		}
233 		return;
234 	}
235 	if (trapstr[0] == '\0') /* SIG_IGN */
236 		return;
237 	if (i == SIGEXIT_ || i == SIGERR_) {	/* avoid recursion on these */
238 		old_changed = p->flags & TF_CHANGED;
239 		p->flags &= ~TF_CHANGED;
240 		p->trap = (char *) 0;
241 	}
242 	oexstat = exstat;
243 	/* Note: trapstr is fully parsed before anything is executed, thus
244 	 * no problem with afree(p->trap) in settrap() while still in use.
245 	 */
246 	command(trapstr);
247 	exstat = oexstat;
248 	if (i == SIGEXIT_ || i == SIGERR_) {
249 		if (p->flags & TF_CHANGED)
250 			/* don't clear TF_CHANGED */
251 			afree(trapstr, APERM);
252 		else
253 			p->trap = trapstr;
254 		p->flags |= old_changed;
255 	}
256 }
257 
258 /* clear pending traps and reset user's trap handlers; used after fork(2) */
259 void
260 cleartraps()
261 {
262 	int i;
263 	Trap *p;
264 
265 	trap = 0;
266 	intrsig = 0;
267 	fatal_trap = 0;
268 	for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++) {
269 		p->set = 0;
270 		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
271 			settrap(p, (char *) 0);
272 	}
273 }
274 
275 /* restore signals just before an exec(2) */
276 void
277 restoresigs()
278 {
279 	int i;
280 	Trap *p;
281 
282 	for (i = SIGNALS+1, p = sigtraps; --i >= 0; p++)
283 		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
284 			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
285 				SS_RESTORE_CURR|SS_FORCE);
286 }
287 
288 void
289 settrap(p, s)
290 	Trap *p;
291 	char *s;
292 {
293 	handler_t f;
294 
295 	if (p->trap)
296 		afree(p->trap, APERM);
297 	p->trap = str_save(s, APERM); /* handles s == 0 */
298 	p->flags |= TF_CHANGED;
299 	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
300 
301 	p->flags |= TF_USER_SET;
302 	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
303 		f = trapsig;
304 	else if (p->flags & TF_SHELL_USES) {
305 		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
306 			/* do what user wants at exec time */
307 			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
308 			if (f == SIG_IGN)
309 				p->flags |= TF_EXEC_IGN;
310 			else
311 				p->flags |= TF_EXEC_DFL;
312 		}
313 		/* assumes handler already set to what shell wants it
314 		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
315 		 */
316 		return;
317 	}
318 
319 	/* todo: should we let user know signal is ignored? how? */
320 	setsig(p, f, SS_RESTORE_CURR|SS_USER);
321 }
322 
323 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
324  * kill shell (unless user catches it and exits)
325  */
326 int
327 block_pipe()
328 {
329 	int restore_dfl = 0;
330 	Trap *p = &sigtraps[SIGPIPE];
331 
332 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
333 		setsig(p, SIG_IGN, SS_RESTORE_CURR);
334 		if (p->flags & TF_ORIG_DFL)
335 			restore_dfl = 1;
336 	} else if (p->cursig == SIG_DFL) {
337 		setsig(p, SIG_IGN, SS_RESTORE_CURR);
338 		restore_dfl = 1; /* restore to SIG_DFL */
339 	}
340 	return restore_dfl;
341 }
342 
343 /* Called by c_print() to undo whatever block_pipe() did */
344 void
345 restore_pipe(restore_dfl)
346 	int restore_dfl;
347 {
348 	if (restore_dfl)
349 		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
350 }
351 
352 /* Set action for a signal.  Action may not be set if original
353  * action was SIG_IGN, depending on the value of flags and
354  * FTALKING.
355  */
356 int
357 setsig(p, f, flags)
358 	Trap *p;
359 	handler_t f;
360 	int flags;
361 {
362 	struct sigaction sigact;
363 
364 	if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
365 		return 1;
366 
367 	/* First time setting this signal?  If so, get and note the current
368 	 * setting.
369 	 */
370 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
371 		sigaction(p->signal, &Sigact_ign, &sigact);
372 		p->flags |= sigact.sa_handler == SIG_IGN ?
373 					TF_ORIG_IGN : TF_ORIG_DFL;
374 		p->cursig = SIG_IGN;
375 	}
376 
377 	/* Generally, an ignored signal stays ignored, except if
378 	 *	- the user of an interactive shell wants to change it
379 	 *	- the shell wants for force a change
380 	 */
381 	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE)
382 	    && (!(flags & SS_USER) || !Flag(FTALKING)))
383 		return 0;
384 
385 	setexecsig(p, flags & SS_RESTORE_MASK);
386 
387 	/* This is here 'cause there should be a way of clearing shtraps, but
388 	 * don't know if this is a sane way of doing it.  At the moment,
389 	 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
390 	 */
391 	if (!(flags & SS_USER))
392 		p->shtrap = (handler_t) 0;
393 	if (flags & SS_SHTRAP) {
394 		p->shtrap = f;
395 		f = trapsig;
396 	}
397 
398 	if (p->cursig != f) {
399 		p->cursig = f;
400 		sigemptyset(&sigact.sa_mask);
401 		sigact.sa_flags = KSH_SA_FLAGS;
402 		sigact.sa_handler = f;
403 		sigaction(p->signal, &sigact, (struct sigaction *) 0);
404 	}
405 
406 	return 1;
407 }
408 
409 /* control what signal is set to before an exec() */
410 void
411 setexecsig(p, restore)
412 	Trap *p;
413 	int restore;
414 {
415 	/* XXX debugging */
416 	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
417 		internal_errorf(1, "setexecsig: unset signal %d(%s)",
418 			p->signal, p->name);
419 
420 	/* restore original value for exec'd kids */
421 	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
422 	switch (restore & SS_RESTORE_MASK) {
423 	  case SS_RESTORE_CURR: /* leave things as they currently are */
424 		break;
425 	  case SS_RESTORE_ORIG:
426 		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
427 		break;
428 	  case SS_RESTORE_DFL:
429 		p->flags |= TF_EXEC_DFL;
430 		break;
431 	  case SS_RESTORE_IGN:
432 		p->flags |= TF_EXEC_IGN;
433 		break;
434 	}
435 }
436