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