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.159 2016/11/11 18:44:32 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 = cmd + strlen(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 #endif 718 719 void 720 hist_init(Source *s) 721 { 722 #if HAVE_PERSISTENT_HISTORY 723 unsigned char *base; 724 int lines, fd; 725 enum { hist_init_first, hist_init_retry, hist_init_restore } hs; 726 #endif 727 728 histsave(NULL, NULL, HIST_DISCARD, true); 729 730 if (Flag(FTALKING) == 0) 731 return; 732 733 hstarted = true; 734 hist_source = s; 735 736 #if HAVE_PERSISTENT_HISTORY 737 if (((hname = str_val(global("HISTFILE"))) == NULL) || !*hname) { 738 hname = NULL; 739 return; 740 } 741 strdupx(hname, hname, APERM); 742 hs = hist_init_first; 743 744 retry: 745 /* we have a file and are interactive */ 746 if ((fd = binopen3(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0) 747 return; 748 749 histfd = savefd(fd); 750 if (histfd != fd) 751 close(fd); 752 753 mksh_lockfd(histfd); 754 755 histfsize = lseek(histfd, (off_t)0, SEEK_END); 756 if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) { 757 /* we ignore too large files but still append to them */ 758 /* we also don't need to re-read after truncation */ 759 goto hist_init_tail; 760 } else if (histfsize > 2) { 761 /* we have some data, check its validity */ 762 base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ, 763 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 764 if (base == (unsigned char *)MAP_FAILED) 765 goto hist_init_fail; 766 if (base[0] != HMAGIC1 || base[1] != HMAGIC2) { 767 munmap(caddr_cast(base), (size_t)histfsize); 768 goto hist_init_fail; 769 } 770 /* load _all_ data */ 771 lines = histload(hist_source, base + 2, (size_t)histfsize - 2); 772 munmap(caddr_cast(base), (size_t)histfsize); 773 /* check if the file needs to be truncated */ 774 if (lines > histsize && histptr >= history) { 775 /* you're fucked up with the current code, trust me */ 776 char *nhname, **hp; 777 struct stat sb; 778 779 /* create temporary file */ 780 nhname = shf_smprintf("%s.%d", hname, (int)procpid); 781 if ((fd = binopen3(nhname, O_RDWR | O_CREAT | O_TRUNC | 782 O_EXCL, 0600)) < 0) { 783 /* just don't truncate then, meh. */ 784 goto hist_trunc_dont; 785 } 786 if (fstat(histfd, &sb) >= 0 && 787 chown(nhname, sb.st_uid, sb.st_gid)) { 788 /* abort the truncation then, meh. */ 789 goto hist_trunc_abort; 790 } 791 /* we definitively want some magic in that file */ 792 if (write(fd, sprinkle, 2) != 2) 793 goto hist_trunc_abort; 794 /* and of course the entries */ 795 hp = history; 796 while (hp < histptr) { 797 if (!writehistline(fd, 798 s->line - (histptr - hp), *hp)) 799 goto hist_trunc_abort; 800 ++hp; 801 } 802 /* now unlock, close both, rename, rinse, repeat */ 803 close(fd); 804 fd = -1; 805 hist_finish(); 806 if (rename(nhname, hname) < 0) { 807 hist_trunc_abort: 808 if (fd != -1) 809 close(fd); 810 unlink(nhname); 811 if (fd != -1) 812 goto hist_trunc_dont; 813 /* darn! restore histfd and pray */ 814 } 815 hs = hist_init_restore; 816 hist_trunc_dont: 817 afree(nhname, ATEMP); 818 if (hs == hist_init_restore) 819 goto retry; 820 } 821 } else if (histfsize != 0) { 822 /* negative or too small... */ 823 hist_init_fail: 824 /* ... or mmap failed or illegal */ 825 hist_finish(); 826 /* nuke the bogus file then retry, at most once */ 827 if (!unlink(hname) && hs != hist_init_retry) { 828 hs = hist_init_retry; 829 goto retry; 830 } 831 if (hs != hist_init_retry) 832 bi_errorf(Tf_cant, 833 "unlink HISTFILE", hname, cstrerror(errno)); 834 histfsize = 0; 835 return; 836 } else { 837 /* size 0, add magic to the history file */ 838 if (write(histfd, sprinkle, 2) != 2) { 839 hist_finish(); 840 return; 841 } 842 } 843 histfsize = lseek(histfd, (off_t)0, SEEK_END); 844 hist_init_tail: 845 mksh_unlkfd(histfd); 846 #endif 847 } 848 849 #if HAVE_PERSISTENT_HISTORY 850 /* 851 * load the history structure from the stored data 852 */ 853 static int 854 histload(Source *s, unsigned char *base, size_t bytes) 855 { 856 int lno = 0, lines = 0; 857 unsigned char *cp; 858 859 histload_loop: 860 /* !bytes check as some systems (older FreeBSDs) have buggy memchr */ 861 if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL) 862 return (lines); 863 /* advance base pointer past COMMAND byte */ 864 bytes -= ++cp - base; 865 base = cp; 866 /* if there is no full string left, don't bother with the rest */ 867 if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL) 868 return (lines); 869 /* load the stored line number */ 870 lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) | 871 ((base[2] & 0xFF) << 8) | (base[3] & 0xFF); 872 /* store away the found line (@base[4]) */ 873 ++lines; 874 if (histptr >= history && lno - 1 != s->line) { 875 /* a replacement? */ 876 char **hp; 877 878 if (lno >= s->line - (histptr - history) && lno <= s->line) { 879 hp = &histptr[lno - s->line]; 880 afree(*hp, APERM); 881 strdupx(*hp, (char *)(base + 4), APERM); 882 } 883 } else { 884 s->line = lno--; 885 histsave(&lno, (char *)(base + 4), HIST_NOTE, false); 886 } 887 /* advance base pointer past NUL */ 888 bytes -= ++cp - base; 889 base = cp; 890 /* repeat until no more */ 891 goto histload_loop; 892 } 893 894 /* 895 * write a command to the end of the history file 896 * 897 * This *MAY* seem easy but it's also necessary to check 898 * that the history file has not changed in size. 899 * If it has - then some other shell has written to it and 900 * we should (re)read those commands to update our history 901 */ 902 static void 903 writehistfile(int lno, const char *cmd) 904 { 905 off_t sizenow; 906 size_t bytes; 907 unsigned char *base, *news; 908 909 mksh_lockfd(histfd); 910 sizenow = lseek(histfd, (off_t)0, SEEK_END); 911 if (sizenow < histfsize) { 912 /* the file has shrunk; give up */ 913 goto bad; 914 } 915 if ( 916 /* ignore changes when the file is too large */ 917 sizenow <= MKSH_MAXHISTFSIZE 918 && 919 /* the size has changed, we need to do read updates */ 920 sizenow > histfsize 921 ) { 922 /* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */ 923 bytes = (size_t)(sizenow - histfsize); 924 base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ, 925 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 926 if (base == (unsigned char *)MAP_FAILED) 927 goto bad; 928 news = base + (size_t)histfsize; 929 if (*news == COMMAND) { 930 hist_source->line--; 931 histload(hist_source, news, bytes); 932 hist_source->line++; 933 lno = hist_source->line; 934 } else 935 bytes = 0; 936 munmap(caddr_cast(base), (size_t)sizenow); 937 if (!bytes) 938 goto bad; 939 } 940 if (cmd && !writehistline(histfd, lno, cmd)) { 941 bad: 942 hist_finish(); 943 return; 944 } 945 histfsize = lseek(histfd, (off_t)0, SEEK_END); 946 mksh_unlkfd(histfd); 947 } 948 949 static int 950 writehistline(int fd, int lno, const char *cmd) 951 { 952 ssize_t n; 953 unsigned char hdr[5]; 954 955 hdr[0] = COMMAND; 956 hdr[1] = (lno >> 24) & 0xFF; 957 hdr[2] = (lno >> 16) & 0xFF; 958 hdr[3] = (lno >> 8) & 0xFF; 959 hdr[4] = lno & 0xFF; 960 n = strlen(cmd) + 1; 961 return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n); 962 } 963 964 void 965 hist_finish(void) 966 { 967 if (histfd >= 0) { 968 mksh_unlkfd(histfd); 969 (void)close(histfd); 970 } 971 histfd = -1; 972 } 973 #endif 974 975 976 #if !HAVE_SYS_SIGNAME 977 static const struct mksh_sigpair { 978 const char * const name; 979 int nr; 980 } mksh_sigpairs[] = { 981 #include "signames.inc" 982 { NULL, 0 } 983 }; 984 #endif 985 986 #if HAVE_SYS_SIGLIST 987 #if !HAVE_SYS_SIGLIST_DECL 988 extern const char * const sys_siglist[]; 989 #endif 990 #endif 991 992 void 993 inittraps(void) 994 { 995 int i; 996 const char *cs; 997 #if !HAVE_SYS_SIGNAME 998 const struct mksh_sigpair *pair; 999 #endif 1000 1001 trap_exstat = -1; 1002 1003 /* populate sigtraps based on sys_signame and sys_siglist */ 1004 for (i = 1; i < ksh_NSIG; i++) { 1005 sigtraps[i].signal = i; 1006 #if HAVE_SYS_SIGNAME 1007 cs = sys_signame[i]; 1008 #else 1009 pair = mksh_sigpairs; 1010 while ((pair->nr != i) && (pair->name != NULL)) 1011 ++pair; 1012 cs = pair->name; 1013 #endif 1014 if ((cs == NULL) || 1015 (cs[0] == '\0')) 1016 sigtraps[i].name = null; 1017 else { 1018 char *s; 1019 1020 /* this is not optimal, what about SIGSIG1? */ 1021 if (ksh_eq(cs[0], 'S', 's') && 1022 ksh_eq(cs[1], 'I', 'i') && 1023 ksh_eq(cs[2], 'G', 'g') && 1024 cs[3] != '\0') { 1025 /* skip leading "SIG" */ 1026 cs += 3; 1027 } 1028 strdupx(s, cs, APERM); 1029 sigtraps[i].name = s; 1030 while ((*s = ksh_toupper(*s))) 1031 ++s; 1032 /* check for reserved names */ 1033 if (!strcmp(sigtraps[i].name, "EXIT") || 1034 !strcmp(sigtraps[i].name, "ERR")) { 1035 #ifndef MKSH_SMALL 1036 internal_warningf("ignoring invalid signal name %s", 1037 sigtraps[i].name); 1038 #endif 1039 sigtraps[i].name = null; 1040 } 1041 } 1042 if (sigtraps[i].name == null) 1043 sigtraps[i].name = shf_smprintf(Tf_d, i); 1044 #if HAVE_SYS_SIGLIST 1045 sigtraps[i].mess = sys_siglist[i]; 1046 #elif HAVE_STRSIGNAL 1047 sigtraps[i].mess = strsignal(i); 1048 #else 1049 sigtraps[i].mess = NULL; 1050 #endif 1051 if ((sigtraps[i].mess == NULL) || 1052 (sigtraps[i].mess[0] == '\0')) 1053 sigtraps[i].mess = shf_smprintf(Tf_sd, 1054 "Signal", i); 1055 } 1056 sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT; 1057 sigtraps[ksh_SIGEXIT].name = "EXIT"; 1058 sigtraps[ksh_SIGEXIT].mess = "Exit trap"; 1059 sigtraps[ksh_SIGERR].signal = ksh_SIGERR; 1060 sigtraps[ksh_SIGERR].name = "ERR"; 1061 sigtraps[ksh_SIGERR].mess = "Error handler"; 1062 1063 (void)sigemptyset(&Sigact_ign.sa_mask); 1064 Sigact_ign.sa_flags = 0; /* interruptible */ 1065 Sigact_ign.sa_handler = SIG_IGN; 1066 1067 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1068 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1069 /* SIGTERM is not fatal for interactive */ 1070 sigtraps[SIGTERM].flags |= TF_DFL_INTR; 1071 sigtraps[SIGHUP].flags |= TF_FATAL; 1072 sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 1073 1074 /* these are always caught so we can clean up any temporary files. */ 1075 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 1076 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 1077 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 1078 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 1079 } 1080 1081 static void alarm_catcher(int sig); 1082 1083 void 1084 alarm_init(void) 1085 { 1086 sigtraps[SIGALRM].flags |= TF_SHELL_USES; 1087 setsig(&sigtraps[SIGALRM], alarm_catcher, 1088 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 1089 } 1090 1091 /* ARGSUSED */ 1092 static void 1093 alarm_catcher(int sig MKSH_A_UNUSED) 1094 { 1095 /* this runs inside interrupt context, with errno saved */ 1096 1097 if (ksh_tmout_state == TMOUT_READING) { 1098 int left = alarm(0); 1099 1100 if (left == 0) { 1101 ksh_tmout_state = TMOUT_LEAVING; 1102 intrsig = 1; 1103 } else 1104 alarm(left); 1105 } 1106 } 1107 1108 Trap * 1109 gettrap(const char *cs, bool igncase, bool allsigs) 1110 { 1111 int i; 1112 Trap *p; 1113 char *as; 1114 1115 /* signal number (1..ksh_NSIG) or 0? */ 1116 1117 if (ksh_isdigit(*cs)) 1118 return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ? 1119 (&sigtraps[i]) : NULL); 1120 1121 /* do a lookup by name then */ 1122 1123 /* this breaks SIGSIG1, but we do that above anyway */ 1124 if (ksh_eq(cs[0], 'S', 's') && 1125 ksh_eq(cs[1], 'I', 'i') && 1126 ksh_eq(cs[2], 'G', 'g') && 1127 cs[3] != '\0') { 1128 /* skip leading "SIG" */ 1129 cs += 3; 1130 } 1131 if (igncase) { 1132 char *s; 1133 1134 strdupx(as, cs, ATEMP); 1135 cs = s = as; 1136 while ((*s = ksh_toupper(*s))) 1137 ++s; 1138 } else 1139 as = NULL; 1140 1141 /* this is idiotic, we really want a hashtable here */ 1142 1143 p = sigtraps; 1144 i = ksh_NSIG + 1; 1145 do { 1146 if (!strcmp(p->name, cs)) 1147 goto found; 1148 ++p; 1149 } while (--i); 1150 goto notfound; 1151 1152 found: 1153 if (!allsigs) { 1154 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) { 1155 notfound: 1156 p = NULL; 1157 } 1158 } 1159 afree(as, ATEMP); 1160 return (p); 1161 } 1162 1163 /* 1164 * trap signal handler 1165 */ 1166 void 1167 trapsig(int i) 1168 { 1169 Trap *p = &sigtraps[i]; 1170 int eno = errno; 1171 1172 trap = p->set = 1; 1173 if (p->flags & TF_DFL_INTR) 1174 intrsig = 1; 1175 if ((p->flags & TF_FATAL) && !p->trap) { 1176 fatal_trap = 1; 1177 intrsig = 1; 1178 } 1179 if (p->shtrap) 1180 (*p->shtrap)(i); 1181 errno = eno; 1182 } 1183 1184 /* 1185 * called when we want to allow the user to ^C out of something - won't 1186 * work if user has trapped SIGINT. 1187 */ 1188 void 1189 intrcheck(void) 1190 { 1191 if (intrsig) 1192 runtraps(TF_DFL_INTR|TF_FATAL); 1193 } 1194 1195 /* 1196 * called after EINTR to check if a signal with normally causes process 1197 * termination has been received. 1198 */ 1199 int 1200 fatal_trap_check(void) 1201 { 1202 Trap *p = sigtraps; 1203 int i = ksh_NSIG + 1; 1204 1205 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 1206 do { 1207 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 1208 /* return value is used as an exit code */ 1209 return (ksh_sigmask(p->signal)); 1210 ++p; 1211 } while (--i); 1212 return (0); 1213 } 1214 1215 /* 1216 * Returns the signal number of any pending traps: ie, a signal which has 1217 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 1218 * is set. 1219 */ 1220 int 1221 trap_pending(void) 1222 { 1223 Trap *p = sigtraps; 1224 int i = ksh_NSIG + 1; 1225 1226 do { 1227 if (p->set && ((p->trap && p->trap[0]) || 1228 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 1229 return (p->signal); 1230 ++p; 1231 } while (--i); 1232 return (0); 1233 } 1234 1235 /* 1236 * run any pending traps. If intr is set, only run traps that 1237 * can interrupt commands. 1238 */ 1239 void 1240 runtraps(int flag) 1241 { 1242 Trap *p = sigtraps; 1243 int i = ksh_NSIG + 1; 1244 1245 if (ksh_tmout_state == TMOUT_LEAVING) { 1246 ksh_tmout_state = TMOUT_EXECUTING; 1247 warningf(false, "timed out waiting for input"); 1248 unwind(LEXIT); 1249 } else 1250 /* 1251 * XXX: this means the alarm will have no effect if a trap 1252 * is caught after the alarm() was started...not good. 1253 */ 1254 ksh_tmout_state = TMOUT_EXECUTING; 1255 if (!flag) 1256 trap = 0; 1257 if (flag & TF_DFL_INTR) 1258 intrsig = 0; 1259 if (flag & TF_FATAL) 1260 fatal_trap = 0; 1261 ++trap_nested; 1262 do { 1263 if (p->set && (!flag || 1264 ((p->flags & flag) && p->trap == NULL))) 1265 runtrap(p, false); 1266 ++p; 1267 } while (--i); 1268 if (!--trap_nested) 1269 runtrap(NULL, true); 1270 } 1271 1272 void 1273 runtrap(Trap *p, bool is_last) 1274 { 1275 int old_changed = 0, i; 1276 char *trapstr; 1277 1278 if (p == NULL) 1279 /* just clean up, see runtraps() above */ 1280 goto donetrap; 1281 i = p->signal; 1282 trapstr = p->trap; 1283 p->set = 0; 1284 if (trapstr == NULL) { 1285 /* SIG_DFL */ 1286 if (p->flags & (TF_FATAL | TF_DFL_INTR)) { 1287 exstat = (int)(128U + (unsigned)i); 1288 if ((unsigned)exstat > 255U) 1289 exstat = 255; 1290 } 1291 /* e.g. SIGHUP */ 1292 if (p->flags & TF_FATAL) 1293 unwind(LLEAVE); 1294 /* e.g. SIGINT, SIGQUIT, SIGTERM, etc. */ 1295 if (p->flags & TF_DFL_INTR) 1296 unwind(LINTR); 1297 goto donetrap; 1298 } 1299 if (trapstr[0] == '\0') 1300 /* SIG_IGN */ 1301 goto donetrap; 1302 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1303 /* avoid recursion on these */ 1304 old_changed = p->flags & TF_CHANGED; 1305 p->flags &= ~TF_CHANGED; 1306 p->trap = NULL; 1307 } 1308 if (trap_exstat == -1) 1309 trap_exstat = exstat & 0xFF; 1310 /* 1311 * Note: trapstr is fully parsed before anything is executed, thus 1312 * no problem with afree(p->trap) in settrap() while still in use. 1313 */ 1314 command(trapstr, current_lineno); 1315 if (i == ksh_SIGEXIT || i == ksh_SIGERR) { 1316 if (p->flags & TF_CHANGED) 1317 /* don't clear TF_CHANGED */ 1318 afree(trapstr, APERM); 1319 else 1320 p->trap = trapstr; 1321 p->flags |= old_changed; 1322 } 1323 1324 donetrap: 1325 /* we're the last trap of a sequence executed */ 1326 if (is_last && trap_exstat != -1) { 1327 exstat = trap_exstat; 1328 trap_exstat = -1; 1329 } 1330 } 1331 1332 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 1333 void 1334 cleartraps(void) 1335 { 1336 Trap *p = sigtraps; 1337 int i = ksh_NSIG + 1; 1338 1339 trap = 0; 1340 intrsig = 0; 1341 fatal_trap = 0; 1342 1343 do { 1344 p->set = 0; 1345 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 1346 settrap(p, NULL); 1347 ++p; 1348 } while (--i); 1349 } 1350 1351 /* restore signals just before an exec(2) */ 1352 void 1353 restoresigs(void) 1354 { 1355 Trap *p = sigtraps; 1356 int i = ksh_NSIG + 1; 1357 1358 do { 1359 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 1360 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 1361 SS_RESTORE_CURR|SS_FORCE); 1362 ++p; 1363 } while (--i); 1364 } 1365 1366 void 1367 settrap(Trap *p, const char *s) 1368 { 1369 sig_t f; 1370 1371 afree(p->trap, APERM); 1372 /* handles s == NULL */ 1373 strdupx(p->trap, s, APERM); 1374 p->flags |= TF_CHANGED; 1375 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 1376 1377 p->flags |= TF_USER_SET; 1378 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 1379 f = trapsig; 1380 else if (p->flags & TF_SHELL_USES) { 1381 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 1382 /* do what user wants at exec time */ 1383 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1384 if (f == SIG_IGN) 1385 p->flags |= TF_EXEC_IGN; 1386 else 1387 p->flags |= TF_EXEC_DFL; 1388 } 1389 1390 /* 1391 * assumes handler already set to what shell wants it 1392 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 1393 */ 1394 return; 1395 } 1396 1397 /* todo: should we let user know signal is ignored? how? */ 1398 setsig(p, f, SS_RESTORE_CURR|SS_USER); 1399 } 1400 1401 /* 1402 * called by c_print() when writing to a co-process to ensure 1403 * SIGPIPE won't kill shell (unless user catches it and exits) 1404 */ 1405 bool 1406 block_pipe(void) 1407 { 1408 bool restore_dfl = false; 1409 Trap *p = &sigtraps[SIGPIPE]; 1410 1411 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1412 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1413 if (p->flags & TF_ORIG_DFL) 1414 restore_dfl = true; 1415 } else if (p->cursig == SIG_DFL) { 1416 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1417 /* restore to SIG_DFL */ 1418 restore_dfl = true; 1419 } 1420 return (restore_dfl); 1421 } 1422 1423 /* called by c_print() to undo whatever block_pipe() did */ 1424 void 1425 restore_pipe(void) 1426 { 1427 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 1428 } 1429 1430 /* 1431 * Set action for a signal. Action may not be set if original 1432 * action was SIG_IGN, depending on the value of flags and FTALKING. 1433 */ 1434 int 1435 setsig(Trap *p, sig_t f, int flags) 1436 { 1437 struct sigaction sigact; 1438 1439 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) 1440 return (1); 1441 1442 memset(&sigact, 0, sizeof(sigact)); 1443 1444 /* 1445 * First time setting this signal? If so, get and note the current 1446 * setting. 1447 */ 1448 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1449 sigaction(p->signal, &Sigact_ign, &sigact); 1450 p->flags |= sigact.sa_handler == SIG_IGN ? 1451 TF_ORIG_IGN : TF_ORIG_DFL; 1452 p->cursig = SIG_IGN; 1453 } 1454 1455 /*- 1456 * Generally, an ignored signal stays ignored, except if 1457 * - the user of an interactive shell wants to change it 1458 * - the shell wants for force a change 1459 */ 1460 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 1461 (!(flags & SS_USER) || !Flag(FTALKING))) 1462 return (0); 1463 1464 setexecsig(p, flags & SS_RESTORE_MASK); 1465 1466 /* 1467 * This is here 'cause there should be a way of clearing 1468 * shtraps, but don't know if this is a sane way of doing 1469 * it. At the moment, all users of shtrap are lifetime 1470 * users (SIGALRM, SIGCHLD, SIGWINCH). 1471 */ 1472 if (!(flags & SS_USER)) 1473 p->shtrap = (sig_t)NULL; 1474 if (flags & SS_SHTRAP) { 1475 p->shtrap = f; 1476 f = trapsig; 1477 } 1478 1479 if (p->cursig != f) { 1480 p->cursig = f; 1481 (void)sigemptyset(&sigact.sa_mask); 1482 /* interruptible */ 1483 sigact.sa_flags = 0; 1484 sigact.sa_handler = f; 1485 sigaction(p->signal, &sigact, NULL); 1486 } 1487 1488 return (1); 1489 } 1490 1491 /* control what signal is set to before an exec() */ 1492 void 1493 setexecsig(Trap *p, int restore) 1494 { 1495 /* XXX debugging */ 1496 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 1497 internal_errorf("setexecsig: unset signal %d(%s)", 1498 p->signal, p->name); 1499 1500 /* restore original value for exec'd kids */ 1501 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1502 switch (restore & SS_RESTORE_MASK) { 1503 case SS_RESTORE_CURR: 1504 /* leave things as they currently are */ 1505 break; 1506 case SS_RESTORE_ORIG: 1507 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 1508 break; 1509 case SS_RESTORE_DFL: 1510 p->flags |= TF_EXEC_DFL; 1511 break; 1512 case SS_RESTORE_IGN: 1513 p->flags |= TF_EXEC_IGN; 1514 break; 1515 } 1516 } 1517 1518 #if HAVE_PERSISTENT_HISTORY || defined(DF) 1519 /* 1520 * File descriptor locking and unlocking functions. 1521 * Could use some error handling, but hey, this is only 1522 * advisory locking anyway, will often not work over NFS, 1523 * and you are SOL if this fails... 1524 */ 1525 1526 void 1527 mksh_lockfd(int fd) 1528 { 1529 #if defined(__OpenBSD__) 1530 /* flock is not interrupted by signals */ 1531 (void)flock(fd, LOCK_EX); 1532 #elif HAVE_FLOCK 1533 int rv; 1534 1535 /* e.g. on Linux */ 1536 do { 1537 rv = flock(fd, LOCK_EX); 1538 } while (rv == 1 && errno == EINTR); 1539 #elif HAVE_LOCK_FCNTL 1540 int rv; 1541 struct flock lks; 1542 1543 memset(&lks, 0, sizeof(lks)); 1544 lks.l_type = F_WRLCK; 1545 do { 1546 rv = fcntl(fd, F_SETLKW, &lks); 1547 } while (rv == 1 && errno == EINTR); 1548 #endif 1549 } 1550 1551 /* designed to not define mksh_unlkfd if none triggered */ 1552 #if HAVE_FLOCK 1553 void 1554 mksh_unlkfd(int fd) 1555 { 1556 (void)flock(fd, LOCK_UN); 1557 } 1558 #elif HAVE_LOCK_FCNTL 1559 void 1560 mksh_unlkfd(int fd) 1561 { 1562 struct flock lks; 1563 1564 memset(&lks, 0, sizeof(lks)); 1565 lks.l_type = F_UNLCK; 1566 (void)fcntl(fd, F_SETLKW, &lks); 1567 } 1568 #endif 1569 #endif 1570