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 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.166 2017/08/07 23:25:09 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 histfsize = lseek(histfd, (off_t)0, SEEK_END); 883 hist_init_tail: 884 mksh_unlkfd(histfd); 885 } 886 #endif 887 888 void 889 hist_init(Source *s) 890 { 891 histsave(NULL, NULL, HIST_DISCARD, true); 892 893 if (Flag(FTALKING) == 0) 894 return; 895 896 hstarted = true; 897 hist_source = s; 898 899 #if HAVE_PERSISTENT_HISTORY 900 hist_persist_init(); 901 #endif 902 } 903 904 #if HAVE_PERSISTENT_HISTORY 905 /* 906 * load the history structure from the stored data 907 */ 908 static int 909 histload(Source *s, unsigned char *base, size_t bytes) 910 { 911 int lno = 0, lines = 0; 912 unsigned char *cp; 913 914 histload_loop: 915 /* !bytes check as some systems (older FreeBSDs) have buggy memchr */ 916 if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL) 917 return (lines); 918 /* advance base pointer past COMMAND byte */ 919 bytes -= ++cp - base; 920 base = cp; 921 /* if there is no full string left, don't bother with the rest */ 922 if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL) 923 return (lines); 924 /* load the stored line number */ 925 lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) | 926 ((base[2] & 0xFF) << 8) | (base[3] & 0xFF); 927 /* store away the found line (@base[4]) */ 928 ++lines; 929 if (histptr >= history && lno - 1 != s->line) { 930 /* a replacement? */ 931 char **hp; 932 933 if (lno >= s->line - (histptr - history) && lno <= s->line) { 934 hp = &histptr[lno - s->line]; 935 afree(*hp, APERM); 936 strdupx(*hp, (char *)(base + 4), APERM); 937 } 938 } else { 939 s->line = lno--; 940 histsave(&lno, (char *)(base + 4), HIST_NOTE, false); 941 } 942 /* advance base pointer past NUL */ 943 bytes -= ++cp - base; 944 base = cp; 945 /* repeat until no more */ 946 goto histload_loop; 947 } 948 949 /* 950 * write a command to the end of the history file 951 * 952 * This *MAY* seem easy but it's also necessary to check 953 * that the history file has not changed in size. 954 * If it has - then some other shell has written to it and 955 * we should (re)read those commands to update our history 956 */ 957 static void 958 writehistfile(int lno, const char *cmd) 959 { 960 off_t sizenow; 961 size_t bytes; 962 unsigned char *base, *news; 963 964 mksh_lockfd(histfd); 965 sizenow = lseek(histfd, (off_t)0, SEEK_END); 966 if (sizenow < histfsize) { 967 /* the file has shrunk; trust it just appending the new data */ 968 /* well, for now, anyway since mksh strdups all into memory */ 969 /* we can use a nicer approach some time later */ 970 ; 971 } else if ( 972 /* ignore changes when the file is too large */ 973 sizenow <= MKSH_MAXHISTFSIZE 974 && 975 /* the size has changed, we need to do read updates */ 976 sizenow > histfsize 977 ) { 978 /* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */ 979 bytes = (size_t)(sizenow - histfsize); 980 base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ, 981 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 982 if (base == (unsigned char *)MAP_FAILED) 983 goto bad; 984 news = base + (size_t)histfsize; 985 if (*news == COMMAND) { 986 hist_source->line--; 987 histload(hist_source, news, bytes); 988 hist_source->line++; 989 lno = hist_source->line; 990 } else 991 bytes = 0; 992 munmap(caddr_cast(base), (size_t)sizenow); 993 if (!bytes) 994 goto bad; 995 } 996 if (cmd && !writehistline(histfd, lno, cmd)) { 997 bad: 998 hist_finish(); 999 return; 1000 } 1001 histfsize = lseek(histfd, (off_t)0, SEEK_END); 1002 mksh_unlkfd(histfd); 1003 } 1004 1005 static int 1006 writehistline(int fd, int lno, const char *cmd) 1007 { 1008 ssize_t n; 1009 unsigned char hdr[5]; 1010 1011 hdr[0] = COMMAND; 1012 hdr[1] = (lno >> 24) & 0xFF; 1013 hdr[2] = (lno >> 16) & 0xFF; 1014 hdr[3] = (lno >> 8) & 0xFF; 1015 hdr[4] = lno & 0xFF; 1016 n = strlen(cmd) + 1; 1017 return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n); 1018 } 1019 1020 void 1021 hist_finish(void) 1022 { 1023 if (histfd >= 0) { 1024 mksh_unlkfd(histfd); 1025 (void)close(histfd); 1026 } 1027 histfd = -1; 1028 } 1029 #endif 1030 1031 1032 #if !HAVE_SYS_SIGNAME 1033 static const struct mksh_sigpair { 1034 const char * const name; 1035 int nr; 1036 } mksh_sigpairs[] = { 1037 #include "signames.inc" 1038 { NULL, 0 } 1039 }; 1040 #endif 1041 1042 #if HAVE_SYS_SIGLIST 1043 #if !HAVE_SYS_SIGLIST_DECL 1044 extern const char * const sys_siglist[]; 1045 #endif 1046 #endif 1047 1048 void 1049 inittraps(void) 1050 { 1051 int i; 1052 const char *cs; 1053 #if !HAVE_SYS_SIGNAME 1054 const struct mksh_sigpair *pair; 1055 #endif 1056 1057 trap_exstat = -1; 1058 1059 /* populate sigtraps based on sys_signame and sys_siglist */ 1060 for (i = 1; i < ksh_NSIG; i++) { 1061 sigtraps[i].signal = i; 1062 #if HAVE_SYS_SIGNAME 1063 cs = sys_signame[i]; 1064 #else 1065 pair = mksh_sigpairs; 1066 while ((pair->nr != i) && (pair->name != NULL)) 1067 ++pair; 1068 cs = pair->name; 1069 #endif 1070 if ((cs == NULL) || 1071 (cs[0] == '\0')) 1072 sigtraps[i].name = null; 1073 else { 1074 char *s; 1075 1076 /* this is not optimal, what about SIGSIG1? */ 1077 if (ksh_eq(cs[0], 'S', 's') && 1078 ksh_eq(cs[1], 'I', 'i') && 1079 ksh_eq(cs[2], 'G', 'g') && 1080 cs[3] != '\0') { 1081 /* skip leading "SIG" */ 1082 cs += 3; 1083 } 1084 strdupx(s, cs, APERM); 1085 sigtraps[i].name = s; 1086 while ((*s = ksh_toupper(*s))) 1087 ++s; 1088 /* check for reserved names */ 1089 if (!strcmp(sigtraps[i].name, "EXIT") || 1090 !strcmp(sigtraps[i].name, "ERR")) { 1091 #ifndef MKSH_SMALL 1092 internal_warningf(Tinvname, sigtraps[i].name, 1093 "signal"); 1094 #endif 1095 sigtraps[i].name = null; 1096 } 1097 } 1098 if (sigtraps[i].name == null) 1099 sigtraps[i].name = shf_smprintf(Tf_d, i); 1100 #if HAVE_SYS_SIGLIST 1101 sigtraps[i].mess = sys_siglist[i]; 1102 #elif HAVE_STRSIGNAL 1103 sigtraps[i].mess = strsignal(i); 1104 #else 1105 sigtraps[i].mess = NULL; 1106 #endif 1107 if ((sigtraps[i].mess == NULL) || 1108 (sigtraps[i].mess[0] == '\0')) 1109 sigtraps[i].mess = shf_smprintf(Tf_sd, 1110 "Signal", i); 1111 } 1112 sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT; 1113 sigtraps[ksh_SIGEXIT].name = "EXIT"; 1114 sigtraps[ksh_SIGEXIT].mess = "Exit trap"; 1115 sigtraps[ksh_SIGERR].signal = ksh_SIGERR; 1116 sigtraps[ksh_SIGERR].name = "ERR"; 1117 sigtraps[ksh_SIGERR].mess = "Error handler"; 1118 1119 (void)sigemptyset(&Sigact_ign.sa_mask); 1120 Sigact_ign.sa_flags = 0; /* interruptible */ 1121 Sigact_ign.sa_handler = SIG_IGN; 1122 1123 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1124 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1125 /* SIGTERM is not fatal for interactive */ 1126 sigtraps[SIGTERM].flags |= TF_DFL_INTR; 1127 sigtraps[SIGHUP].flags |= TF_FATAL; 1128 sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 1129 1130 /* these are always caught so we can clean up any temporary files. */ 1131 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 1132 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 1133 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 1134 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 1135 } 1136 1137 static void alarm_catcher(int sig); 1138 1139 void 1140 alarm_init(void) 1141 { 1142 sigtraps[SIGALRM].flags |= TF_SHELL_USES; 1143 setsig(&sigtraps[SIGALRM], alarm_catcher, 1144 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 1145 } 1146 1147 /* ARGSUSED */ 1148 static void 1149 alarm_catcher(int sig MKSH_A_UNUSED) 1150 { 1151 /* this runs inside interrupt context, with errno saved */ 1152 1153 if (ksh_tmout_state == TMOUT_READING) { 1154 int left = alarm(0); 1155 1156 if (left == 0) { 1157 ksh_tmout_state = TMOUT_LEAVING; 1158 intrsig = 1; 1159 } else 1160 alarm(left); 1161 } 1162 } 1163 1164 Trap * 1165 gettrap(const char *cs, bool igncase, bool allsigs) 1166 { 1167 int i; 1168 Trap *p; 1169 char *as; 1170 1171 /* signal number (1..ksh_NSIG) or 0? */ 1172 1173 if (ctype(*cs, C_DIGIT)) 1174 return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ? 1175 (&sigtraps[i]) : NULL); 1176 1177 /* do a lookup by name then */ 1178 1179 /* this breaks SIGSIG1, but we do that above anyway */ 1180 if (ksh_eq(cs[0], 'S', 's') && 1181 ksh_eq(cs[1], 'I', 'i') && 1182 ksh_eq(cs[2], 'G', 'g') && 1183 cs[3] != '\0') { 1184 /* skip leading "SIG" */ 1185 cs += 3; 1186 } 1187 if (igncase) { 1188 char *s; 1189 1190 strdupx(as, cs, ATEMP); 1191 cs = s = as; 1192 while ((*s = ksh_toupper(*s))) 1193 ++s; 1194 } else 1195 as = NULL; 1196 1197 /* this is idiotic, we really want a hashtable here */ 1198 1199 p = sigtraps; 1200 i = ksh_NSIG + 1; 1201 do { 1202 if (!strcmp(p->name, cs)) 1203 goto found; 1204 ++p; 1205 } while (--i); 1206 goto notfound; 1207 1208 found: 1209 if (!allsigs) { 1210 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) { 1211 notfound: 1212 p = NULL; 1213 } 1214 } 1215 afree(as, ATEMP); 1216 return (p); 1217 } 1218 1219 /* 1220 * trap signal handler 1221 */ 1222 void 1223 trapsig(int i) 1224 { 1225 Trap *p = &sigtraps[i]; 1226 int eno = errno; 1227 1228 trap = p->set = 1; 1229 if (p->flags & TF_DFL_INTR) 1230 intrsig = 1; 1231 if ((p->flags & TF_FATAL) && !p->trap) { 1232 fatal_trap = 1; 1233 intrsig = 1; 1234 } 1235 if (p->shtrap) 1236 (*p->shtrap)(i); 1237 errno = eno; 1238 } 1239 1240 /* 1241 * called when we want to allow the user to ^C out of something - won't 1242 * work if user has trapped SIGINT. 1243 */ 1244 void 1245 intrcheck(void) 1246 { 1247 if (intrsig) 1248 runtraps(TF_DFL_INTR|TF_FATAL); 1249 } 1250 1251 /* 1252 * called after EINTR to check if a signal with normally causes process 1253 * termination has been received. 1254 */ 1255 int 1256 fatal_trap_check(void) 1257 { 1258 Trap *p = sigtraps; 1259 int i = ksh_NSIG + 1; 1260 1261 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 1262 do { 1263 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 1264 /* return value is used as an exit code */ 1265 return (ksh_sigmask(p->signal)); 1266 ++p; 1267 } while (--i); 1268 return (0); 1269 } 1270 1271 /* 1272 * Returns the signal number of any pending traps: ie, a signal which has 1273 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 1274 * is set. 1275 */ 1276 int 1277 trap_pending(void) 1278 { 1279 Trap *p = sigtraps; 1280 int i = ksh_NSIG + 1; 1281 1282 do { 1283 if (p->set && ((p->trap && p->trap[0]) || 1284 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 1285 return (p->signal); 1286 ++p; 1287 } while (--i); 1288 return (0); 1289 } 1290 1291 /* 1292 * run any pending traps. If intr is set, only run traps that 1293 * can interrupt commands. 1294 */ 1295 void 1296 runtraps(int flag) 1297 { 1298 Trap *p = sigtraps; 1299 int i = ksh_NSIG + 1; 1300 1301 if (ksh_tmout_state == TMOUT_LEAVING) { 1302 ksh_tmout_state = TMOUT_EXECUTING; 1303 warningf(false, "timed out waiting for input"); 1304 unwind(LEXIT); 1305 } else 1306 /* 1307 * XXX: this means the alarm will have no effect if a trap 1308 * is caught after the alarm() was started...not good. 1309 */ 1310 ksh_tmout_state = TMOUT_EXECUTING; 1311 if (!flag) 1312 trap = 0; 1313 if (flag & TF_DFL_INTR) 1314 intrsig = 0; 1315 if (flag & TF_FATAL) 1316 fatal_trap = 0; 1317 ++trap_nested; 1318 do { 1319 if (p->set && (!flag || 1320 ((p->flags & flag) && p->trap == NULL))) 1321 runtrap(p, false); 1322 ++p; 1323 } while (--i); 1324 if (!--trap_nested) 1325 runtrap(NULL, true); 1326 } 1327 1328 void 1329 runtrap(Trap *p, bool is_last) 1330 { 1331 int old_changed = 0, i; 1332 char *trapstr; 1333 1334 if (p == NULL) 1335 /* just clean up, see runtraps() above */ 1336 goto donetrap; 1337 i = p->signal; 1338 trapstr = p->trap; 1339 p->set = 0; 1340 if (trapstr == NULL) { 1341 /* SIG_DFL */ 1342 if (p->flags & (TF_FATAL | TF_DFL_INTR)) { 1343 exstat = (int)(128U + (unsigned)i); 1344 if ((unsigned)exstat > 255U) 1345 exstat = 255; 1346 } 1347 /* e.g. SIGHUP */ 1348 if (p->flags & TF_FATAL) 1349 unwind(LLEAVE); 1350 /* e.g. SIGINT, SIGQUIT, SIGTERM, etc. */ 1351 if (p->flags & TF_DFL_INTR) 1352 unwind(LINTR); 1353 goto donetrap; 1354 } 1355 if (trapstr[0] == '\0') 1356 /* SIG_IGN */ 1357 goto donetrap; 1358 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1359 /* avoid recursion on these */ 1360 old_changed = p->flags & TF_CHANGED; 1361 p->flags &= ~TF_CHANGED; 1362 p->trap = NULL; 1363 } 1364 if (trap_exstat == -1) 1365 trap_exstat = exstat & 0xFF; 1366 /* 1367 * Note: trapstr is fully parsed before anything is executed, thus 1368 * no problem with afree(p->trap) in settrap() while still in use. 1369 */ 1370 command(trapstr, current_lineno); 1371 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1372 if (p->flags & TF_CHANGED) 1373 /* don't clear TF_CHANGED */ 1374 afree(trapstr, APERM); 1375 else 1376 p->trap = trapstr; 1377 p->flags |= old_changed; 1378 } 1379 1380 donetrap: 1381 /* we're the last trap of a sequence executed */ 1382 if (is_last && trap_exstat != -1) { 1383 exstat = trap_exstat; 1384 trap_exstat = -1; 1385 } 1386 } 1387 1388 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 1389 void 1390 cleartraps(void) 1391 { 1392 Trap *p = sigtraps; 1393 int i = ksh_NSIG + 1; 1394 1395 trap = 0; 1396 intrsig = 0; 1397 fatal_trap = 0; 1398 1399 do { 1400 p->set = 0; 1401 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 1402 settrap(p, NULL); 1403 ++p; 1404 } while (--i); 1405 } 1406 1407 /* restore signals just before an exec(2) */ 1408 void 1409 restoresigs(void) 1410 { 1411 Trap *p = sigtraps; 1412 int i = ksh_NSIG + 1; 1413 1414 do { 1415 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 1416 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 1417 SS_RESTORE_CURR|SS_FORCE); 1418 ++p; 1419 } while (--i); 1420 } 1421 1422 void 1423 settrap(Trap *p, const char *s) 1424 { 1425 sig_t f; 1426 1427 afree(p->trap, APERM); 1428 /* handles s == NULL */ 1429 strdupx(p->trap, s, APERM); 1430 p->flags |= TF_CHANGED; 1431 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 1432 1433 p->flags |= TF_USER_SET; 1434 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 1435 f = trapsig; 1436 else if (p->flags & TF_SHELL_USES) { 1437 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 1438 /* do what user wants at exec time */ 1439 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1440 if (f == SIG_IGN) 1441 p->flags |= TF_EXEC_IGN; 1442 else 1443 p->flags |= TF_EXEC_DFL; 1444 } 1445 1446 /* 1447 * assumes handler already set to what shell wants it 1448 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 1449 */ 1450 return; 1451 } 1452 1453 /* todo: should we let user know signal is ignored? how? */ 1454 setsig(p, f, SS_RESTORE_CURR|SS_USER); 1455 } 1456 1457 /* 1458 * called by c_print() when writing to a co-process to ensure 1459 * SIGPIPE won't kill shell (unless user catches it and exits) 1460 */ 1461 bool 1462 block_pipe(void) 1463 { 1464 bool restore_dfl = false; 1465 Trap *p = &sigtraps[SIGPIPE]; 1466 1467 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1468 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1469 if (p->flags & TF_ORIG_DFL) 1470 restore_dfl = true; 1471 } else if (p->cursig == SIG_DFL) { 1472 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1473 /* restore to SIG_DFL */ 1474 restore_dfl = true; 1475 } 1476 return (restore_dfl); 1477 } 1478 1479 /* called by c_print() to undo whatever block_pipe() did */ 1480 void 1481 restore_pipe(void) 1482 { 1483 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 1484 } 1485 1486 /* 1487 * Set action for a signal. Action may not be set if original 1488 * action was SIG_IGN, depending on the value of flags and FTALKING. 1489 */ 1490 int 1491 setsig(Trap *p, sig_t f, int flags) 1492 { 1493 struct sigaction sigact; 1494 1495 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) 1496 return (1); 1497 1498 memset(&sigact, 0, sizeof(sigact)); 1499 1500 /* 1501 * First time setting this signal? If so, get and note the current 1502 * setting. 1503 */ 1504 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1505 sigaction(p->signal, &Sigact_ign, &sigact); 1506 p->flags |= sigact.sa_handler == SIG_IGN ? 1507 TF_ORIG_IGN : TF_ORIG_DFL; 1508 p->cursig = SIG_IGN; 1509 } 1510 1511 /*- 1512 * Generally, an ignored signal stays ignored, except if 1513 * - the user of an interactive shell wants to change it 1514 * - the shell wants for force a change 1515 */ 1516 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 1517 (!(flags & SS_USER) || !Flag(FTALKING))) 1518 return (0); 1519 1520 setexecsig(p, flags & SS_RESTORE_MASK); 1521 1522 /* 1523 * This is here 'cause there should be a way of clearing 1524 * shtraps, but don't know if this is a sane way of doing 1525 * it. At the moment, all users of shtrap are lifetime 1526 * users (SIGALRM, SIGCHLD, SIGWINCH). 1527 */ 1528 if (!(flags & SS_USER)) 1529 p->shtrap = (sig_t)NULL; 1530 if (flags & SS_SHTRAP) { 1531 p->shtrap = f; 1532 f = trapsig; 1533 } 1534 1535 if (p->cursig != f) { 1536 p->cursig = f; 1537 (void)sigemptyset(&sigact.sa_mask); 1538 /* interruptible */ 1539 sigact.sa_flags = 0; 1540 sigact.sa_handler = f; 1541 sigaction(p->signal, &sigact, NULL); 1542 } 1543 1544 return (1); 1545 } 1546 1547 /* control what signal is set to before an exec() */ 1548 void 1549 setexecsig(Trap *p, int restore) 1550 { 1551 /* XXX debugging */ 1552 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 1553 internal_errorf("setexecsig: unset signal %d(%s)", 1554 p->signal, p->name); 1555 1556 /* restore original value for exec'd kids */ 1557 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1558 switch (restore & SS_RESTORE_MASK) { 1559 case SS_RESTORE_CURR: 1560 /* leave things as they currently are */ 1561 break; 1562 case SS_RESTORE_ORIG: 1563 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 1564 break; 1565 case SS_RESTORE_DFL: 1566 p->flags |= TF_EXEC_DFL; 1567 break; 1568 case SS_RESTORE_IGN: 1569 p->flags |= TF_EXEC_IGN; 1570 break; 1571 } 1572 } 1573 1574 #if HAVE_PERSISTENT_HISTORY || defined(DF) 1575 /* 1576 * File descriptor locking and unlocking functions. 1577 * Could use some error handling, but hey, this is only 1578 * advisory locking anyway, will often not work over NFS, 1579 * and you are SOL if this fails... 1580 */ 1581 1582 void 1583 mksh_lockfd(int fd) 1584 { 1585 #if defined(__OpenBSD__) 1586 /* flock is not interrupted by signals */ 1587 (void)flock(fd, LOCK_EX); 1588 #elif HAVE_FLOCK 1589 int rv; 1590 1591 /* e.g. on Linux */ 1592 do { 1593 rv = flock(fd, LOCK_EX); 1594 } while (rv == 1 && errno == EINTR); 1595 #elif HAVE_LOCK_FCNTL 1596 int rv; 1597 struct flock lks; 1598 1599 memset(&lks, 0, sizeof(lks)); 1600 lks.l_type = F_WRLCK; 1601 do { 1602 rv = fcntl(fd, F_SETLKW, &lks); 1603 } while (rv == 1 && errno == EINTR); 1604 #endif 1605 } 1606 1607 /* designed to not define mksh_unlkfd if none triggered */ 1608 #if HAVE_FLOCK 1609 void 1610 mksh_unlkfd(int fd) 1611 { 1612 (void)flock(fd, LOCK_UN); 1613 } 1614 #elif HAVE_LOCK_FCNTL 1615 void 1616 mksh_unlkfd(int fd) 1617 { 1618 struct flock lks; 1619 1620 memset(&lks, 0, sizeof(lks)); 1621 lks.l_type = F_UNLCK; 1622 (void)fcntl(fd, F_SETLKW, &lks); 1623 } 1624 #endif 1625 #endif 1626