1 /* $OpenBSD: trap.c,v 1.30 2016/03/17 23:33:23 mmcc 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 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 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 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 * 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 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; 108 } 109 } 110 return NULL; 111 } 112 113 /* 114 * trap signal handler 115 */ 116 void 117 trapsig(int i) 118 { 119 Trap *p = &sigtraps[i]; 120 int errno_ = errno; 121 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; 128 } 129 if (p->shtrap) 130 (*p->shtrap)(i); 131 errno = errno_; 132 } 133 134 /* called when we want to allow the user to ^C out of something - won't 135 * work if user has trapped SIGINT. 136 */ 137 void 138 intrcheck(void) 139 { 140 if (intrsig) 141 runtraps(TF_DFL_INTR|TF_FATAL); 142 } 143 144 /* called after EINTR to check if a signal with normally causes process 145 * termination has been received. 146 */ 147 int 148 fatal_trap_check(void) 149 { 150 int i; 151 Trap *p; 152 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; 159 } 160 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. 164 */ 165 int 166 trap_pending(void) 167 { 168 int i; 169 Trap *p; 170 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; 176 } 177 178 /* 179 * run any pending traps. If intr is set, only run traps that 180 * can interrupt commands. 181 */ 182 void 183 runtraps(int flag) 184 { 185 int i; 186 Trap *p; 187 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. 195 */ 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); 207 } 208 209 void 210 runtrap(Trap *p) 211 { 212 int i = p->signal; 213 char *trapstr = p->trap; 214 int oexstat; 215 int old_changed = 0; 216 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); 223 } 224 if (p->flags & TF_DFL_INTR) { 225 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ 226 exstat = 128 + i; 227 unwind(LINTR); 228 } 229 return; 230 } 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; 237 } 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. 241 */ 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; 251 } 252 } 253 254 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 255 void 256 cleartraps(void) 257 { 258 int i; 259 Trap *p; 260 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); 268 } 269 } 270 271 /* restore signals just before an exec(2) */ 272 void 273 restoresigs(void) 274 { 275 int i; 276 Trap *p; 277 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); 282 } 283 284 void 285 settrap(Trap *p, char *s) 286 { 287 sig_t f; 288 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; 293 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; 305 } 306 307 /* assumes handler already set to what shell wants it 308 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 309 */ 310 return; 311 } 312 313 /* todo: should we let user know signal is ignored? how? */ 314 setsig(p, f, SS_RESTORE_CURR|SS_USER); 315 } 316 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) 319 */ 320 int 321 block_pipe(void) 322 { 323 int restore_dfl = 0; 324 Trap *p = &sigtraps[SIGPIPE]; 325 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 */ 333 } 334 return restore_dfl; 335 } 336 337 /* Called by c_print() to undo whatever block_pipe() did */ 338 void 339 restore_pipe(int restore_dfl) 340 { 341 if (restore_dfl) 342 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 343 } 344 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. 348 */ 349 int 350 setsig(Trap *p, sig_t f, int flags) 351 { 352 struct sigaction sigact; 353 354 if (p->signal == SIGEXIT_ || p->signal == SIGERR_) 355 return 1; 356 357 /* First time setting this signal? If so, get and note the current 358 * setting. 359 */ 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; 365 } 366 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 370 */ 371 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 372 (!(flags & SS_USER) || !Flag(FTALKING))) 373 return 0; 374 375 setexecsig(p, flags & SS_RESTORE_MASK); 376 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). 380 */ 381 if (!(flags & SS_USER)) 382 p->shtrap = NULL; 383 if (flags & SS_SHTRAP) { 384 p->shtrap = f; 385 f = trapsig; 386 } 387 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); 394 } 395 396 return 1; 397 } 398 399 /* control what signal is set to before an exec() */ 400 void 401 setexecsig(Trap *p, int restore) 402 { 403 /* XXX debugging */ 404 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 405 internal_errorf(1, "setexecsig: unset signal %d(%s)", 406 p->signal, p->name); 407 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; 422 } 423 } 424