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