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