1 /* $OpenBSD: trap.c,v 1.33 2018/12/08 21:03:51 jca 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 96 if (igncase && strncasecmp(name, "SIG", 3) == 0) 97 name += 3; 98 if (!igncase && strncmp(name, "SIG", 3) == 0) 99 name += 3; 100 101 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 102 if (p->name) { 103 if (igncase && strcasecmp(p->name, name) == 0) 104 return p; 105 if (!igncase && strcmp(p->name, name) == 0) 106 return p; 107 } 108 return NULL; 109 } 110 111 /* 112 * trap signal handler 113 */ 114 void 115 trapsig(int i) 116 { 117 Trap *p = &sigtraps[i]; 118 int errno_ = errno; 119 120 trap = p->set = 1; 121 if (p->flags & TF_DFL_INTR) 122 intrsig = 1; 123 if ((p->flags & TF_FATAL) && !p->trap) { 124 fatal_trap = 1; 125 intrsig = 1; 126 } 127 if (p->shtrap) 128 (*p->shtrap)(i); 129 errno = errno_; 130 } 131 132 /* called when we want to allow the user to ^C out of something - won't 133 * work if user has trapped SIGINT. 134 */ 135 void 136 intrcheck(void) 137 { 138 if (intrsig) 139 runtraps(TF_DFL_INTR|TF_FATAL); 140 } 141 142 /* called after EINTR to check if a signal with normally causes process 143 * termination has been received. 144 */ 145 int 146 fatal_trap_check(void) 147 { 148 int i; 149 Trap *p; 150 151 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 152 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 153 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 154 /* return value is used as an exit code */ 155 return 128 + p->signal; 156 return 0; 157 } 158 159 /* Returns the signal number of any pending traps: ie, a signal which has 160 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 161 * is set. 162 */ 163 int 164 trap_pending(void) 165 { 166 int i; 167 Trap *p; 168 169 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 170 if (p->set && ((p->trap && p->trap[0]) || 171 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 172 return p->signal; 173 return 0; 174 } 175 176 /* 177 * run any pending traps. If intr is set, only run traps that 178 * can interrupt commands. 179 */ 180 void 181 runtraps(int flag) 182 { 183 int i; 184 Trap *p; 185 186 if (ksh_tmout_state == TMOUT_LEAVING) { 187 ksh_tmout_state = TMOUT_EXECUTING; 188 warningf(false, "timed out waiting for input"); 189 unwind(LEXIT); 190 } else 191 /* XXX: this means the alarm will have no effect if a trap 192 * is caught after the alarm() was started...not good. 193 */ 194 ksh_tmout_state = TMOUT_EXECUTING; 195 if (!flag) 196 trap = 0; 197 if (flag & TF_DFL_INTR) 198 intrsig = 0; 199 if (flag & TF_FATAL) 200 fatal_trap = 0; 201 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 202 if (p->set && (!flag || 203 ((p->flags & flag) && p->trap == NULL))) 204 runtrap(p); 205 } 206 207 void 208 runtrap(Trap *p) 209 { 210 int i = p->signal; 211 char *trapstr = p->trap; 212 int oexstat; 213 int old_changed = 0; 214 215 p->set = 0; 216 if (trapstr == NULL) { /* SIG_DFL */ 217 if (p->flags & TF_FATAL) { 218 /* eg, SIGHUP */ 219 exstat = 128 + i; 220 unwind(LLEAVE); 221 } 222 if (p->flags & TF_DFL_INTR) { 223 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ 224 exstat = 128 + i; 225 unwind(LINTR); 226 } 227 return; 228 } 229 if (trapstr[0] == '\0') /* SIG_IGN */ 230 return; 231 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */ 232 old_changed = p->flags & TF_CHANGED; 233 p->flags &= ~TF_CHANGED; 234 p->trap = NULL; 235 } 236 oexstat = exstat; 237 /* Note: trapstr is fully parsed before anything is executed, thus 238 * no problem with afree(p->trap) in settrap() while still in use. 239 */ 240 command(trapstr, current_lineno); 241 exstat = oexstat; 242 if (i == SIGEXIT_ || i == SIGERR_) { 243 if (p->flags & TF_CHANGED) 244 /* don't clear TF_CHANGED */ 245 afree(trapstr, APERM); 246 else 247 p->trap = trapstr; 248 p->flags |= old_changed; 249 } 250 } 251 252 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 253 void 254 cleartraps(void) 255 { 256 int i; 257 Trap *p; 258 259 trap = 0; 260 intrsig = 0; 261 fatal_trap = 0; 262 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) { 263 p->set = 0; 264 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 265 settrap(p, NULL); 266 } 267 } 268 269 /* restore signals just before an exec(2) */ 270 void 271 restoresigs(void) 272 { 273 int i; 274 Trap *p; 275 276 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) 277 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 278 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 279 SS_RESTORE_CURR|SS_FORCE); 280 } 281 282 void 283 settrap(Trap *p, char *s) 284 { 285 sig_t f; 286 287 afree(p->trap, APERM); 288 p->trap = str_save(s, APERM); /* handles s == 0 */ 289 p->flags |= TF_CHANGED; 290 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 291 292 p->flags |= TF_USER_SET; 293 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 294 f = trapsig; 295 else if (p->flags & TF_SHELL_USES) { 296 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 297 /* do what user wants at exec time */ 298 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 299 if (f == SIG_IGN) 300 p->flags |= TF_EXEC_IGN; 301 else 302 p->flags |= TF_EXEC_DFL; 303 } 304 305 /* assumes handler already set to what shell wants it 306 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 307 */ 308 return; 309 } 310 311 /* todo: should we let user know signal is ignored? how? */ 312 setsig(p, f, SS_RESTORE_CURR|SS_USER); 313 } 314 315 /* Called by c_print() when writing to a co-process to ensure SIGPIPE won't 316 * kill shell (unless user catches it and exits) 317 */ 318 int 319 block_pipe(void) 320 { 321 int restore_dfl = 0; 322 Trap *p = &sigtraps[SIGPIPE]; 323 324 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 325 setsig(p, SIG_IGN, SS_RESTORE_CURR); 326 if (p->flags & TF_ORIG_DFL) 327 restore_dfl = 1; 328 } else if (p->cursig == SIG_DFL) { 329 setsig(p, SIG_IGN, SS_RESTORE_CURR); 330 restore_dfl = 1; /* restore to SIG_DFL */ 331 } 332 return restore_dfl; 333 } 334 335 /* Called by c_print() to undo whatever block_pipe() did */ 336 void 337 restore_pipe(int restore_dfl) 338 { 339 if (restore_dfl) 340 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 341 } 342 343 /* Set action for a signal. Action may not be set if original 344 * action was SIG_IGN, depending on the value of flags and 345 * FTALKING. 346 */ 347 int 348 setsig(Trap *p, sig_t f, int flags) 349 { 350 struct sigaction sigact; 351 352 if (p->signal == SIGEXIT_ || p->signal == SIGERR_) 353 return 1; 354 355 /* First time setting this signal? If so, get and note the current 356 * setting. 357 */ 358 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 359 sigaction(p->signal, &Sigact_ign, &sigact); 360 p->flags |= sigact.sa_handler == SIG_IGN ? 361 TF_ORIG_IGN : TF_ORIG_DFL; 362 p->cursig = SIG_IGN; 363 } 364 365 /* Generally, an ignored signal stays ignored, except if 366 * - the user of an interactive shell wants to change it 367 * - the shell wants for force a change 368 */ 369 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 370 (!(flags & SS_USER) || !Flag(FTALKING))) 371 return 0; 372 373 setexecsig(p, flags & SS_RESTORE_MASK); 374 375 /* This is here 'cause there should be a way of clearing shtraps, but 376 * don't know if this is a sane way of doing it. At the moment, 377 * all users of shtrap are lifetime users (SIGCHLD, SIGALRM, SIGWINCH). 378 */ 379 if (!(flags & SS_USER)) 380 p->shtrap = NULL; 381 if (flags & SS_SHTRAP) { 382 p->shtrap = f; 383 f = trapsig; 384 } 385 386 if (p->cursig != f) { 387 p->cursig = f; 388 sigemptyset(&sigact.sa_mask); 389 sigact.sa_flags = 0 /* interruptible */; 390 sigact.sa_handler = f; 391 sigaction(p->signal, &sigact, NULL); 392 } 393 394 return 1; 395 } 396 397 /* control what signal is set to before an exec() */ 398 void 399 setexecsig(Trap *p, int restore) 400 { 401 /* XXX debugging */ 402 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 403 internal_errorf("%s: unset signal %d(%s)", 404 __func__, p->signal, p->name); 405 406 /* restore original value for exec'd kids */ 407 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 408 switch (restore & SS_RESTORE_MASK) { 409 case SS_RESTORE_CURR: /* leave things as they currently are */ 410 break; 411 case SS_RESTORE_ORIG: 412 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 413 break; 414 case SS_RESTORE_DFL: 415 p->flags |= TF_EXEC_DFL; 416 break; 417 case SS_RESTORE_IGN: 418 p->flags |= TF_EXEC_IGN; 419 break; 420 } 421 } 422