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