GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/trap.c Lines: 150 193 77.7 %
Date: 2017-11-07 Branches: 96 152 63.2 %

Line Branch Exec Source
1
/*	$OpenBSD: trap.c,v 1.30 2016/03/17 23:33:23 mmcc Exp $	*/
2
3
/*
4
 * signal handling
5
 */
6
7
#include <ctype.h>
8
#include <errno.h>
9
#include <string.h>
10
#include <unistd.h>
11
12
#include "sh.h"
13
14
Trap sigtraps[NSIG + 1];
15
16
static struct sigaction Sigact_ign, Sigact_trap;
17
18
void
19
inittraps(void)
20
{
21
	int	i;
22
23
	/* Populate sigtraps based on sys_signame and sys_siglist. */
24
15461670
	for (i = 0; i <= NSIG; i++) {
25
7404180
		sigtraps[i].signal = i;
26
7404180
		if (i == SIGERR_) {
27
217770
			sigtraps[i].name = "ERR";
28
			sigtraps[i].mess = "Error handler";
29
217770
		} else {
30
7186410
			sigtraps[i].name = sys_signame[i];
31
7186410
			sigtraps[i].mess = sys_siglist[i];
32
		}
33
	}
34
217770
	sigtraps[SIGEXIT_].name = "EXIT";	/* our name for signal 0 */
35
36
217770
	sigemptyset(&Sigact_ign.sa_mask);
37
217770
	Sigact_ign.sa_flags = 0; /* interruptible */
38
217770
	Sigact_ign.sa_handler = SIG_IGN;
39
217770
	Sigact_trap = Sigact_ign;
40
217770
	Sigact_trap.sa_handler = trapsig;
41
42
217770
	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
43
217770
	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
44
217770
	sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
45
217770
	sigtraps[SIGHUP].flags |= TF_FATAL;
46
217770
	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
47
48
	/* these are always caught so we can clean up any temporary files. */
49
217770
	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
50
217770
	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
51
217770
	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
52
217770
	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
53
217770
}
54
55
static void alarm_catcher(int sig);
56
57
void
58
alarm_init(void)
59
{
60
7248
	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
61
3624
	setsig(&sigtraps[SIGALRM], alarm_catcher,
62
		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
63
3624
}
64
65
static void
66
alarm_catcher(int sig)
67
{
68
	int errno_ = errno;
69
70
	if (ksh_tmout_state == TMOUT_READING) {
71
		int left = alarm(0);
72
73
		if (left == 0) {
74
			ksh_tmout_state = TMOUT_LEAVING;
75
			intrsig = 1;
76
		} else
77
			alarm(left);
78
	}
79
	errno = errno_;
80
}
81
82
Trap *
83
gettrap(const char *name, int igncase)
84
{
85
	int i;
86
	Trap *p;
87
88
47470
	if (digit(*name)) {
89
12307
		int n;
90
91
12307
		if (getn(name, &n) && 0 <= n && n < NSIG)
92
12307
			return &sigtraps[n];
93
		return NULL;
94
12307
	}
95
778890
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
96
378429
		if (p->name) {
97
378429
			if (igncase) {
98

7770
				if (p->name && (!strcasecmp(p->name, name) ||
99
4241
				    (strlen(name) > 3 && !strncasecmp("SIG",
100
1536
				    p->name, 3) &&
101
				    !strcasecmp(p->name, name + 3))))
102
412
					return p;
103
			} else {
104

749088
				if (p->name && (!strcmp(p->name, name) ||
105
749088
				    (strlen(name) > 3 && !strncmp("SIG",
106
749088
				    p->name, 3) && !strcmp(p->name, name + 3))))
107
					return p;
108
			}
109
		}
110
11016
	return NULL;
111
23735
}
112
113
/*
114
 * trap signal handler
115
 */
116
void
117
trapsig(int i)
118
{
119
14130298
	Trap *p = &sigtraps[i];
120
7065149
	int errno_ = errno;
121
122
7065149
	trap = p->set = 1;
123
7065149
	if (p->flags & TF_DFL_INTR)
124
284
		intrsig = 1;
125

7067874
	if ((p->flags & TF_FATAL) && !p->trap) {
126
2725
		fatal_trap = 1;
127
2725
		intrsig = 1;
128
2725
	}
129
7065149
	if (p->shtrap)
130
6958547
		(*p->shtrap)(i);
131
7065149
	errno = errno_;
132
7065149
}
133
134
/* called when we want to allow the user to ^C out of something - won't
135
 * work if user has trapped SIGINT.
136
 */
137
void
138
intrcheck(void)
139
{
140
22262086
	if (intrsig)
141
74
		runtraps(TF_DFL_INTR|TF_FATAL);
142
11131043
}
143
144
/* called after EINTR to check if a signal with normally causes process
145
 * termination has been received.
146
 */
147
int
148
fatal_trap_check(void)
149
{
150
	int i;
151
	Trap *p;
152
153
	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
154
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
155
		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
156
			/* return value is used as an exit code */
157
			return 128 + p->signal;
158
	return 0;
159
}
160
161
/* Returns the signal number of any pending traps: ie, a signal which has
162
 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
163
 * is set.
164
 */
165
int
166
trap_pending(void)
167
{
168
	int i;
169
	Trap *p;
170
171
4189
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
172


2065
		if (p->set && ((p->trap && p->trap[0]) ||
173
59
		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
174
			return p->signal;
175
59
	return 0;
176
59
}
177
178
/*
179
 * run any pending traps.  If intr is set, only run traps that
180
 * can interrupt commands.
181
 */
182
void
183
runtraps(int flag)
184
{
185
	int i;
186
	Trap *p;
187
188
8915060
	if (ksh_tmout_state == TMOUT_LEAVING) {
189
		ksh_tmout_state = TMOUT_EXECUTING;
190
		warningf(false, "timed out waiting for input");
191
		unwind(LEXIT);
192
	} else
193
		/* XXX: this means the alarm will have no effect if a trap
194
		 * is caught after the alarm() was started...not good.
195
		 */
196
		ksh_tmout_state = TMOUT_EXECUTING;
197
4457530
	if (!flag)
198
4457456
		trap = 0;
199
4457530
	if (flag & TF_DFL_INTR)
200
74
		intrsig = 0;
201
4457530
	if (flag & TF_FATAL)
202
74
		fatal_trap = 0;
203
311832508
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
204

155959559
		if (p->set && (!flag ||
205
6
		    ((p->flags & flag) && p->trap == NULL)))
206
4497870
			runtrap(p);
207
4454571
}
208
209
void
210
runtrap(Trap *p)
211
{
212
9285478
	int	i = p->signal;
213
4642739
	char	*trapstr = p->trap;
214
	int	oexstat;
215
	int	old_changed = 0;
216
217
4642739
	p->set = 0;
218
4642739
	if (trapstr == NULL) { /* SIG_DFL */
219
4642269
		if (p->flags & TF_FATAL) {
220
			/* eg, SIGHUP */
221
			exstat = 128 + i;
222
			unwind(LLEAVE);
223
		}
224
4639544
		if (p->flags & TF_DFL_INTR) {
225
			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
226
			exstat = 128 + i;
227
			unwind(LINTR);
228
		}
229
4639321
		return;
230
	}
231
470
	if (trapstr[0] == '\0') /* SIG_IGN */
232
		return;
233
470
	if (i == SIGEXIT_ || i == SIGERR_) {	/* avoid recursion on these */
234
459
		old_changed = p->flags & TF_CHANGED;
235
459
		p->flags &= ~TF_CHANGED;
236
459
		p->trap = NULL;
237
459
	}
238
198
	oexstat = exstat;
239
	/* Note: trapstr is fully parsed before anything is executed, thus
240
	 * no problem with afree(p->trap) in settrap() while still in use.
241
	 */
242
198
	command(trapstr, current_lineno);
243
198
	exstat = oexstat;
244
198
	if (i == SIGEXIT_ || i == SIGERR_) {
245
198
		if (p->flags & TF_CHANGED)
246
			/* don't clear TF_CHANGED */
247
			afree(trapstr, APERM);
248
		else
249
198
			p->trap = trapstr;
250
198
		p->flags |= old_changed;
251
198
	}
252
4639717
}
253
254
/* clear pending traps and reset user's trap handlers; used after fork(2) */
255
void
256
cleartraps(void)
257
{
258
	int i;
259
	Trap *p;
260
261
163930
	trap = 0;
262
81965
	intrsig = 0;
263
81965
	fatal_trap = 0;
264
5737550
	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
265
2786810
		p->set = 0;
266

2809492
		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
267
11058
			settrap(p, NULL);
268
	}
269
81965
}
270
271
/* restore signals just before an exec(2) */
272
void
273
restoresigs(void)
274
{
275
	int i;
276
	Trap *p;
277
278
	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
279
		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
280
			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
281
			    SS_RESTORE_CURR|SS_FORCE);
282
}
283
284
void
285
settrap(Trap *p, char *s)
286
{
287
	sig_t f;
288
289
47468
	afree(p->trap, APERM);
290
23734
	p->trap = str_save(s, APERM); /* handles s == 0 */
291
23734
	p->flags |= TF_CHANGED;
292
60144
	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
293
294
23734
	p->flags |= TF_USER_SET;
295
23734
	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
296
5610
		f = trapsig;
297
18124
	else if (p->flags & TF_SHELL_USES) {
298
		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
299
			/* do what user wants at exec time */
300
			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
301
			if (f == SIG_IGN)
302
				p->flags |= TF_EXEC_IGN;
303
			else
304
				p->flags |= TF_EXEC_DFL;
305
		}
306
307
		/* assumes handler already set to what shell wants it
308
		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
309
		 */
310
		return;
311
	}
312
313
	/* todo: should we let user know signal is ignored? how? */
314
23734
	setsig(p, f, SS_RESTORE_CURR|SS_USER);
315
47468
}
316
317
/* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
318
 * kill shell (unless user catches it and exits)
319
 */
320
int
321
block_pipe(void)
322
{
323
	int restore_dfl = 0;
324
	Trap *p = &sigtraps[SIGPIPE];
325
326
	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
327
		setsig(p, SIG_IGN, SS_RESTORE_CURR);
328
		if (p->flags & TF_ORIG_DFL)
329
			restore_dfl = 1;
330
	} else if (p->cursig == SIG_DFL) {
331
		setsig(p, SIG_IGN, SS_RESTORE_CURR);
332
		restore_dfl = 1; /* restore to SIG_DFL */
333
	}
334
	return restore_dfl;
335
}
336
337
/* Called by c_print() to undo whatever block_pipe() did */
338
void
339
restore_pipe(int restore_dfl)
340
{
341
	if (restore_dfl)
342
		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
343
}
344
345
/* Set action for a signal.  Action may not be set if original
346
 * action was SIG_IGN, depending on the value of flags and
347
 * FTALKING.
348
 */
349
int
350
setsig(Trap *p, sig_t f, int flags)
351
{
352
2313934
	struct sigaction sigact;
353
354

2306307
	if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
355
7727
		return 1;
356
357
	/* First time setting this signal?  If so, get and note the current
358
	 * setting.
359
	 */
360
1149240
	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
361
1109007
		sigaction(p->signal, &Sigact_ign, &sigact);
362
1109007
		p->flags |= sigact.sa_handler == SIG_IGN ?
363
		    TF_ORIG_IGN : TF_ORIG_DFL;
364
1109007
		p->cursig = SIG_IGN;
365
1109007
	}
366
367
	/* Generally, an ignored signal stays ignored, except if
368
	 *	- the user of an interactive shell wants to change it
369
	 *	- the shell wants for force a change
370
	 */
371

1150676
	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
372
1426
	    (!(flags & SS_USER) || !Flag(FTALKING)))
373
1426
		return 0;
374
375
1147814
	setexecsig(p, flags & SS_RESTORE_MASK);
376
377
	/* This is here 'cause there should be a way of clearing shtraps, but
378
	 * don't know if this is a sane way of doing it.  At the moment,
379
	 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
380
	 */
381
1147814
	if (!(flags & SS_USER))
382
1131807
		p->shtrap = NULL;
383
1147814
	if (flags & SS_SHTRAP) {
384
225016
		p->shtrap = f;
385
		f = trapsig;
386
225016
	}
387
388
1147814
	if (p->cursig != f) {
389
1106860
		p->cursig = f;
390
1106860
		sigemptyset(&sigact.sa_mask);
391
1106860
		sigact.sa_flags = 0 /* interruptible */;
392
1106860
		sigact.sa_handler = f;
393
1106860
		sigaction(p->signal, &sigact, NULL);
394
1106860
	}
395
396
1147814
	return 1;
397
1156967
}
398
399
/* control what signal is set to before an exec() */
400
void
401
setexecsig(Trap *p, int restore)
402
{
403
	/* XXX debugging */
404
2295628
	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
405
		internal_errorf(1, "setexecsig: unset signal %d(%s)",
406
		    p->signal, p->name);
407
408
	/* restore original value for exec'd kids */
409
2279621
	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
410

2279621
	switch (restore & SS_RESTORE_MASK) {
411
	case SS_RESTORE_CURR: /* leave things as they currently are */
412
		break;
413
	case SS_RESTORE_ORIG:
414
1098294
		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
415
1098294
		break;
416
	case SS_RESTORE_DFL:
417
		p->flags |= TF_EXEC_DFL;
418
11181
		break;
419
	case SS_RESTORE_IGN:
420
		p->flags |= TF_EXEC_IGN;
421
22332
		break;
422
	}
423
2279621
}