1 /* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */
2 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */
5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
7 * Thorsten Glaser <tg@mirbsd.org>
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.
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.
30 __RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134 2014/06/09 13:25:53 tg Exp $");
32 Trap sigtraps[NSIG + 1];
33 static struct sigaction Sigact_ign;
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 *);
41 static int hist_execute(char *);
42 static char **hist_get(const char *, bool, bool);
43 static char **hist_get_oldest(void);
45 static bool hstarted; /* set after hist_init() called */
46 static Source *hist_source;
48 #if HAVE_PERSISTENT_HISTORY
51 #define caddr_cast(x) ((void *)(x))
53 #define caddr_cast(x) ((caddr_t)(x))
56 /* several OEs do not have these constants */
58 #define MAP_FAILED caddr_cast(-1)
61 /* some OEs need the default mapping type specified */
66 /* current history file: name, fd, size */
68 static int histfd = -1;
69 static off_t histfsize;
72 static const char Tnot_in_history[] = "not in history";
73 #define Thistory (Tnot_in_history + 7)
75 static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
76 #define Tspdollaru (TFCEDIT_dollaru + 18)
78 /* HISTSIZE default: size of saved history, persistent or standard */
80 #define MKSH_DEFHISTSIZE 255
82 #define MKSH_DEFHISTSIZE 2047
84 /* maximum considered size of persistent history file */
85 #define MKSH_MAXHISTFSIZE ((off_t)1048576 * 96)
92 bool gflag = false, lflag = false, nflag = false, rflag = false,
95 const char *p, *first = NULL, *last = NULL;
96 char **hfirst, **hlast, **hp, *editor = NULL;
98 if (!Flag(FTALKING_I)) {
99 bi_errorf("history %ss not available", Tfunction);
103 while ((optc = ksh_getopt(wp, &builtin_opt,
104 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
108 p = builtin_opt.optarg;
112 size_t len = strlen(p);
114 /* almost certainly not overflowing */
115 editor = alloc(len + 4, ATEMP);
116 memcpy(editor, p, len);
117 memcpy(editor + len, Tspdollaru, 4);
138 /* POSIX version of -e - */
143 /* kludge city - accept -num as -- -num (kind of) */
144 case '0': case '1': case '2': case '3': case '4':
145 case '5': case '6': case '7': case '8': case '9':
146 p = shf_smprintf("-%c%s",
147 optc, builtin_opt.optarg);
153 bi_errorf("too many arguments");
161 wp += builtin_opt.optind;
163 /* Substitute and execute command */
165 char *pat = NULL, *rep = NULL, *line;
167 if (editor || lflag || nflag || rflag) {
168 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
172 /* Check for pattern replacement argument */
173 if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
174 strdupx(pat, *wp, ATEMP);
175 rep = pat + (p - *wp);
179 /* Check for search prefix */
180 if (!first && (first = *wp))
183 bi_errorf("too many arguments");
187 hp = first ? hist_get(first, false, false) :
188 hist_get_newest(false);
193 strdupx(line, *hp, ATEMP);
196 size_t len, pat_len, rep_len;
199 bool any_subst = false;
201 pat_len = strlen(pat);
202 rep_len = strlen(rep);
203 Xinit(xs, xp, 128, ATEMP);
204 for (s = *hp; (s1 = strstr(s, pat)) &&
205 (!any_subst || gflag); s = s1 + pat_len) {
208 XcheckN(xs, xp, len + rep_len);
213 memcpy(xp, rep, rep_len);
217 bi_errorf("bad substitution");
221 XcheckN(xs, xp, len);
224 line = Xclose(xs, xp);
226 return (hist_execute(line));
229 if (editor && (lflag || nflag)) {
230 bi_errorf("can't use -l, -n with -e");
234 if (!first && (first = *wp))
236 if (!last && (last = *wp))
239 bi_errorf("too many arguments");
243 hfirst = lflag ? hist_get("-16", true, true) :
244 hist_get_newest(false);
247 /* can't fail if hfirst didn't fail */
248 hlast = hist_get_newest(false);
251 * POSIX says not an error if first/last out of bounds
252 * when range is specified; AT&T ksh and pdksh allow out
253 * of bounds for -l as well.
255 hfirst = hist_get(first, tobool(lflag || last), lflag);
258 hlast = last ? hist_get(last, true, lflag) :
259 (lflag ? hist_get_newest(false) : hfirst);
263 if (hfirst > hlast) {
266 temp = hfirst; hfirst = hlast; hlast = temp;
275 for (hp = rflag ? hlast : hfirst;
276 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
278 shf_fprintf(shl_stdout, "%d",
279 hist_source->line - (int)(histptr - hp));
280 shf_putc('\t', shl_stdout);
281 /* print multi-line commands correctly */
283 while ((t = strchr(s, '\n'))) {
285 shf_fprintf(shl_stdout, "%s\n\t", s);
289 shf_fprintf(shl_stdout, "%s\n", s);
291 shf_flush(shl_stdout);
295 /* Run editor on selected lines, then run resulting commands */
297 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
298 if (!(shf = tf->shf)) {
299 bi_errorf("can't %s temporary file %s: %s",
300 "create", tf->tffn, cstrerror(errno));
303 for (hp = rflag ? hlast : hfirst;
304 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
305 shf_fprintf(shf, "%s\n", *hp);
306 if (shf_close(shf) == EOF) {
307 bi_errorf("can't %s temporary file %s: %s",
308 "write", tf->tffn, cstrerror(errno));
312 /* Ignore setstr errors here (arbitrary) */
313 setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
315 /* XXX: source should not get trashed by this.. */
317 Source *sold = source;
320 ret = command(editor ? editor : TFCEDIT_dollaru, 0);
332 if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
333 bi_errorf("can't %s temporary file %s: %s",
334 "open", tf->tffn, cstrerror(errno));
338 if (stat(tf->tffn, &statb) < 0)
340 else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
341 bi_errorf("%s %s too large: %lu", Thistory,
342 "file", (unsigned long)statb.st_size);
345 n = (size_t)statb.st_size + 1;
346 Xinit(xs, xp, n, hist_source->areap);
347 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
349 if (Xnleft(xs, xp) <= 0)
350 XcheckN(xs, xp, Xlength(xs, xp));
353 bi_errorf("can't %s temporary file %s: %s",
354 "read", tf->tffn, cstrerror(shf_errno(shf)));
361 strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
362 return (hist_execute(Xstring(xs, xp)));
366 /* Save cmd in history, execute cmd (cmd gets trashed) */
368 hist_execute(char *cmd)
370 static int last_line = -1;
375 /* Back up over last histsave */
376 if (histptr >= history && last_line != hist_source->line) {
378 afree(*histptr, APERM);
380 last_line = hist_source->line;
383 for (p = cmd; p; p = q) {
384 if ((q = strchr(p, '\n'))) {
385 /* kill the newline */
388 /* ignore trailing newline */
391 histsave(&hist_source->line, p, true, true);
393 /* POSIX doesn't say this is done... */
396 /* restore \n (trailing \n not restored) */
401 * Commands are executed here instead of pushing them onto the
402 * input 'cause POSIX says the redirection and variable assignments
404 * X=y fc -e - 42 2> /dev/null
405 * are to effect the repeated commands environment.
407 /* XXX: source should not get trashed by this.. */
409 ret = command(cmd, 0);
415 * get pointer to history given pattern
416 * pattern is a number or string
419 hist_get(const char *str, bool approx, bool allow_cur)
425 hp = histptr + (n < 0 ? n : (n - hist_source->line));
426 if ((ptrdiff_t)hp < (ptrdiff_t)history) {
428 hp = hist_get_oldest();
430 bi_errorf("%s: %s", str, Tnot_in_history);
433 } else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
435 hp = hist_get_newest(allow_cur);
437 bi_errorf("%s: %s", str, Tnot_in_history);
440 } else if (!allow_cur && hp == histptr) {
441 bi_errorf("%s: %s", str, "invalid range");
445 bool anchored = *str == '?' ? (++str, false) : true;
447 /* the -1 is to avoid the current fc command */
448 if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
449 bi_errorf("%s: %s", str, Tnot_in_history);
456 /* Return a pointer to the newest command in the history */
458 hist_get_newest(bool allow_cur)
460 if (histptr < history || (!allow_cur && histptr == history)) {
461 bi_errorf("no history (yet)");
464 return (allow_cur ? histptr : histptr - 1);
467 /* Return a pointer to the oldest command in the history */
469 hist_get_oldest(void)
471 if (histptr <= history) {
472 bi_errorf("no history (yet)");
478 #if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
479 /* current position in history[] */
480 static char **current;
483 * Return the current position.
494 int last = histptr - history;
496 if (n < 0 || n >= last) {
500 current = &history[n];
507 * This will become unnecessary if hist_get is modified to allow
508 * searching from positions other than the end, and in either
512 findhist(int start, int fwd, const char *str, bool anchored)
515 int maxhist = histptr - history;
516 int incr = fwd ? 1 : -1;
517 size_t len = strlen(str);
519 if (start < 0 || start >= maxhist)
522 hp = &history[start];
523 for (; hp >= history && hp <= histptr; hp += incr)
524 if ((anchored && strncmp(*hp, str, len) == 0) ||
525 (!anchored && strstr(*hp, str)))
526 return (hp - history);
532 * set history; this means reallocating the dataspace
535 sethistsize(mksh_ari_t n)
537 if (n > 0 && n != histsize) {
538 int cursize = histptr - history;
540 /* save most recent history */
542 memmove(history, histptr - n + 1, n * sizeof(char *));
546 history = aresize2(history, n, sizeof(char *), APERM);
549 histptr = history + cursize;
553 #if HAVE_PERSISTENT_HISTORY
555 * set history file; this can mean reloading/resetting/starting
556 * history file maintenance
559 sethistfile(const char *name)
561 /* if not started then nothing to do */
562 if (hstarted == false)
565 /* if the name is the same as the name we have */
566 if (hname && strcmp(hname, name) == 0)
570 * it's a new name - possibly
573 /* yes the file is open */
579 /* let's reset the history */
580 histptr = history - 1;
581 hist_source->line = 0;
584 hist_init(hist_source);
589 * initialise the history vector
594 if (history == (char **)NULL) {
595 histsize = MKSH_DEFHISTSIZE;
596 history = alloc2(histsize, sizeof(char *), APERM);
597 histptr = history - 1;
603 * It turns out that there is a lot of ghastly hackery here
606 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
607 /* do not save command in history but possibly sync */
611 bool changed = false;
614 int lno = hist_source->line;
617 writehistfile(0, NULL);
620 if (lno != hist_source->line)
629 * save command in history
632 histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
637 mkssert(cmd != NULL);
638 strdupx(c, cmd, APERM);
639 if ((cp = strchr(c, '\n')) != NULL)
642 if (ignoredups && !strcmp(c, *histptr)
643 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
652 #if HAVE_PERSISTENT_HISTORY
653 if (dowrite && histfd != -1)
654 writehistfile(*lnp, c);
659 if (++hp >= history + histsize) {
660 /* remove oldest command */
661 afree(*history, APERM);
662 for (hp = history; hp < history + histsize - 1; hp++)
670 * Write history data to a file nominated by HISTFILE;
671 * if HISTFILE is unset then history still happens, but
672 * the data is not written to a file. All copies of ksh
673 * looking at the file will maintain the same history.
674 * This is ksh behaviour.
676 * This stuff uses mmap()
678 * This stuff is so totally broken it must eventually be
679 * redesigned, without mmap, better checks, support for
680 * larger files, etc. and handle partially corrupted files
684 * Open a history file
687 * HMAGIC - just to check that we are dealing with the correct object
688 * Then follows a number of stored commands
690 * <command byte><command number(4 octets, big endian)><bytes><NUL>
696 #if HAVE_PERSISTENT_HISTORY
697 static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 };
703 #if HAVE_PERSISTENT_HISTORY
706 enum { hist_init_first, hist_init_retry, hist_init_restore } hs;
709 if (Flag(FTALKING) == 0)
715 #if HAVE_PERSISTENT_HISTORY
716 if ((hname = str_val(global("HISTFILE"))) == NULL)
718 strdupx(hname, hname, APERM);
719 hs = hist_init_first;
722 /* we have a file and are interactive */
723 if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND | O_BINARY,
733 histfsize = lseek(histfd, (off_t)0, SEEK_END);
734 if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
735 /* we ignore too large files but still append to them */
736 /* we also don't need to re-read after truncation */
738 } else if (histfsize > 2) {
739 /* we have some data, check its validity */
740 base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ,
741 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
742 if (base == (unsigned char *)MAP_FAILED)
744 if (base[0] != HMAGIC1 || base[1] != HMAGIC2) {
745 munmap(caddr_cast(base), (size_t)histfsize);
748 /* load _all_ data */
749 lines = histload(hist_source, base + 2, (size_t)histfsize - 2);
750 munmap(caddr_cast(base), (size_t)histfsize);
751 /* check if the file needs to be truncated */
752 if (lines > histsize && histptr >= history) {
753 /* you're fucked up with the current code, trust me */
757 /* create temporary file */
758 nhname = shf_smprintf("%s.%d", hname, (int)procpid);
759 if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
760 O_EXCL | O_BINARY, 0600)) < 0) {
761 /* just don't truncate then, meh. */
762 goto hist_trunc_dont;
764 if (fstat(histfd, &sb) >= 0 &&
765 chown(nhname, sb.st_uid, sb.st_gid)) {
766 /* abort the truncation then, meh. */
767 goto hist_trunc_abort;
769 /* we definitively want some magic in that file */
770 if (write(fd, sprinkle, 2) != 2)
771 goto hist_trunc_abort;
772 /* and of course the entries */
774 while (hp < histptr) {
775 if (!writehistline(fd,
776 s->line - (histptr - hp), *hp))
777 goto hist_trunc_abort;
780 /* now unlock, close both, rename, rinse, repeat */
784 if (rename(nhname, hname) < 0) {
790 goto hist_trunc_dont;
791 /* darn! restore histfd and pray */
793 hs = hist_init_restore;
795 afree(nhname, ATEMP);
796 if (hs == hist_init_restore)
799 } else if (histfsize != 0) {
800 /* negative or too small... */
802 /* ... or mmap failed or illegal */
804 /* nuke the bogus file then retry, at most once */
805 if (!unlink(hname) && hs != hist_init_retry) {
806 hs = hist_init_retry;
809 if (hs != hist_init_retry)
810 bi_errorf("can't %s %s: %s",
811 "unlink HISTFILE", hname, cstrerror(errno));
815 /* size 0, add magic to the history file */
816 if (write(histfd, sprinkle, 2) != 2) {
821 histfsize = lseek(histfd, (off_t)0, SEEK_END);
827 #if HAVE_PERSISTENT_HISTORY
829 * load the history structure from the stored data
832 histload(Source *s, unsigned char *base, size_t bytes)
834 int lno = 0, lines = 0;
838 /* !bytes check as some systems (older FreeBSDs) have buggy memchr */
839 if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL)
841 /* advance base pointer past COMMAND byte */
842 bytes -= ++cp - base;
844 /* if there is no full string left, don't bother with the rest */
845 if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL)
847 /* load the stored line number */
848 lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) |
849 ((base[2] & 0xFF) << 8) | (base[3] & 0xFF);
850 /* store away the found line (@base[4]) */
852 if (histptr >= history && lno - 1 != s->line) {
856 if (lno >= s->line - (histptr - history) && lno <= s->line) {
857 hp = &histptr[lno - s->line];
860 strdupx(*hp, (char *)(base + 4), APERM);
864 histsave(&lno, (char *)(base + 4), false, false);
866 /* advance base pointer past NUL */
867 bytes -= ++cp - base;
869 /* repeat until no more */
874 * write a command to the end of the history file
876 * This *MAY* seem easy but it's also necessary to check
877 * that the history file has not changed in size.
878 * If it has - then some other shell has written to it and
879 * we should (re)read those commands to update our history
882 writehistfile(int lno, const char *cmd)
886 unsigned char *base, *news;
889 sizenow = lseek(histfd, (off_t)0, SEEK_END);
890 if (sizenow < histfsize) {
891 /* the file has shrunk; give up */
895 /* ignore changes when the file is too large */
896 sizenow <= MKSH_MAXHISTFSIZE
898 /* the size has changed, we need to do read updates */
901 /* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */
902 bytes = (size_t)(sizenow - histfsize);
903 base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
904 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
905 if (base == (unsigned char *)MAP_FAILED)
907 news = base + (size_t)histfsize;
908 if (*news == COMMAND) {
910 histload(hist_source, news, bytes);
912 lno = hist_source->line;
915 munmap(caddr_cast(base), (size_t)sizenow);
919 if (cmd && !writehistline(histfd, lno, cmd)) {
924 histfsize = lseek(histfd, (off_t)0, SEEK_END);
929 writehistline(int fd, int lno, const char *cmd)
932 unsigned char hdr[5];
935 hdr[1] = (lno >> 24) & 0xFF;
936 hdr[2] = (lno >> 16) & 0xFF;
937 hdr[3] = (lno >> 8) & 0xFF;
940 return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n);
955 #if !HAVE_SYS_SIGNAME
956 static const struct mksh_sigpair {
957 const char * const name;
959 } mksh_sigpairs[] = {
960 #include "signames.inc"
966 #if !HAVE_SYS_SIGLIST_DECL
967 extern const char * const sys_siglist[];
979 /* Populate sigtraps based on sys_signame and sys_siglist. */
980 /*XXX this is idiotic, use a multi-key/value hashtable! */
981 for (i = 0; i <= NSIG; i++) {
982 sigtraps[i].signal = i;
983 if (i == ksh_SIGERR) {
984 sigtraps[i].name = "ERR";
985 sigtraps[i].mess = "Error handler";
990 const struct mksh_sigpair *pair = mksh_sigpairs;
991 while ((pair->nr != i) && (pair->name != NULL))
997 sigtraps[i].name = shf_smprintf("%d", i);
1001 /* this is not optimal, what about SIGSIG1? */
1002 if ((cs[0] & 0xDF) == 'S' &&
1003 (cs[1] & 0xDF) == 'I' &&
1004 (cs[2] & 0xDF) == 'G' &&
1006 /* skip leading "SIG" */
1009 strdupx(s, cs, APERM);
1010 sigtraps[i].name = s;
1011 while ((*s = ksh_toupper(*s)))
1014 #if HAVE_SYS_SIGLIST
1015 sigtraps[i].mess = sys_siglist[i];
1016 #elif HAVE_STRSIGNAL
1017 sigtraps[i].mess = strsignal(i);
1019 sigtraps[i].mess = NULL;
1021 if ((sigtraps[i].mess == NULL) ||
1022 (sigtraps[i].mess[0] == '\0'))
1023 sigtraps[i].mess = shf_smprintf("%s %d",
1027 /* our name for signal 0 */
1028 sigtraps[ksh_SIGEXIT].name = "EXIT";
1030 (void)sigemptyset(&Sigact_ign.sa_mask);
1031 Sigact_ign.sa_flags = 0; /* interruptible */
1032 Sigact_ign.sa_handler = SIG_IGN;
1034 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1035 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
1036 /* SIGTERM is not fatal for interactive */
1037 sigtraps[SIGTERM].flags |= TF_DFL_INTR;
1038 sigtraps[SIGHUP].flags |= TF_FATAL;
1039 sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
1041 /* these are always caught so we can clean up any temporary files. */
1042 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
1043 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
1044 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
1045 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
1048 static void alarm_catcher(int sig);
1053 sigtraps[SIGALRM].flags |= TF_SHELL_USES;
1054 setsig(&sigtraps[SIGALRM], alarm_catcher,
1055 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
1060 alarm_catcher(int sig MKSH_A_UNUSED)
1062 /* this runs inside interrupt context, with errno saved */
1064 if (ksh_tmout_state == TMOUT_READING) {
1065 int left = alarm(0);
1068 ksh_tmout_state = TMOUT_LEAVING;
1076 gettrap(const char *cs, bool igncase)
1082 if (ksh_isdigit(*cs)) {
1083 return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
1084 (&sigtraps[i]) : NULL);
1087 /* this breaks SIGSIG1, but we do that above anyway */
1088 if ((cs[0] & 0xDF) == 'S' &&
1089 (cs[1] & 0xDF) == 'I' &&
1090 (cs[2] & 0xDF) == 'G' &&
1092 /* skip leading "SIG" */
1098 strdupx(as, cs, ATEMP);
1100 while ((*s = ksh_toupper(*s)))
1106 for (i = 0; i <= NSIG; i++) {
1107 if (!strcmp(p->name, cs))
1118 * trap signal handler
1123 Trap *p = &sigtraps[i];
1127 if (p->flags & TF_DFL_INTR)
1129 if ((p->flags & TF_FATAL) && !p->trap) {
1139 * called when we want to allow the user to ^C out of something - won't
1140 * work if user has trapped SIGINT.
1146 runtraps(TF_DFL_INTR|TF_FATAL);
1150 * called after EINTR to check if a signal with normally causes process
1151 * termination has been received.
1154 fatal_trap_check(void)
1159 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
1160 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1161 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
1162 /* return value is used as an exit code */
1163 return (128 + p->signal);
1168 * Returns the signal number of any pending traps: ie, a signal which has
1169 * occurred for which a trap has been set or for which the TF_DFL_INTR flag
1178 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1179 if (p->set && ((p->trap && p->trap[0]) ||
1180 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
1186 * run any pending traps. If intr is set, only run traps that
1187 * can interrupt commands.
1195 if (ksh_tmout_state == TMOUT_LEAVING) {
1196 ksh_tmout_state = TMOUT_EXECUTING;
1197 warningf(false, "timed out waiting for input");
1201 * XXX: this means the alarm will have no effect if a trap
1202 * is caught after the alarm() was started...not good.
1204 ksh_tmout_state = TMOUT_EXECUTING;
1207 if (flag & TF_DFL_INTR)
1209 if (flag & TF_FATAL)
1212 for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
1213 if (p->set && (!flag ||
1214 ((p->flags & flag) && p->trap == NULL)))
1217 runtrap(NULL, true);
1221 runtrap(Trap *p, bool is_last)
1223 int old_changed = 0, i;
1227 /* just clean up, see runtraps() above */
1232 if (trapstr == NULL) {
1234 if (p->flags & TF_FATAL) {
1236 exstat = (int)ksh_min(128U + (unsigned)i, 255U);
1239 if (p->flags & TF_DFL_INTR) {
1240 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
1241 exstat = (int)ksh_min(128U + (unsigned)i, 255U);
1246 if (trapstr[0] == '\0')
1249 if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1250 /* avoid recursion on these */
1251 old_changed = p->flags & TF_CHANGED;
1252 p->flags &= ~TF_CHANGED;
1255 if (trap_exstat == -1)
1256 trap_exstat = exstat & 0xFF;
1258 * Note: trapstr is fully parsed before anything is executed, thus
1259 * no problem with afree(p->trap) in settrap() while still in use.
1261 command(trapstr, current_lineno);
1262 if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
1263 if (p->flags & TF_CHANGED)
1264 /* don't clear TF_CHANGED */
1265 afree(trapstr, APERM);
1268 p->flags |= old_changed;
1272 /* we're the last trap of a sequence executed */
1273 if (is_last && trap_exstat != -1) {
1274 exstat = trap_exstat;
1279 /* clear pending traps and reset user's trap handlers; used after fork(2) */
1289 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
1291 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
1296 /* restore signals just before an exec(2) */
1303 for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
1304 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
1305 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
1306 SS_RESTORE_CURR|SS_FORCE);
1310 settrap(Trap *p, const char *s)
1315 afree(p->trap, APERM);
1316 /* handles s == NULL */
1317 strdupx(p->trap, s, APERM);
1318 p->flags |= TF_CHANGED;
1319 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
1321 p->flags |= TF_USER_SET;
1322 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
1324 else if (p->flags & TF_SHELL_USES) {
1325 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
1326 /* do what user wants at exec time */
1327 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1329 p->flags |= TF_EXEC_IGN;
1331 p->flags |= TF_EXEC_DFL;
1335 * assumes handler already set to what shell wants it
1336 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
1341 /* todo: should we let user know signal is ignored? how? */
1342 setsig(p, f, SS_RESTORE_CURR|SS_USER);
1346 * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
1347 * kill shell (unless user catches it and exits)
1352 int restore_dfl = 0;
1353 Trap *p = &sigtraps[SIGPIPE];
1355 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1356 setsig(p, SIG_IGN, SS_RESTORE_CURR);
1357 if (p->flags & TF_ORIG_DFL)
1359 } else if (p->cursig == SIG_DFL) {
1360 setsig(p, SIG_IGN, SS_RESTORE_CURR);
1361 /* restore to SIG_DFL */
1364 return (restore_dfl);
1367 /* Called by c_print() to undo whatever block_pipe() did */
1369 restore_pipe(int restore_dfl)
1372 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
1376 * Set action for a signal. Action may not be set if original
1377 * action was SIG_IGN, depending on the value of flags and FTALKING.
1380 setsig(Trap *p, sig_t f, int flags)
1382 struct sigaction sigact;
1384 if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
1387 memset(&sigact, 0, sizeof(sigact));
1390 * First time setting this signal? If so, get and note the current
1393 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
1394 sigaction(p->signal, &Sigact_ign, &sigact);
1395 p->flags |= sigact.sa_handler == SIG_IGN ?
1396 TF_ORIG_IGN : TF_ORIG_DFL;
1397 p->cursig = SIG_IGN;
1401 * Generally, an ignored signal stays ignored, except if
1402 * - the user of an interactive shell wants to change it
1403 * - the shell wants for force a change
1405 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
1406 (!(flags & SS_USER) || !Flag(FTALKING)))
1409 setexecsig(p, flags & SS_RESTORE_MASK);
1412 * This is here 'cause there should be a way of clearing
1413 * shtraps, but don't know if this is a sane way of doing
1414 * it. At the moment, all users of shtrap are lifetime
1415 * users (SIGALRM, SIGCHLD, SIGWINCH).
1417 if (!(flags & SS_USER))
1418 p->shtrap = (sig_t)NULL;
1419 if (flags & SS_SHTRAP) {
1424 if (p->cursig != f) {
1426 (void)sigemptyset(&sigact.sa_mask);
1428 sigact.sa_flags = 0;
1429 sigact.sa_handler = f;
1430 sigaction(p->signal, &sigact, NULL);
1436 /* control what signal is set to before an exec() */
1438 setexecsig(Trap *p, int restore)
1441 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
1442 internal_errorf("setexecsig: unset signal %d(%s)",
1443 p->signal, p->name);
1445 /* restore original value for exec'd kids */
1446 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
1447 switch (restore & SS_RESTORE_MASK) {
1448 case SS_RESTORE_CURR:
1449 /* leave things as they currently are */
1451 case SS_RESTORE_ORIG:
1452 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
1454 case SS_RESTORE_DFL:
1455 p->flags |= TF_EXEC_DFL;
1457 case SS_RESTORE_IGN:
1458 p->flags |= TF_EXEC_IGN;
1463 #if HAVE_PERSISTENT_HISTORY || defined(DF)
1465 * File descriptor locking and unlocking functions.
1466 * Could use some error handling, but hey, this is only
1467 * advisory locking anyway, will often not work over NFS,
1468 * and you are SOL if this fails...
1474 #if defined(__OpenBSD__)
1475 /* flock is not interrupted by signals */
1476 (void)flock(fd, LOCK_EX);
1482 rv = flock(fd, LOCK_EX);
1483 } while (rv == 1 && errno == EINTR);
1484 #elif HAVE_LOCK_FCNTL
1488 memset(&lks, 0, sizeof(lks));
1489 lks.l_type = F_WRLCK;
1491 rv = fcntl(fd, F_SETLKW, &lks);
1492 } while (rv == 1 && errno == EINTR);
1496 /* designed to not define mksh_unlkfd if none triggered */
1501 (void)flock(fd, LOCK_UN);
1503 #elif HAVE_LOCK_FCNTL
1509 memset(&lks, 0, sizeof(lks));
1510 lks.l_type = F_UNLCK;
1511 (void)fcntl(fd, F_SETLKW, &lks);