1 /* $OpenBSD: history.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */ 2 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */ 3 4 /*- 5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 6 * 2011, 2012, 2014, 2015, 2016, 2017, 2018 7 * mirabilos <m (at) mirbsd.org> 8 * 9 * Provided that these terms and disclaimer and all copyright notices 10 * are retained or reproduced in an accompanying document, permission 11 * is granted to deal in this work without restriction, including un- 12 * limited rights to use, publicly perform, distribute, sell, modify, 13 * merge, give away, or sublicence. 14 * 15 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 16 * the utmost extent permitted by applicable law, neither express nor 17 * implied; without malicious intent or gross negligence. In no event 18 * may a licensor, author or contributor be held liable for indirect, 19 * direct, other damage, loss, or other issues arising in any way out 20 * of dealing in the work, even if advised of the possibility of such 21 * damage or existence of a defect, except proven that it results out 22 * of said person's immediate fault when using the work as intended. 23 */ 24 25 #include "sh.h" 26 #if HAVE_SYS_FILE_H 27 #include <sys/file.h> 28 #endif 29 30 __RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.167 2018/04/28 17:16:54 tg Exp $"); 31 32 Trap sigtraps[ksh_NSIG + 1]; 33 static struct sigaction Sigact_ign; 34 35 #if HAVE_PERSISTENT_HISTORY 36 static int histload(Source *, unsigned char *, size_t); 37 static int writehistline(int, int, const char *); 38 static void writehistfile(int, const char *); 39 #endif 40 41 static int hist_execute(char *, Area *); 42 static char **hist_get(const char *, bool, bool); 43 static char **hist_get_oldest(void); 44 45 static bool hstarted; /* set after hist_init() called */ 46 static Source *hist_source; 47 48 #if HAVE_PERSISTENT_HISTORY 49 /*XXX imake style */ 50 #if defined(__linux) 51 #define caddr_cast(x) ((void *)(x)) 52 #else 53 #define caddr_cast(x) ((caddr_t)(x)) 54 #endif 55 56 /* several OEs do not have these constants */ 57 #ifndef MAP_FAILED 58 #define MAP_FAILED caddr_cast(-1) 59 #endif 60 61 /* some OEs need the default mapping type specified */ 62 #ifndef MAP_FILE 63 #define MAP_FILE 0 64 #endif 65 66 /* current history file: name, fd, size */ 67 static char *hname; 68 static int histfd = -1; 69 static off_t histfsize; 70 #endif 71 72 /* HISTSIZE default: size of saved history, persistent or standard */ 73 #ifdef MKSH_SMALL 74 #define MKSH_DEFHISTSIZE 255 75 #else 76 #define MKSH_DEFHISTSIZE 2047 77 #endif 78 /* maximum considered size of persistent history file */ 79 #define MKSH_MAXHISTFSIZE ((off_t)1048576 * 96) 80 81 /* hidden option */ 82 #define HIST_DISCARD 5 83 84 int 85 c_fc(const char **wp) 86 { 87 struct shf *shf; 88 struct temp *tf; 89 bool gflag = false, lflag = false, nflag = false, rflag = false, 90 sflag = false; 91 int optc; 92 const char *p, *first = NULL, *last = NULL; 93 char **hfirst, **hlast, **hp, *editor = NULL; 94 95 if (!Flag(FTALKING_I)) { 96 bi_errorf("history %ss not available", Tfunction); 97 return (1); 98 } 99 100 while ((optc = ksh_getopt(wp, &builtin_opt, 101 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1) 102 switch (optc) { 103 104 case 'e': 105 p = builtin_opt.optarg; 106 if (ksh_isdash(p)) 107 sflag = true; 108 else { 109 size_t len = strlen(p); 110 111 /* almost certainly not overflowing */ 112 editor = alloc(len + 4, ATEMP); 113 memcpy(editor, p, len); 114 memcpy(editor + len, Tspdollaru, 4); 115 } 116 break; 117 118 /* non-AT&T ksh */ 119 case 'g': 120 gflag = true; 121 break; 122 123 case 'l': 124 lflag = true; 125 break; 126 127 case 'n': 128 nflag = true; 129 break; 130 131 case 'r': 132 rflag = true; 133 break; 134 135 /* POSIX version of -e - */ 136 case 's': 137 sflag = true; 138 break; 139 140 /* kludge city - accept -num as -- -num (kind of) */ 141 case '0': case '1': case '2': case '3': case '4': 142 case '5': case '6': case '7': case '8': case '9': 143 p = shf_smprintf("-%c%s", 144 optc, builtin_opt.optarg); 145 if (!first) 146 first = p; 147 else if (!last) 148 last = p; 149 else { 150 bi_errorf(Ttoo_many_args); 151 return (1); 152 } 153 break; 154 155 case '?': 156 return (1); 157 } 158 wp += builtin_opt.optind; 159 160 /* Substitute and execute command */ 161 if (sflag) { 162 char *pat = NULL, *rep = NULL, *line; 163 164 if (editor || lflag || nflag || rflag) { 165 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); 166 return (1); 167 } 168 169 /* Check for pattern replacement argument */ 170 if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) { 171 strdupx(pat, *wp, ATEMP); 172 rep = pat + (p - *wp); 173 *rep++ = '\0'; 174 wp++; 175 } 176 /* Check for search prefix */ 177 if (!first && (first = *wp)) 178 wp++; 179 if (last || *wp) { 180 bi_errorf(Ttoo_many_args); 181 return (1); 182 } 183 184 hp = first ? hist_get(first, false, false) : 185 hist_get_newest(false); 186 if (!hp) 187 return (1); 188 /* hist_replace */ 189 if (!pat) 190 strdupx(line, *hp, ATEMP); 191 else { 192 char *s, *s1; 193 size_t len, pat_len, rep_len; 194 XString xs; 195 char *xp; 196 bool any_subst = false; 197 198 pat_len = strlen(pat); 199 rep_len = strlen(rep); 200 Xinit(xs, xp, 128, ATEMP); 201 for (s = *hp; (s1 = strstr(s, pat)) && 202 (!any_subst || gflag); s = s1 + pat_len) { 203 any_subst = true; 204 len = s1 - s; 205 XcheckN(xs, xp, len + rep_len); 206 /*; first part */ 207 memcpy(xp, s, len); 208 xp += len; 209 /* replacement */ 210 memcpy(xp, rep, rep_len); 211 xp += rep_len; 212 } 213 if (!any_subst) { 214 bi_errorf(Tbadsubst); 215 return (1); 216 } 217 len = strlen(s) + 1; 218 XcheckN(xs, xp, len); 219 memcpy(xp, s, len); 220 xp += len; 221 line = Xclose(xs, xp); 222 } 223 return (hist_execute(line, ATEMP)); 224 } 225 226 if (editor && (lflag || nflag)) { 227 bi_errorf("can't use -l, -n with -e"); 228 return (1); 229 } 230 231 if (!first && (first = *wp)) 232 wp++; 233 if (!last && (last = *wp)) 234 wp++; 235 if (*wp) { 236 bi_errorf(Ttoo_many_args); 237 return (1); 238 } 239 if (!first) { 240 hfirst = lflag ? hist_get("-16", true, true) : 241 hist_get_newest(false); 242 if (!hfirst) 243 return (1); 244 /* can't fail if hfirst didn't fail */ 245 hlast = hist_get_newest(false); 246 } else { 247 /* 248 * POSIX says not an error if first/last out of bounds 249 * when range is specified; AT&T ksh and pdksh allow out 250 * of bounds for -l as well. 251 */ 252 hfirst = hist_get(first, tobool(lflag || last), lflag); 253 if (!hfirst) 254 return (1); 255 hlast = last ? hist_get(last, true, lflag) : 256 (lflag ? hist_get_newest(false) : hfirst); 257 if (!hlast) 258 return (1); 259 } 260 if (hfirst > hlast) { 261 char **temp; 262 263 temp = hfirst; hfirst = hlast; hlast = temp; 264 /* POSIX */ 265 rflag = !rflag; 266 } 267 268 /* List history */ 269 if (lflag) { 270 char *s, *t; 271 272 for (hp = rflag ? hlast : hfirst; 273 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) { 274 if (!nflag) 275 shf_fprintf(shl_stdout, Tf_lu, 276 (unsigned long)hist_source->line - 277 (unsigned long)(histptr - hp)); 278 shf_putc('\t', shl_stdout); 279 /* print multi-line commands correctly */ 280 s = *hp; 281 while ((t = strchr(s, '\n'))) { 282 *t = '\0'; 283 shf_fprintf(shl_stdout, "%s\n\t", s); 284 *t++ = '\n'; 285 s = t; 286 } 287 shf_fprintf(shl_stdout, Tf_sN, s); 288 } 289 shf_flush(shl_stdout); 290 return (0); 291 } 292 293 /* Run editor on selected lines, then run resulting commands */ 294 295 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps); 296 if (!(shf = tf->shf)) { 297 bi_errorf(Tf_temp, Tcreate, tf->tffn, cstrerror(errno)); 298 return (1); 299 } 300 for (hp = rflag ? hlast : hfirst; 301 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 302 shf_fprintf(shf, Tf_sN, *hp); 303 if (shf_close(shf) == -1) { 304 bi_errorf(Tf_temp, Twrite, tf->tffn, cstrerror(errno)); 305 return (1); 306 } 307 308 /* Ignore setstr errors here (arbitrary) */ 309 setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR); 310 311 if ((optc = command(editor ? editor : TFCEDIT_dollaru, 0))) 312 return (optc); 313 314 { 315 struct stat statb; 316 XString xs; 317 char *xp; 318 ssize_t n; 319 320 if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) { 321 bi_errorf(Tf_temp, Topen, tf->tffn, cstrerror(errno)); 322 return (1); 323 } 324 325 if (stat(tf->tffn, &statb) < 0) 326 n = 128; 327 else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) { 328 bi_errorf(Tf_toolarge, Thistory, 329 Tfile, (unsigned long)statb.st_size); 330 goto errout; 331 } else 332 n = (size_t)statb.st_size + 1; 333 Xinit(xs, xp, n, hist_source->areap); 334 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) { 335 xp += n; 336 if (Xnleft(xs, xp) <= 0) 337 XcheckN(xs, xp, Xlength(xs, xp)); 338 } 339 if (n < 0) { 340 bi_errorf(Tf_temp, Tread, tf->tffn, 341 cstrerror(shf_errno(shf))); 342 errout: 343 shf_close(shf); 344 return (1); 345 } 346 shf_close(shf); 347 *xp = '\0'; 348 strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); 349 return (hist_execute(Xstring(xs, xp), hist_source->areap)); 350 } 351 } 352 353 /* save cmd in history, execute cmd (cmd gets afreed) */ 354 static int 355 hist_execute(char *cmd, Area *areap) 356 { 357 static int last_line = -1; 358 359 /* Back up over last histsave */ 360 if (histptr >= history && last_line != hist_source->line) { 361 hist_source->line--; 362 afree(*histptr, APERM); 363 histptr--; 364 last_line = hist_source->line; 365 } 366 367 histsave(&hist_source->line, cmd, HIST_STORE, true); 368 /* now *histptr == cmd without all trailing newlines */ 369 afree(cmd, areap); 370 cmd = *histptr; 371 /* pdksh says POSIX doesnt say this is done, testsuite needs it */ 372 shellf(Tf_sN, cmd); 373 374 /*- 375 * Commands are executed here instead of pushing them onto the 376 * input 'cause POSIX says the redirection and variable assignments 377 * in 378 * X=y fc -e - 42 2> /dev/null 379 * are to effect the repeated commands environment. 380 */ 381 return (command(cmd, 0)); 382 } 383 384 /* 385 * get pointer to history given pattern 386 * pattern is a number or string 387 */ 388 static char ** 389 hist_get(const char *str, bool approx, bool allow_cur) 390 { 391 char **hp = NULL; 392 int n; 393 394 if (getn(str, &n)) { 395 hp = histptr + (n < 0 ? n : (n - hist_source->line)); 396 if ((size_t)hp < (size_t)history) { 397 if (approx) 398 hp = hist_get_oldest(); 399 else { 400 bi_errorf(Tf_sD_s, str, Tnot_in_history); 401 hp = NULL; 402 } 403 } else if ((size_t)hp > (size_t)histptr) { 404 if (approx) 405 hp = hist_get_newest(allow_cur); 406 else { 407 bi_errorf(Tf_sD_s, str, Tnot_in_history); 408 hp = NULL; 409 } 410 } else if (!allow_cur && hp == histptr) { 411 bi_errorf(Tf_sD_s, str, "invalid range"); 412 hp = NULL; 413 } 414 } else { 415 bool anchored = *str == '?' ? (++str, false) : true; 416 417 /* the -1 is to avoid the current fc command */ 418 if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0) 419 bi_errorf(Tf_sD_s, str, Tnot_in_history); 420 else 421 hp = &history[n]; 422 } 423 return (hp); 424 } 425 426 /* Return a pointer to the newest command in the history */ 427 char ** 428 hist_get_newest(bool allow_cur) 429 { 430 if (histptr < history || (!allow_cur && histptr == history)) { 431 bi_errorf("no history (yet)"); 432 return (NULL); 433 } 434 return (allow_cur ? histptr : histptr - 1); 435 } 436 437 /* Return a pointer to the oldest command in the history */ 438 static char ** 439 hist_get_oldest(void) 440 { 441 if (histptr <= history) { 442 bi_errorf("no history (yet)"); 443 return (NULL); 444 } 445 return (history); 446 } 447 448 #if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI 449 /* current position in history[] */ 450 static char **current; 451 452 /* 453 * Return the current position. 454 */ 455 char ** 456 histpos(void) 457 { 458 return (current); 459 } 460 461 int 462 histnum(int n) 463 { 464 int last = histptr - history; 465 466 if (n < 0 || n >= last) { 467 current = histptr; 468 return (last); 469 } else { 470 current = &history[n]; 471 return (n); 472 } 473 } 474 #endif 475 476 /* 477 * This will become unnecessary if hist_get is modified to allow 478 * searching from positions other than the end, and in either 479 * direction. 480 */ 481 int 482 findhist(int start, int fwd, const char *str, bool anchored) 483 { 484 char **hp; 485 int maxhist = histptr - history; 486 int incr = fwd ? 1 : -1; 487 size_t len = strlen(str); 488 489 if (start < 0 || start >= maxhist) 490 start = maxhist; 491 492 hp = &history[start]; 493 for (; hp >= history && hp <= histptr; hp += incr) 494 if ((anchored && strncmp(*hp, str, len) == 0) || 495 (!anchored && strstr(*hp, str))) 496 return (hp - history); 497 498 return (-1); 499 } 500 501 /* 502 * set history; this means reallocating the dataspace 503 */ 504 void 505 sethistsize(mksh_ari_t n) 506 { 507 if (n > 0 && n != histsize) { 508 int cursize = histptr - history; 509 510 /* save most recent history */ 511 if (n < cursize) { 512 memmove(history, histptr - n + 1, n * sizeof(char *)); 513 cursize = n - 1; 514 } 515 516 history = aresize2(history, n, sizeof(char *), APERM); 517 518 histsize = n; 519 histptr = history + cursize; 520 } 521 } 522 523 #if HAVE_PERSISTENT_HISTORY 524 /* 525 * set history file; this can mean reloading/resetting/starting 526 * history file maintenance 527 */ 528 void 529 sethistfile(const char *name) 530 { 531 /* if not started then nothing to do */ 532 if (hstarted == false) 533 return; 534 535 /* if the name is the same as the name we have */ 536 if (hname && name && !strcmp(hname, name)) 537 return; 538 539 /* 540 * it's a new name - possibly 541 */ 542 if (histfd != -1) { 543 /* yes the file is open */ 544 (void)close(histfd); 545 histfd = -1; 546 histfsize = 0; 547 afree(hname, APERM); 548 hname = NULL; 549 /* let's reset the history */ 550 histsave(NULL, NULL, HIST_DISCARD, true); 551 histptr = history - 1; 552 hist_source->line = 0; 553 } 554 555 if (name) 556 hist_init(hist_source); 557 } 558 #endif 559 560 /* 561 * initialise the history vector 562 */ 563 void 564 init_histvec(void) 565 { 566 if (history == (char **)NULL) { 567 histsize = MKSH_DEFHISTSIZE; 568 history = alloc2(histsize, sizeof(char *), APERM); 569 histptr = history - 1; 570 } 571 } 572 573 574 /* 575 * It turns out that there is a lot of ghastly hackery here 576 */ 577 578 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY 579 /* do not save command in history but possibly sync */ 580 bool 581 histsync(void) 582 { 583 bool changed = false; 584 585 /* called by histsave(), may not HIST_DISCARD, caller should flush */ 586 587 if (histfd != -1) { 588 int lno = hist_source->line; 589 590 hist_source->line++; 591 writehistfile(0, NULL); 592 hist_source->line--; 593 594 if (lno != hist_source->line) 595 changed = true; 596 } 597 598 return (changed); 599 } 600 #endif 601 602 /* 603 * save command in history 604 */ 605 void 606 histsave(int *lnp, const char *cmd, int svmode, bool ignoredups) 607 { 608 static char *enqueued = NULL; 609 char **hp, *c; 610 const char *ccp; 611 612 if (svmode == HIST_DISCARD) { 613 afree(enqueued, APERM); 614 enqueued = NULL; 615 return; 616 } 617 618 if (svmode == HIST_APPEND) { 619 if (!enqueued) 620 svmode = HIST_STORE; 621 } else if (enqueued) { 622 c = enqueued; 623 enqueued = NULL; 624 --*lnp; 625 histsave(lnp, c, HIST_STORE, true); 626 afree(c, APERM); 627 } 628 629 if (svmode == HIST_FLUSH) 630 return; 631 632 ccp = strnul(cmd); 633 while (ccp > cmd && ccp[-1] == '\n') 634 --ccp; 635 strndupx(c, cmd, ccp - cmd, APERM); 636 637 if (svmode != HIST_APPEND) { 638 if (ignoredups && 639 histptr >= history && 640 !strcmp(c, *histptr) 641 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY 642 && !histsync() 643 #endif 644 ) { 645 afree(c, APERM); 646 return; 647 } 648 ++*lnp; 649 } 650 651 #if HAVE_PERSISTENT_HISTORY 652 if (svmode == HIST_STORE && histfd != -1) 653 writehistfile(*lnp, c); 654 #endif 655 656 if (svmode == HIST_QUEUE || svmode == HIST_APPEND) { 657 size_t nenq, ncmd; 658 659 if (!enqueued) { 660 if (*c) 661 enqueued = c; 662 else 663 afree(c, APERM); 664 return; 665 } 666 667 nenq = strlen(enqueued); 668 ncmd = strlen(c); 669 enqueued = aresize(enqueued, nenq + 1 + ncmd + 1, APERM); 670 enqueued[nenq] = '\n'; 671 memcpy(enqueued + nenq + 1, c, ncmd + 1); 672 afree(c, APERM); 673 return; 674 } 675 676 hp = histptr; 677 678 if (++hp >= history + histsize) { 679 /* remove oldest command */ 680 afree(*history, APERM); 681 for (hp = history; hp < history + histsize - 1; hp++) 682 hp[0] = hp[1]; 683 } 684 *hp = c; 685 histptr = hp; 686 } 687 688 /* 689 * Write history data to a file nominated by HISTFILE; 690 * if HISTFILE is unset then history still happens, but 691 * the data is not written to a file. All copies of ksh 692 * looking at the file will maintain the same history. 693 * This is ksh behaviour. 694 * 695 * This stuff uses mmap() 696 * 697 * This stuff is so totally broken it must eventually be 698 * redesigned, without mmap, better checks, support for 699 * larger files, etc. and handle partially corrupted files 700 */ 701 702 /*- 703 * Open a history file 704 * Format is: 705 * Bytes 1, 2: 706 * HMAGIC - just to check that we are dealing with the correct object 707 * Then follows a number of stored commands 708 * Each command is 709 * <command byte><command number(4 octets, big endian)><bytes><NUL> 710 */ 711 #define HMAGIC1 0xAB 712 #define HMAGIC2 0xCD 713 #define COMMAND 0xFF 714 715 #if HAVE_PERSISTENT_HISTORY 716 static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 }; 717 718 static int 719 hist_persist_back(int srcfd) 720 { 721 off_t tot, mis; 722 ssize_t n, w; 723 char *buf, *cp; 724 int rv = 0; 725 #define MKSH_HS_BUFSIZ 4096 726 727 if ((tot = lseek(srcfd, (off_t)0, SEEK_END)) < 0 || 728 lseek(srcfd, (off_t)0, SEEK_SET) < 0 || 729 lseek(histfd, (off_t)0, SEEK_SET) < 0) 730 return (1); 731 732 if ((buf = malloc_osfunc(MKSH_HS_BUFSIZ)) == NULL) 733 return (1); 734 735 mis = tot; 736 while (mis > 0) { 737 if ((n = blocking_read(srcfd, (cp = buf), 738 MKSH_HS_BUFSIZ)) == -1) { 739 if (errno == EINTR) { 740 intrcheck(); 741 continue; 742 } 743 goto copy_error; 744 } 745 mis -= n; 746 while (n) { 747 if (intrsig) 748 goto has_intrsig; 749 if ((w = write(histfd, cp, n)) != -1) { 750 n -= w; 751 cp += w; 752 continue; 753 } 754 if (errno == EINTR) { 755 has_intrsig: 756 intrcheck(); 757 continue; 758 } 759 goto copy_error; 760 } 761 } 762 if (ftruncate(histfd, tot)) { 763 copy_error: 764 rv = 1; 765 } 766 free_osfunc(buf); 767 return (rv); 768 } 769 770 static void 771 hist_persist_init(void) 772 { 773 unsigned char *base; 774 int lines, fd; 775 enum { hist_init_first, hist_init_retry, hist_use_it } hs; 776 777 if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) { 778 hname = NULL; 779 return; 780 } 781 strdupx(hname, hname, APERM); 782 hs = hist_init_first; 783 784 retry: 785 /* we have a file and are interactive */ 786 if ((fd = binopen3(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0) 787 return; 788 if ((histfd = savefd(fd)) < 0) 789 return; 790 if (histfd != fd) 791 close(fd); 792 793 mksh_lockfd(histfd); 794 795 histfsize = lseek(histfd, (off_t)0, SEEK_END); 796 if (histfsize > MKSH_MAXHISTFSIZE) { 797 /* we ignore too large files but still append to them */ 798 goto hist_init_tail; 799 } else if (histfsize > 2) { 800 /* we have some data, check its validity */ 801 base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ, 802 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 803 if (base == (unsigned char *)MAP_FAILED) 804 goto hist_init_fail; 805 if (base[0] != HMAGIC1 || base[1] != HMAGIC2) { 806 munmap(caddr_cast(base), (size_t)histfsize); 807 goto hist_init_fail; 808 } 809 /* load _all_ data */ 810 lines = histload(hist_source, base + 2, (size_t)histfsize - 2); 811 munmap(caddr_cast(base), (size_t)histfsize); 812 /* check if the file needs to be truncated */ 813 if (lines > histsize && histptr >= history) { 814 /* you're fucked up with the current code, trust me */ 815 char *nhname, **hp; 816 struct stat sb; 817 818 /* create temporary file */ 819 nhname = shf_smprintf("%s.%d", hname, (int)procpid); 820 if ((fd = binopen3(nhname, O_RDWR | O_CREAT | O_TRUNC | 821 O_EXCL, 0600)) < 0) { 822 /* just don't truncate then, meh. */ 823 hs = hist_use_it; 824 goto hist_trunc_dont; 825 } 826 if (fstat(histfd, &sb) >= 0 && 827 chown(nhname, sb.st_uid, sb.st_gid)) { 828 /* abort the truncation then, meh. */ 829 goto hist_trunc_abort; 830 } 831 /* we definitively want some magic in that file */ 832 if (write(fd, sprinkle, 2) != 2) 833 goto hist_trunc_abort; 834 /* and of course the entries */ 835 hp = history; 836 while (hp < histptr) { 837 if (!writehistline(fd, 838 hist_source->line - (histptr - hp), *hp)) 839 goto hist_trunc_abort; 840 ++hp; 841 } 842 /* now transfer back */ 843 if (!hist_persist_back(fd)) { 844 /* success! */ 845 hs = hist_use_it; 846 } 847 hist_trunc_abort: 848 /* remove temporary file */ 849 close(fd); 850 fd = -1; 851 unlink(nhname); 852 /* use whatever is in the file now */ 853 hist_trunc_dont: 854 afree(nhname, ATEMP); 855 if (hs == hist_use_it) 856 goto hist_trunc_done; 857 goto hist_init_fail; 858 } 859 } else if (histfsize != 0) { 860 /* negative or too small... */ 861 hist_init_fail: 862 /* ... or mmap failed or illegal */ 863 hist_finish(); 864 /* nuke the bogus file then retry, at most once */ 865 if (!unlink(hname) && hs != hist_init_retry) { 866 hs = hist_init_retry; 867 goto retry; 868 } 869 if (hs != hist_init_retry) 870 bi_errorf(Tf_cant_ss_s, 871 "unlink HISTFILE", hname, cstrerror(errno)); 872 histfsize = 0; 873 return; 874 } else { 875 /* size 0, add magic to the history file */ 876 if (write(histfd, sprinkle, 2) != 2) { 877 hist_finish(); 878 return; 879 } 880 } 881 hist_trunc_done: 882 if ((histfsize = lseek(histfd, (off_t)0, SEEK_END)) < 0) 883 goto hist_init_fail; 884 hist_init_tail: 885 mksh_unlkfd(histfd); 886 } 887 #endif 888 889 void 890 hist_init(Source *s) 891 { 892 histsave(NULL, NULL, HIST_DISCARD, true); 893 894 if (Flag(FTALKING) == 0) 895 return; 896 897 hstarted = true; 898 hist_source = s; 899 900 #if HAVE_PERSISTENT_HISTORY 901 hist_persist_init(); 902 #endif 903 } 904 905 #if HAVE_PERSISTENT_HISTORY 906 /* 907 * load the history structure from the stored data 908 */ 909 static int 910 histload(Source *s, unsigned char *base, size_t bytes) 911 { 912 int lno = 0, lines = 0; 913 unsigned char *cp; 914 915 histload_loop: 916 /* !bytes check as some systems (older FreeBSDs) have buggy memchr */ 917 if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL) 918 return (lines); 919 /* advance base pointer past COMMAND byte */ 920 bytes -= ++cp - base; 921 base = cp; 922 /* if there is no full string left, don't bother with the rest */ 923 if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL) 924 return (lines); 925 /* load the stored line number */ 926 lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) | 927 ((base[2] & 0xFF) << 8) | (base[3] & 0xFF); 928 /* store away the found line (@base[4]) */ 929 ++lines; 930 if (histptr >= history && lno - 1 != s->line) { 931 /* a replacement? */ 932 char **hp; 933 934 if (lno >= s->line - (histptr - history) && lno <= s->line) { 935 hp = &histptr[lno - s->line]; 936 afree(*hp, APERM); 937 strdupx(*hp, (char *)(base + 4), APERM); 938 } 939 } else { 940 s->line = lno--; 941 histsave(&lno, (char *)(base + 4), HIST_NOTE, false); 942 } 943 /* advance base pointer past NUL */ 944 bytes -= ++cp - base; 945 base = cp; 946 /* repeat until no more */ 947 goto histload_loop; 948 } 949 950 /* 951 * write a command to the end of the history file 952 * 953 * This *MAY* seem easy but it's also necessary to check 954 * that the history file has not changed in size. 955 * If it has - then some other shell has written to it and 956 * we should (re)read those commands to update our history 957 */ 958 static void 959 writehistfile(int lno, const char *cmd) 960 { 961 off_t sizenow; 962 size_t bytes; 963 unsigned char *base, *news; 964 965 mksh_lockfd(histfd); 966 if ((sizenow = lseek(histfd, (off_t)0, SEEK_END)) < 0) 967 goto bad; 968 else if (sizenow < histfsize) { 969 /* the file has shrunk; trust it just appending the new data */ 970 /* well, for now, anyway since mksh strdups all into memory */ 971 /* we can use a nicer approach some time later */ 972 ; 973 } else if ( 974 /* ignore changes when the file is too large */ 975 sizenow <= MKSH_MAXHISTFSIZE 976 && 977 /* the size has changed, we need to do read updates */ 978 sizenow > histfsize 979 ) { 980 /* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */ 981 bytes = (size_t)(sizenow - histfsize); 982 base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ, 983 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 984 if (base == (unsigned char *)MAP_FAILED) 985 goto bad; 986 news = base + (size_t)histfsize; 987 if (*news == COMMAND) { 988 hist_source->line--; 989 histload(hist_source, news, bytes); 990 hist_source->line++; 991 lno = hist_source->line; 992 } else 993 bytes = 0; 994 munmap(caddr_cast(base), (size_t)sizenow); 995 if (!bytes) 996 goto bad; 997 } 998 if (cmd && !writehistline(histfd, lno, cmd)) { 999 bad: 1000 hist_finish(); 1001 return; 1002 } 1003 if ((histfsize = lseek(histfd, (off_t)0, SEEK_END)) < 0) 1004 goto bad; 1005 mksh_unlkfd(histfd); 1006 } 1007 1008 static int 1009 writehistline(int fd, int lno, const char *cmd) 1010 { 1011 ssize_t n; 1012 unsigned char hdr[5]; 1013 1014 hdr[0] = COMMAND; 1015 hdr[1] = (lno >> 24) & 0xFF; 1016 hdr[2] = (lno >> 16) & 0xFF; 1017 hdr[3] = (lno >> 8) & 0xFF; 1018 hdr[4] = lno & 0xFF; 1019 n = strlen(cmd) + 1; 1020 return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n); 1021 } 1022 1023 void 1024 hist_finish(void) 1025 { 1026 if (histfd >= 0) { 1027 mksh_unlkfd(histfd); 1028 (void)close(histfd); 1029 } 1030 histfd = -1; 1031 } 1032 #endif 1033 1034 1035 #if !HAVE_SYS_SIGNAME 1036 static const struct mksh_sigpair { 1037 const char * const name; 1038 int nr; 1039 } mksh_sigpairs[] = { 1040 #include "signames.inc" 1041 { NULL, 0 } 1042 }; 1043 #endif 1044 1045 #if HAVE_SYS_SIGLIST 1046 #if !HAVE_SYS_SIGLIST_DECL 1047 extern const char * const sys_siglist[]; 1048 #endif 1049 #endif 1050 1051 void 1052 inittraps(void) 1053 { 1054 int i; 1055 const char *cs; 1056 #if !HAVE_SYS_SIGNAME 1057 const struct mksh_sigpair *pair; 1058 #endif 1059 1060 trap_exstat = -1; 1061 1062 /* populate sigtraps based on sys_signame and sys_siglist */ 1063 for (i = 1; i < ksh_NSIG; i++) { 1064 sigtraps[i].signal = i; 1065 #if HAVE_SYS_SIGNAME 1066 cs = sys_signame[i]; 1067 #else 1068 pair = mksh_sigpairs; 1069 while ((pair->nr != i) && (pair->name != NULL)) 1070 ++pair; 1071 cs = pair->name; 1072 #endif 1073 if ((cs == NULL) || 1074 (cs[0] == '\0')) 1075 sigtraps[i].name = null; 1076 else { 1077 char *s; 1078 1079 /* this is not optimal, what about SIGSIG1? */ 1080 if (ksh_eq(cs[0], 'S', 's') && 1081 ksh_eq(cs[1], 'I', 'i') && 1082 ksh_eq(cs[2], 'G', 'g') && 1083 cs[3] != '\0') { 1084 /* skip leading "SIG" */ 1085 cs += 3; 1086 } 1087 strdupx(s, cs, APERM); 1088 sigtraps[i].name = s; 1089 while ((*s = ksh_toupper(*s))) 1090 ++s; 1091 /* check for reserved names */ 1092 if (!strcmp(sigtraps[i].name, "EXIT") || 1093 !strcmp(sigtraps[i].name, "ERR")) { 1094 #ifndef MKSH_SMALL 1095 internal_warningf(Tinvname, sigtraps[i].name, 1096 "signal"); 1097 #endif 1098 sigtraps[i].name = null; 1099 } 1100 } 1101 if (sigtraps[i].name == null) 1102 sigtraps[i].name = shf_smprintf(Tf_d, i); 1103 #if HAVE_SYS_SIGLIST 1104 sigtraps[i].mess = sys_siglist[i]; 1105 #elif HAVE_STRSIGNAL 1106 sigtraps[i].mess = strsignal(i); 1107 #else 1108 sigtraps[i].mess = NULL; 1109 #endif 1110 if ((sigtraps[i].mess == NULL) || 1111 (sigtraps[i].mess[0] == '\0')) 1112 sigtraps[i].mess = shf_smprintf(Tf_sd, 1113 "Signal", i); 1114 } 1115 sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT; 1116 sigtraps[ksh_SIGEXIT].name = "EXIT"; 1117 sigtraps[ksh_SIGEXIT].mess = "Exit trap"; 1118 sigtraps[ksh_SIGERR].signal = ksh_SIGERR; 1119 sigtraps[ksh_SIGERR].name = "ERR"; 1120 sigtraps[ksh_SIGERR].mess = "Error handler"; 1121 1122 (void)sigemptyset(&Sigact_ign.sa_mask); 1123 Sigact_ign.sa_flags = 0; /* interruptible */ 1124 Sigact_ign.sa_handler = SIG_IGN; 1125 1126 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1127 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1128 /* SIGTERM is not fatal for interactive */ 1129 sigtraps[SIGTERM].flags |= TF_DFL_INTR; 1130 sigtraps[SIGHUP].flags |= TF_FATAL; 1131 sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 1132 1133 /* these are always caught so we can clean up any temporary files. */ 1134 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 1135 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 1136 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 1137 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 1138 } 1139 1140 static void alarm_catcher(int sig); 1141 1142 void 1143 alarm_init(void) 1144 { 1145 sigtraps[SIGALRM].flags |= TF_SHELL_USES; 1146 setsig(&sigtraps[SIGALRM], alarm_catcher, 1147 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 1148 } 1149 1150 /* ARGSUSED */ 1151 static void 1152 alarm_catcher(int sig MKSH_A_UNUSED) 1153 { 1154 /* this runs inside interrupt context, with errno saved */ 1155 1156 if (ksh_tmout_state == TMOUT_READING) { 1157 int left = alarm(0); 1158 1159 if (left == 0) { 1160 ksh_tmout_state = TMOUT_LEAVING; 1161 intrsig = 1; 1162 } else 1163 alarm(left); 1164 } 1165 } 1166 1167 Trap * 1168 gettrap(const char *cs, bool igncase, bool allsigs) 1169 { 1170 int i; 1171 Trap *p; 1172 char *as; 1173 1174 /* signal number (1..ksh_NSIG) or 0? */ 1175 1176 if (ctype(*cs, C_DIGIT)) 1177 return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ? 1178 (&sigtraps[i]) : NULL); 1179 1180 /* do a lookup by name then */ 1181 1182 /* this breaks SIGSIG1, but we do that above anyway */ 1183 if (ksh_eq(cs[0], 'S', 's') && 1184 ksh_eq(cs[1], 'I', 'i') && 1185 ksh_eq(cs[2], 'G', 'g') && 1186 cs[3] != '\0') { 1187 /* skip leading "SIG" */ 1188 cs += 3; 1189 } 1190 if (igncase) { 1191 char *s; 1192 1193 strdupx(as, cs, ATEMP); 1194 cs = s = as; 1195 while ((*s = ksh_toupper(*s))) 1196 ++s; 1197 } else 1198 as = NULL; 1199 1200 /* this is idiotic, we really want a hashtable here */ 1201 1202 p = sigtraps; 1203 i = ksh_NSIG + 1; 1204 do { 1205 if (!strcmp(p->name, cs)) 1206 goto found; 1207 ++p; 1208 } while (--i); 1209 goto notfound; 1210 1211 found: 1212 if (!allsigs) { 1213 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) { 1214 notfound: 1215 p = NULL; 1216 } 1217 } 1218 afree(as, ATEMP); 1219 return (p); 1220 } 1221 1222 /* 1223 * trap signal handler 1224 */ 1225 void 1226 trapsig(int i) 1227 { 1228 Trap *p = &sigtraps[i]; 1229 int eno = errno; 1230 1231 trap = p->set = 1; 1232 if (p->flags & TF_DFL_INTR) 1233 intrsig = 1; 1234 if ((p->flags & TF_FATAL) && !p->trap) { 1235 fatal_trap = 1; 1236 intrsig = 1; 1237 } 1238 if (p->shtrap) 1239 (*p->shtrap)(i); 1240 errno = eno; 1241 } 1242 1243 /* 1244 * called when we want to allow the user to ^C out of something - won't 1245 * work if user has trapped SIGINT. 1246 */ 1247 void 1248 intrcheck(void) 1249 { 1250 if (intrsig) 1251 runtraps(TF_DFL_INTR|TF_FATAL); 1252 } 1253 1254 /* 1255 * called after EINTR to check if a signal with normally causes process 1256 * termination has been received. 1257 */ 1258 int 1259 fatal_trap_check(void) 1260 { 1261 Trap *p = sigtraps; 1262 int i = ksh_NSIG + 1; 1263 1264 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 1265 do { 1266 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 1267 /* return value is used as an exit code */ 1268 return (ksh_sigmask(p->signal)); 1269 ++p; 1270 } while (--i); 1271 return (0); 1272 } 1273 1274 /* 1275 * Returns the signal number of any pending traps: ie, a signal which has 1276 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 1277 * is set. 1278 */ 1279 int 1280 trap_pending(void) 1281 { 1282 Trap *p = sigtraps; 1283 int i = ksh_NSIG + 1; 1284 1285 do { 1286 if (p->set && ((p->trap && p->trap[0]) || 1287 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 1288 return (p->signal); 1289 ++p; 1290 } while (--i); 1291 return (0); 1292 } 1293 1294 /* 1295 * run any pending traps. If intr is set, only run traps that 1296 * can interrupt commands. 1297 */ 1298 void 1299 runtraps(int flag) 1300 { 1301 Trap *p = sigtraps; 1302 int i = ksh_NSIG + 1; 1303 1304 if (ksh_tmout_state == TMOUT_LEAVING) { 1305 ksh_tmout_state = TMOUT_EXECUTING; 1306 warningf(false, "timed out waiting for input"); 1307 unwind(LEXIT); 1308 } else 1309 /* 1310 * XXX: this means the alarm will have no effect if a trap 1311 * is caught after the alarm() was started...not good. 1312 */ 1313 ksh_tmout_state = TMOUT_EXECUTING; 1314 if (!flag) 1315 trap = 0; 1316 if (flag & TF_DFL_INTR) 1317 intrsig = 0; 1318 if (flag & TF_FATAL) 1319 fatal_trap = 0; 1320 ++trap_nested; 1321 do { 1322 if (p->set && (!flag || 1323 ((p->flags & flag) && p->trap == NULL))) 1324 runtrap(p, false); 1325 ++p; 1326 } while (--i); 1327 if (!--trap_nested) 1328 runtrap(NULL, true); 1329 } 1330 1331 void 1332 runtrap(Trap *p, bool is_last) 1333 { 1334 int old_changed = 0, i; 1335 char *trapstr; 1336 1337 if (p == NULL) 1338 /* just clean up, see runtraps() above */ 1339 goto donetrap; 1340 i = p->signal; 1341 trapstr = p->trap; 1342 p->set = 0; 1343 if (trapstr == NULL) { 1344 /* SIG_DFL */ 1345 if (p->flags & (TF_FATAL | TF_DFL_INTR)) { 1346 exstat = (int)(128U + (unsigned)i); 1347 if ((unsigned)exstat > 255U) 1348 exstat = 255; 1349 } 1350 /* e.g. SIGHUP */ 1351 if (p->flags & TF_FATAL) 1352 unwind(LLEAVE); 1353 /* e.g. SIGINT, SIGQUIT, SIGTERM, etc. */ 1354 if (p->flags & TF_DFL_INTR) 1355 unwind(LINTR); 1356 goto donetrap; 1357 } 1358 if (trapstr[0] == '\0') 1359 /* SIG_IGN */ 1360 goto donetrap; 1361 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1362 /* avoid recursion on these */ 1363 old_changed = p->flags & TF_CHANGED; 1364 p->flags &= ~TF_CHANGED; 1365 p->trap = NULL; 1366 } 1367 if (trap_exstat == -1) 1368 trap_exstat = exstat & 0xFF; 1369 /* 1370 * Note: trapstr is fully parsed before anything is executed, thus 1371 * no problem with afree(p->trap) in settrap() while still in use. 1372 */ 1373 command(trapstr, current_lineno); 1374 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1375 if (p->flags & TF_CHANGED) 1376 /* don't clear TF_CHANGED */ 1377 afree(trapstr, APERM); 1378 else 1379 p->trap = trapstr; 1380 p->flags |= old_changed; 1381 } 1382 1383 donetrap: 1384 /* we're the last trap of a sequence executed */ 1385 if (is_last && trap_exstat != -1) { 1386 exstat = trap_exstat; 1387 trap_exstat = -1; 1388 } 1389 } 1390 1391 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 1392 void 1393 cleartraps(void) 1394 { 1395 Trap *p = sigtraps; 1396 int i = ksh_NSIG + 1; 1397 1398 trap = 0; 1399 intrsig = 0; 1400 fatal_trap = 0; 1401 1402 do { 1403 p->set = 0; 1404 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 1405 settrap(p, NULL); 1406 ++p; 1407 } while (--i); 1408 } 1409 1410 /* restore signals just before an exec(2) */ 1411 void 1412 restoresigs(void) 1413 { 1414 Trap *p = sigtraps; 1415 int i = ksh_NSIG + 1; 1416 1417 do { 1418 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 1419 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 1420 SS_RESTORE_CURR|SS_FORCE); 1421 ++p; 1422 } while (--i); 1423 } 1424 1425 void 1426 settrap(Trap *p, const char *s) 1427 { 1428 sig_t f; 1429 1430 afree(p->trap, APERM); 1431 /* handles s == NULL */ 1432 strdupx(p->trap, s, APERM); 1433 p->flags |= TF_CHANGED; 1434 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 1435 1436 p->flags |= TF_USER_SET; 1437 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 1438 f = trapsig; 1439 else if (p->flags & TF_SHELL_USES) { 1440 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 1441 /* do what user wants at exec time */ 1442 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1443 if (f == SIG_IGN) 1444 p->flags |= TF_EXEC_IGN; 1445 else 1446 p->flags |= TF_EXEC_DFL; 1447 } 1448 1449 /* 1450 * assumes handler already set to what shell wants it 1451 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 1452 */ 1453 return; 1454 } 1455 1456 /* todo: should we let user know signal is ignored? how? */ 1457 setsig(p, f, SS_RESTORE_CURR|SS_USER); 1458 } 1459 1460 /* 1461 * called by c_print() when writing to a co-process to ensure 1462 * SIGPIPE won't kill shell (unless user catches it and exits) 1463 */ 1464 bool 1465 block_pipe(void) 1466 { 1467 bool restore_dfl = false; 1468 Trap *p = &sigtraps[SIGPIPE]; 1469 1470 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1471 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1472 if (p->flags & TF_ORIG_DFL) 1473 restore_dfl = true; 1474 } else if (p->cursig == SIG_DFL) { 1475 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1476 /* restore to SIG_DFL */ 1477 restore_dfl = true; 1478 } 1479 return (restore_dfl); 1480 } 1481 1482 /* called by c_print() to undo whatever block_pipe() did */ 1483 void 1484 restore_pipe(void) 1485 { 1486 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 1487 } 1488 1489 /* 1490 * Set action for a signal. Action may not be set if original 1491 * action was SIG_IGN, depending on the value of flags and FTALKING. 1492 */ 1493 int 1494 setsig(Trap *p, sig_t f, int flags) 1495 { 1496 struct sigaction sigact; 1497 1498 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) 1499 return (1); 1500 1501 memset(&sigact, 0, sizeof(sigact)); 1502 1503 /* 1504 * First time setting this signal? If so, get and note the current 1505 * setting. 1506 */ 1507 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1508 sigaction(p->signal, &Sigact_ign, &sigact); 1509 p->flags |= sigact.sa_handler == SIG_IGN ? 1510 TF_ORIG_IGN : TF_ORIG_DFL; 1511 p->cursig = SIG_IGN; 1512 } 1513 1514 /*- 1515 * Generally, an ignored signal stays ignored, except if 1516 * - the user of an interactive shell wants to change it 1517 * - the shell wants for force a change 1518 */ 1519 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 1520 (!(flags & SS_USER) || !Flag(FTALKING))) 1521 return (0); 1522 1523 setexecsig(p, flags & SS_RESTORE_MASK); 1524 1525 /* 1526 * This is here 'cause there should be a way of clearing 1527 * shtraps, but don't know if this is a sane way of doing 1528 * it. At the moment, all users of shtrap are lifetime 1529 * users (SIGALRM, SIGCHLD, SIGWINCH). 1530 */ 1531 if (!(flags & SS_USER)) 1532 p->shtrap = (sig_t)NULL; 1533 if (flags & SS_SHTRAP) { 1534 p->shtrap = f; 1535 f = trapsig; 1536 } 1537 1538 if (p->cursig != f) { 1539 p->cursig = f; 1540 (void)sigemptyset(&sigact.sa_mask); 1541 /* interruptible */ 1542 sigact.sa_flags = 0; 1543 sigact.sa_handler = f; 1544 sigaction(p->signal, &sigact, NULL); 1545 } 1546 1547 return (1); 1548 } 1549 1550 /* control what signal is set to before an exec() */ 1551 void 1552 setexecsig(Trap *p, int restore) 1553 { 1554 /* XXX debugging */ 1555 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 1556 internal_errorf("setexecsig: unset signal %d(%s)", 1557 p->signal, p->name); 1558 1559 /* restore original value for exec'd kids */ 1560 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1561 switch (restore & SS_RESTORE_MASK) { 1562 case SS_RESTORE_CURR: 1563 /* leave things as they currently are */ 1564 break; 1565 case SS_RESTORE_ORIG: 1566 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 1567 break; 1568 case SS_RESTORE_DFL: 1569 p->flags |= TF_EXEC_DFL; 1570 break; 1571 case SS_RESTORE_IGN: 1572 p->flags |= TF_EXEC_IGN; 1573 break; 1574 } 1575 } 1576 1577 #if HAVE_PERSISTENT_HISTORY || defined(DF) 1578 /* 1579 * File descriptor locking and unlocking functions. 1580 * Could use some error handling, but hey, this is only 1581 * advisory locking anyway, will often not work over NFS, 1582 * and you are SOL if this fails... 1583 */ 1584 1585 void 1586 mksh_lockfd(int fd) 1587 { 1588 #if defined(__OpenBSD__) 1589 /* flock is not interrupted by signals */ 1590 (void)flock(fd, LOCK_EX); 1591 #elif HAVE_FLOCK 1592 int rv; 1593 1594 /* e.g. on Linux */ 1595 do { 1596 rv = flock(fd, LOCK_EX); 1597 } while (rv == 1 && errno == EINTR); 1598 #elif HAVE_LOCK_FCNTL 1599 int rv; 1600 struct flock lks; 1601 1602 memset(&lks, 0, sizeof(lks)); 1603 lks.l_type = F_WRLCK; 1604 do { 1605 rv = fcntl(fd, F_SETLKW, &lks); 1606 } while (rv == 1 && errno == EINTR); 1607 #endif 1608 } 1609 1610 /* designed to not define mksh_unlkfd if none triggered */ 1611 #if HAVE_FLOCK 1612 void 1613 mksh_unlkfd(int fd) 1614 { 1615 (void)flock(fd, LOCK_UN); 1616 } 1617 #elif HAVE_LOCK_FCNTL 1618 void 1619 mksh_unlkfd(int fd) 1620 { 1621 struct flock lks; 1622 1623 memset(&lks, 0, sizeof(lks)); 1624 lks.l_type = F_UNLCK; 1625 (void)fcntl(fd, F_SETLKW, &lks); 1626 } 1627 #endif 1628 #endif 1629