sync
[bitrig.git] / bin / ksh / trap.c
blob9b6ca16beedec709fc0336de2f50489da81a05b5
1 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
3 /*
4 * signal handling
5 */
7 #include "sh.h"
9 Trap sigtraps[NSIG + 1];
11 static struct sigaction Sigact_ign, Sigact_trap;
13 void
14 inittraps(void)
16 int i;
18 /* Populate sigtraps based on sys_signame and sys_siglist. */
19 for (i = 0; i <= NSIG; i++) {
20 sigtraps[i].signal = i;
21 if (i == SIGERR_) {
22 sigtraps[i].name = "ERR";
23 sigtraps[i].mess = "Error handler";
24 } else {
25 sigtraps[i].name = sys_signame[i];
26 sigtraps[i].mess = sys_siglist[i];
29 sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */
31 sigemptyset(&Sigact_ign.sa_mask);
32 Sigact_ign.sa_flags = 0; /* interruptible */
33 Sigact_ign.sa_handler = SIG_IGN;
34 Sigact_trap = Sigact_ign;
35 Sigact_trap.sa_handler = trapsig;
37 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
38 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
39 sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */
40 sigtraps[SIGHUP].flags |= TF_FATAL;
41 sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
43 /* these are always caught so we can clean up any temporary files. */
44 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
45 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
46 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
47 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
50 static void alarm_catcher(int sig);
52 void
53 alarm_init(void)
55 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
56 setsig(&sigtraps[SIGALRM], alarm_catcher,
57 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
60 /* ARGSUSED */
61 static void
62 alarm_catcher(int sig)
64 int errno_ = errno;
66 if (ksh_tmout_state == TMOUT_READING) {
67 int left = alarm(0);
69 if (left == 0) {
70 ksh_tmout_state = TMOUT_LEAVING;
71 intrsig = 1;
72 } else
73 alarm(left);
75 errno = errno_;
78 Trap *
79 gettrap(const char *name, int igncase)
81 int i;
82 Trap *p;
84 if (digit(*name)) {
85 int n;
87 if (getn(name, &n) && 0 <= n && n < NSIG)
88 return &sigtraps[n];
89 return NULL;
91 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
92 if (p->name) {
93 if (igncase) {
94 if (p->name && (!strcasecmp(p->name, name) ||
95 (strlen(name) > 3 && !strncasecmp("SIG",
96 p->name, 3) &&
97 !strcasecmp(p->name, name + 3))))
98 return p;
99 } else {
100 if (p->name && (!strcmp(p->name, name) ||
101 (strlen(name) > 3 && !strncmp("SIG",
102 p->name, 3) && !strcmp(p->name, name + 3))))
103 return p;
106 return NULL;
110 * trap signal handler
112 void
113 trapsig(int i)
115 Trap *p = &sigtraps[i];
116 int errno_ = errno;
118 trap = p->set = 1;
119 if (p->flags & TF_DFL_INTR)
120 intrsig = 1;
121 if ((p->flags & TF_FATAL) && !p->trap) {
122 fatal_trap = 1;
123 intrsig = 1;
125 if (p->shtrap)
126 (*p->shtrap)(i);
127 errno = errno_;
130 /* called when we want to allow the user to ^C out of something - won't
131 * work if user has trapped SIGINT.
133 void
134 intrcheck(void)
136 if (intrsig)
137 runtraps(TF_DFL_INTR|TF_FATAL);
140 /* called after EINTR to check if a signal with normally causes process
141 * termination has been received.
144 fatal_trap_check(void)
146 int i;
147 Trap *p;
149 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
150 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
151 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
152 /* return value is used as an exit code */
153 return 128 + p->signal;
154 return 0;
157 /* Returns the signal number of any pending traps: ie, a signal which has
158 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
159 * is set.
162 trap_pending(void)
164 int i;
165 Trap *p;
167 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
168 if (p->set && ((p->trap && p->trap[0]) ||
169 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
170 return p->signal;
171 return 0;
175 * run any pending traps. If intr is set, only run traps that
176 * can interrupt commands.
178 void
179 runtraps(int flag)
181 int i;
182 Trap *p;
184 if (ksh_tmout_state == TMOUT_LEAVING) {
185 ksh_tmout_state = TMOUT_EXECUTING;
186 warningf(false, "timed out waiting for input");
187 unwind(LEXIT);
188 } else
189 /* XXX: this means the alarm will have no effect if a trap
190 * is caught after the alarm() was started...not good.
192 ksh_tmout_state = TMOUT_EXECUTING;
193 if (!flag)
194 trap = 0;
195 if (flag & TF_DFL_INTR)
196 intrsig = 0;
197 if (flag & TF_FATAL)
198 fatal_trap = 0;
199 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
200 if (p->set && (!flag ||
201 ((p->flags & flag) && p->trap == (char *) 0)))
202 runtrap(p);
205 void
206 runtrap(Trap *p)
208 int i = p->signal;
209 char *trapstr = p->trap;
210 int oexstat;
211 int old_changed = 0;
213 p->set = 0;
214 if (trapstr == (char *) 0) { /* SIG_DFL */
215 if (p->flags & TF_FATAL) {
216 /* eg, SIGHUP */
217 exstat = 128 + i;
218 unwind(LLEAVE);
220 if (p->flags & TF_DFL_INTR) {
221 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
222 exstat = 128 + i;
223 unwind(LINTR);
225 return;
227 if (trapstr[0] == '\0') /* SIG_IGN */
228 return;
229 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */
230 old_changed = p->flags & TF_CHANGED;
231 p->flags &= ~TF_CHANGED;
232 p->trap = (char *) 0;
234 oexstat = exstat;
235 /* Note: trapstr is fully parsed before anything is executed, thus
236 * no problem with afree(p->trap) in settrap() while still in use.
238 command(trapstr, current_lineno);
239 exstat = oexstat;
240 if (i == SIGEXIT_ || i == SIGERR_) {
241 if (p->flags & TF_CHANGED)
242 /* don't clear TF_CHANGED */
243 afree(trapstr, APERM);
244 else
245 p->trap = trapstr;
246 p->flags |= old_changed;
250 /* clear pending traps and reset user's trap handlers; used after fork(2) */
251 void
252 cleartraps(void)
254 int i;
255 Trap *p;
257 trap = 0;
258 intrsig = 0;
259 fatal_trap = 0;
260 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
261 p->set = 0;
262 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
263 settrap(p, (char *) 0);
267 /* restore signals just before an exec(2) */
268 void
269 restoresigs(void)
271 int i;
272 Trap *p;
274 for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
275 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
276 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
277 SS_RESTORE_CURR|SS_FORCE);
280 void
281 settrap(Trap *p, char *s)
283 sig_t f;
285 if (p->trap)
286 afree(p->trap, APERM);
287 p->trap = str_save(s, APERM); /* handles s == 0 */
288 p->flags |= TF_CHANGED;
289 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
291 p->flags |= TF_USER_SET;
292 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
293 f = trapsig;
294 else if (p->flags & TF_SHELL_USES) {
295 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
296 /* do what user wants at exec time */
297 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
298 if (f == SIG_IGN)
299 p->flags |= TF_EXEC_IGN;
300 else
301 p->flags |= TF_EXEC_DFL;
304 /* assumes handler already set to what shell wants it
305 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
307 return;
310 /* todo: should we let user know signal is ignored? how? */
311 setsig(p, f, SS_RESTORE_CURR|SS_USER);
314 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't
315 * kill shell (unless user catches it and exits)
318 block_pipe(void)
320 int restore_dfl = 0;
321 Trap *p = &sigtraps[SIGPIPE];
323 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
324 setsig(p, SIG_IGN, SS_RESTORE_CURR);
325 if (p->flags & TF_ORIG_DFL)
326 restore_dfl = 1;
327 } else if (p->cursig == SIG_DFL) {
328 setsig(p, SIG_IGN, SS_RESTORE_CURR);
329 restore_dfl = 1; /* restore to SIG_DFL */
331 return restore_dfl;
334 /* Called by c_print() to undo whatever block_pipe() did */
335 void
336 restore_pipe(int restore_dfl)
338 if (restore_dfl)
339 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
342 /* Set action for a signal. Action may not be set if original
343 * action was SIG_IGN, depending on the value of flags and
344 * FTALKING.
347 setsig(Trap *p, sig_t f, int flags)
349 struct sigaction sigact;
351 if (p->signal == SIGEXIT_ || p->signal == SIGERR_)
352 return 1;
354 /* First time setting this signal? If so, get and note the current
355 * setting.
357 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
358 sigaction(p->signal, &Sigact_ign, &sigact);
359 p->flags |= sigact.sa_handler == SIG_IGN ?
360 TF_ORIG_IGN : TF_ORIG_DFL;
361 p->cursig = SIG_IGN;
364 /* Generally, an ignored signal stays ignored, except if
365 * - the user of an interactive shell wants to change it
366 * - the shell wants for force a change
368 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
369 (!(flags & SS_USER) || !Flag(FTALKING)))
370 return 0;
372 setexecsig(p, flags & SS_RESTORE_MASK);
374 /* This is here 'cause there should be a way of clearing shtraps, but
375 * don't know if this is a sane way of doing it. At the moment,
376 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH).
378 if (!(flags & SS_USER))
379 p->shtrap = NULL;
380 if (flags & SS_SHTRAP) {
381 p->shtrap = f;
382 f = trapsig;
385 if (p->cursig != f) {
386 p->cursig = f;
387 sigemptyset(&sigact.sa_mask);
388 sigact.sa_flags = 0 /* interruptible */;
389 sigact.sa_handler = f;
390 sigaction(p->signal, &sigact, (struct sigaction *) 0);
393 return 1;
396 /* control what signal is set to before an exec() */
397 void
398 setexecsig(Trap *p, int restore)
400 /* XXX debugging */
401 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
402 internal_errorf(1, "setexecsig: unset signal %d(%s)",
403 p->signal, p->name);
405 /* restore original value for exec'd kids */
406 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
407 switch (restore & SS_RESTORE_MASK) {
408 case SS_RESTORE_CURR: /* leave things as they currently are */
409 break;
410 case SS_RESTORE_ORIG:
411 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
412 break;
413 case SS_RESTORE_DFL:
414 p->flags |= TF_EXEC_DFL;
415 break;
416 case SS_RESTORE_IGN:
417 p->flags |= TF_EXEC_IGN;
418 break;