don't bother resolving onbld python module deps
[unleashed.git] / bin / ksh / trap.c
blobfd4d9b2144b5dde247a72ede65178b03a4b5e32f
1 /* $OpenBSD: trap.c,v 1.32 2018/03/15 16:51:29 anton Exp $ */
3 /*
4 * signal handling
5 */
7 #include <ctype.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <unistd.h>
12 #include "sh.h"
14 Trap sigtraps[NSIG + 1];
16 static struct sigaction Sigact_ign, Sigact_trap;
18 void
19 inittraps(void)
21 int i;
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];
34 sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */
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;
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;
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);
55 static void alarm_catcher(int sig);
57 void
58 alarm_init(void)
60 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
61 setsig(&sigtraps[SIGALRM], alarm_catcher,
62 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
65 static void
66 alarm_catcher(int sig)
68 int errno_ = errno;
70 if (ksh_tmout_state == TMOUT_READING) {
71 int left = alarm(0);
73 if (left == 0) {
74 ksh_tmout_state = TMOUT_LEAVING;
75 intrsig = 1;
76 } else
77 alarm(left);
79 errno = errno_;
82 Trap *
83 gettrap(const char *name, int igncase)
85 int i;
86 Trap *p;
88 if (digit(*name)) {
89 int n;
91 if (getn(name, &n) && 0 <= n && n < NSIG)
92 return &sigtraps[n];
93 return NULL;
95 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
96 if (p->name) {
97 if (igncase) {
98 if (p->name && (!strcasecmp(p->name, name) ||
99 (strlen(name) > 3 && !strncasecmp("SIG",
100 p->name, 3) &&
101 !strcasecmp(p->name, name + 3))))
102 return p;
103 } else {
104 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;
110 return NULL;
114 * trap signal handler
116 void
117 trapsig(int i)
119 Trap *p = &sigtraps[i];
120 int errno_ = errno;
122 trap = p->set = 1;
123 if (p->flags & TF_DFL_INTR)
124 intrsig = 1;
125 if ((p->flags & TF_FATAL) && !p->trap) {
126 fatal_trap = 1;
127 intrsig = 1;
129 if (p->shtrap)
130 (*p->shtrap)(i);
131 errno = errno_;
134 /* called when we want to allow the user to ^C out of something - won't
135 * work if user has trapped SIGINT.
137 void
138 intrcheck(void)
140 if (intrsig)
141 runtraps(TF_DFL_INTR|TF_FATAL);
144 /* called after EINTR to check if a signal with normally causes process
145 * termination has been received.
148 fatal_trap_check(void)
150 int i;
151 Trap *p;
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;
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.
166 trap_pending(void)
168 int i;
169 Trap *p;
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;
179 * run any pending traps. If intr is set, only run traps that
180 * can interrupt commands.
182 void
183 runtraps(int flag)
185 int i;
186 Trap *p;
188 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.
196 ksh_tmout_state = TMOUT_EXECUTING;
197 if (!flag)
198 trap = 0;
199 if (flag & TF_DFL_INTR)
200 intrsig = 0;
201 if (flag & TF_FATAL)
202 fatal_trap = 0;
203 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
204 if (p->set && (!flag ||
205 ((p->flags & flag) && p->trap == NULL)))
206 runtrap(p);
209 void
210 runtrap(Trap *p)
212 int i = p->signal;
213 char *trapstr = p->trap;
214 int oexstat;
215 int old_changed = 0;
217 p->set = 0;
218 if (trapstr == NULL) { /* SIG_DFL */
219 if (p->flags & TF_FATAL) {
220 /* eg, SIGHUP */
221 exstat = 128 + i;
222 unwind(LLEAVE);
224 if (p->flags & TF_DFL_INTR) {
225 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
226 exstat = 128 + i;
227 unwind(LINTR);
229 return;
231 if (trapstr[0] == '\0') /* SIG_IGN */
232 return;
233 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
234 old_changed = p->flags & TF_CHANGED;
235 p->flags &= ~TF_CHANGED;
236 p->trap = NULL;
238 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.
242 command(trapstr, current_lineno);
243 exstat = oexstat;
244 if (i == SIGEXIT_ || i == SIGERR_) {
245 if (p->flags & TF_CHANGED)
246 /* don't clear TF_CHANGED */
247 afree(trapstr, APERM);
248 else
249 p->trap = trapstr;
250 p->flags |= old_changed;
254 /* clear pending traps and reset user's trap handlers; used after fork(2) */
255 void
256 cleartraps(void)
258 int i;
259 Trap *p;
261 trap = 0;
262 intrsig = 0;
263 fatal_trap = 0;
264 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
265 p->set = 0;
266 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
267 settrap(p, NULL);
271 /* restore signals just before an exec(2) */
272 void
273 restoresigs(void)
275 int i;
276 Trap *p;
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);
284 void
285 settrap(Trap *p, char *s)
287 sig_t f;
289 afree(p->trap, APERM);
290 p->trap = str_save(s, APERM); /* handles s == 0 */
291 p->flags |= TF_CHANGED;
292 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
294 p->flags |= TF_USER_SET;
295 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
296 f = trapsig;
297 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;
307 /* assumes handler already set to what shell wants it
308 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
310 return;
313 /* todo: should we let user know signal is ignored? how? */
314 setsig(p, f, SS_RESTORE_CURR|SS_USER);
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)
321 block_pipe(void)
323 int restore_dfl = 0;
324 Trap *p = &sigtraps[SIGPIPE];
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 */
334 return restore_dfl;
337 /* Called by c_print() to undo whatever block_pipe() did */
338 void
339 restore_pipe(int restore_dfl)
341 if (restore_dfl)
342 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
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.
350 setsig(Trap *p, sig_t f, int flags)
352 struct sigaction sigact;
354 if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
355 return 1;
357 /* First time setting this signal? If so, get and note the current
358 * setting.
360 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
361 sigaction(p->signal, &Sigact_ign, &sigact);
362 p->flags |= sigact.sa_handler == SIG_IGN ?
363 TF_ORIG_IGN : TF_ORIG_DFL;
364 p->cursig = SIG_IGN;
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
371 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
372 (!(flags & SS_USER) || !Flag(FTALKING)))
373 return 0;
375 setexecsig(p, flags & SS_RESTORE_MASK);
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).
381 if (!(flags & SS_USER))
382 p->shtrap = NULL;
383 if (flags & SS_SHTRAP) {
384 p->shtrap = f;
385 f = trapsig;
388 if (p->cursig != f) {
389 p->cursig = f;
390 sigemptyset(&sigact.sa_mask);
391 sigact.sa_flags = 0 /* interruptible */;
392 sigact.sa_handler = f;
393 sigaction(p->signal, &sigact, NULL);
396 return 1;
399 /* control what signal is set to before an exec() */
400 void
401 setexecsig(Trap *p, int restore)
403 /* XXX debugging */
404 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
405 internal_errorf("%s: unset signal %d(%s)",
406 __func__, p->signal, p->name);
408 /* restore original value for exec'd kids */
409 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
410 switch (restore & SS_RESTORE_MASK) {
411 case SS_RESTORE_CURR: /* leave things as they currently are */
412 break;
413 case SS_RESTORE_ORIG:
414 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
415 break;
416 case SS_RESTORE_DFL:
417 p->flags |= TF_EXEC_DFL;
418 break;
419 case SS_RESTORE_IGN:
420 p->flags |= TF_EXEC_IGN;
421 break;