GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/trap.c Lines: 137 189 72.5 %
Date: 2016-12-06 Branches: 81 166 48.8 %

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
3525
{
21
	int	i;
22
23
	/* Populate sigtraps based on sys_signame and sys_siglist. */
24
123375
	for (i = 0; i <= NSIG; i++) {
25
119850
		sigtraps[i].signal = i;
26
119850
		if (i == SIGERR_) {
27
3525
			sigtraps[i].name = "ERR";
28
3525
			sigtraps[i].mess = "Error handler";
29
		} else {
30
116325
			sigtraps[i].name = sys_signame[i];
31
116325
			sigtraps[i].mess = sys_siglist[i];
32
		}
33
	}
34
3525
	sigtraps[SIGEXIT_].name = "EXIT";	/* our name for signal 0 */
35
36
3525
	sigemptyset(&Sigact_ign.sa_mask);
37
3525
	Sigact_ign.sa_flags = 0; /* interruptible */
38
3525
	Sigact_ign.sa_handler = SIG_IGN;
39
3525
	Sigact_trap = Sigact_ign;
40
3525
	Sigact_trap.sa_handler = trapsig;
41
42
3525
	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
43
3525
	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
44
3525
	sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
45
3525
	sigtraps[SIGHUP].flags |= TF_FATAL;
46
3525
	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
47
48
	/* these are always caught so we can clean up any temporary files. */
49
3525
	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
50
3525
	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
51
3525
	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
52
3525
	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
53
3525
}
54
55
static void alarm_catcher(int sig);
56
57
void
58
alarm_init(void)
59
63
{
60
63
	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
61
63
	setsig(&sigtraps[SIGALRM], alarm_catcher,
62
		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
63
63
}
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
74
{
85
	int i;
86
	Trap *p;
87
88
74
	if (digit(*name)) {
89
		int n;
90
91

31
		if (getn(name, &n) && 0 <= n && n < NSIG)
92
31
			return &sigtraps[n];
93
		return NULL;
94
	}
95
1229
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
96
1198
		if (p->name) {
97
1198
			if (igncase) {
98


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


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

15443
	if ((p->flags & TF_FATAL) && !p->trap) {
126
		fatal_trap = 1;
127
		intrsig = 1;
128
	}
129
15443
	if (p->shtrap)
130
14189
		(*p->shtrap)(i);
131
15443
	errno = errno_;
132
15443
}
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
15505
{
140
15505
	if (intrsig)
141
		runtraps(TF_DFL_INTR|TF_FATAL);
142
15505
}
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
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
172
		if (p->set && ((p->trap && p->trap[0]) ||
173
		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !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(int flag)
184
11928
{
185
	int i;
186
	Trap *p;
187
188
11928
	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
11928
		ksh_tmout_state = TMOUT_EXECUTING;
197
11928
	if (!flag)
198
11928
		trap = 0;
199
11928
	if (flag & TF_DFL_INTR)
200
		intrsig = 0;
201
11928
	if (flag & TF_FATAL)
202
		fatal_trap = 0;
203
417480
	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
204


405552
		if (p->set && (!flag ||
205
		    ((p->flags & flag) && p->trap == NULL)))
206
12688
			runtrap(p);
207
11928
}
208
209
void
210
runtrap(Trap *p)
211
16832
{
212
16832
	int	i = p->signal;
213
16832
	char	*trapstr = p->trap;
214
	int	oexstat;
215
16832
	int	old_changed = 0;
216
217
16832
	p->set = 0;
218
16832
	if (trapstr == NULL) { /* SIG_DFL */
219
16815
		if (p->flags & TF_FATAL) {
220
			/* eg, SIGHUP */
221
			exstat = 128 + i;
222
			unwind(LLEAVE);
223
		}
224
16815
		if (p->flags & TF_DFL_INTR) {
225
			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
226
			exstat = 128 + i;
227
			unwind(LINTR);
228
		}
229
		return;
230
	}
231
17
	if (trapstr[0] == '\0') /* SIG_IGN */
232
		return;
233
17
	if (i == SIGEXIT_ || i == SIGERR_) {	/* avoid recursion on these */
234
17
		old_changed = p->flags & TF_CHANGED;
235
17
		p->flags &= ~TF_CHANGED;
236
17
		p->trap = NULL;
237
	}
238
17
	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
17
	command(trapstr, current_lineno);
243
12
	exstat = oexstat;
244
12
	if (i == SIGEXIT_ || i == SIGERR_) {
245
12
		if (p->flags & TF_CHANGED)
246
			/* don't clear TF_CHANGED */
247
			afree(trapstr, APERM);
248
		else
249
12
			p->trap = trapstr;
250
12
		p->flags |= old_changed;
251
	}
252
}
253
254
/* clear pending traps and reset user's trap handlers; used after fork(2) */
255
void
256
cleartraps(void)
257
14169
{
258
	int i;
259
	Trap *p;
260
261
14169
	trap = 0;
262
14169
	intrsig = 0;
263
14169
	fatal_trap = 0;
264
495915
	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
265
481746
		p->set = 0;
266

481746
		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
267
20953
			settrap(p, NULL);
268
	}
269
14169
}
270
271
/* restore signals just before an exec(2) */
272
void
273
restoresigs(void)
274
10648
{
275
	int i;
276
	Trap *p;
277
278
372680
	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
279
362032
		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
280
42985
			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
281
			    SS_RESTORE_CURR|SS_FORCE);
282
10648
}
283
284
void
285
settrap(Trap *p, char *s)
286
20996
{
287
	sig_t f;
288
289
20996
	afree(p->trap, APERM);
290
20996
	p->trap = str_save(s, APERM); /* handles s == 0 */
291
20996
	p->flags |= TF_CHANGED;
292

20996
	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
293
294
20996
	p->flags |= TF_USER_SET;
295
20996
	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
296
12592
		f = trapsig;
297
8404
	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
20996
	setsig(p, f, SS_RESTORE_CURR|SS_USER);
315
}
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
82662
{
352
	struct sigaction sigact;
353
354
82662
	if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
355
4190
		return 1;
356
357
	/* First time setting this signal?  If so, get and note the current
358
	 * setting.
359
	 */
360
78472
	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
361
17945
		sigaction(p->signal, &Sigact_ign, &sigact);
362
17945
		p->flags |= sigact.sa_handler == SIG_IGN ?
363
		    TF_ORIG_IGN : TF_ORIG_DFL;
364
17945
		p->cursig = SIG_IGN;
365
	}
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


78472
	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
372
	    (!(flags & SS_USER) || !Flag(FTALKING)))
373
		return 0;
374
375
78472
	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
78472
	if (!(flags & SS_USER))
382
61666
		p->shtrap = NULL;
383
78472
	if (flags & SS_SHTRAP) {
384
3651
		p->shtrap = f;
385
3651
		f = trapsig;
386
	}
387
388
78472
	if (p->cursig != f) {
389
65231
		p->cursig = f;
390
		sigemptyset(&sigact.sa_mask);
391
65231
		sigact.sa_flags = 0 /* interruptible */;
392
65231
		sigact.sa_handler = f;
393
65231
		sigaction(p->signal, &sigact, NULL);
394
	}
395
396
78472
	return 1;
397
}
398
399
/* control what signal is set to before an exec() */
400
void
401
setexecsig(Trap *p, int restore)
402
85782
{
403
	/* XXX debugging */
404
85782
	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
85782
	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
410

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