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 $ */ |
3 |
|
4 |
/*- |
5 |
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, |
6 |
* 2011, 2012, 2014 |
7 |
* Thorsten Glaser <tg@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.134 2014/06/09 13:25:53 tg Exp $"); |
31 |
|
32 |
Trap sigtraps[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 *); |
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 |
int |
88 |
c_fc(const char **wp) |
89 |
{ |
90 |
struct shf *shf; |
91 |
struct temp *tf; |
92 |
bool gflag = false, lflag = false, nflag = false, rflag = false, |
93 |
sflag = false; |
94 |
int optc; |
95 |
const char *p, *first = NULL, *last = NULL; |
96 |
char **hfirst, **hlast, **hp, *editor = NULL; |
97 |
|
98 |
if (!Flag(FTALKING_I)) { |
99 |
bi_errorf("history %ss not available", Tfunction); |
100 |
return (1); |
101 |
} |
102 |
|
103 |
while ((optc = ksh_getopt(wp, &builtin_opt, |
104 |
"e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1) |
105 |
switch (optc) { |
106 |
|
107 |
case 'e': |
108 |
p = builtin_opt.optarg; |
109 |
if (ksh_isdash(p)) |
110 |
sflag = true; |
111 |
else { |
112 |
size_t len = strlen(p); |
113 |
|
114 |
/* almost certainly not overflowing */ |
115 |
editor = alloc(len + 4, ATEMP); |
116 |
memcpy(editor, p, len); |
117 |
memcpy(editor + len, Tspdollaru, 4); |
118 |
} |
119 |
break; |
120 |
|
121 |
/* non-AT&T ksh */ |
122 |
case 'g': |
123 |
gflag = true; |
124 |
break; |
125 |
|
126 |
case 'l': |
127 |
lflag = true; |
128 |
break; |
129 |
|
130 |
case 'n': |
131 |
nflag = true; |
132 |
break; |
133 |
|
134 |
case 'r': |
135 |
rflag = true; |
136 |
break; |
137 |
|
138 |
/* POSIX version of -e - */ |
139 |
case 's': |
140 |
sflag = true; |
141 |
break; |
142 |
|
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); |
148 |
if (!first) |
149 |
first = p; |
150 |
else if (!last) |
151 |
last = p; |
152 |
else { |
153 |
bi_errorf("too many arguments"); |
154 |
return (1); |
155 |
} |
156 |
break; |
157 |
|
158 |
case '?': |
159 |
return (1); |
160 |
} |
161 |
wp += builtin_opt.optind; |
162 |
|
163 |
/* Substitute and execute command */ |
164 |
if (sflag) { |
165 |
char *pat = NULL, *rep = NULL, *line; |
166 |
|
167 |
if (editor || lflag || nflag || rflag) { |
168 |
bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); |
169 |
return (1); |
170 |
} |
171 |
|
172 |
/* Check for pattern replacement argument */ |
173 |
if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) { |
174 |
strdupx(pat, *wp, ATEMP); |
175 |
rep = pat + (p - *wp); |
176 |
*rep++ = '\0'; |
177 |
wp++; |
178 |
} |
179 |
/* Check for search prefix */ |
180 |
if (!first && (first = *wp)) |
181 |
wp++; |
182 |
if (last || *wp) { |
183 |
bi_errorf("too many arguments"); |
184 |
return (1); |
185 |
} |
186 |
|
187 |
hp = first ? hist_get(first, false, false) : |
188 |
hist_get_newest(false); |
189 |
if (!hp) |
190 |
return (1); |
191 |
/* hist_replace */ |
192 |
if (!pat) |
193 |
strdupx(line, *hp, ATEMP); |
194 |
else { |
195 |
char *s, *s1; |
196 |
size_t len, pat_len, rep_len; |
197 |
XString xs; |
198 |
char *xp; |
199 |
bool any_subst = false; |
200 |
|
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) { |
206 |
any_subst = true; |
207 |
len = s1 - s; |
208 |
XcheckN(xs, xp, len + rep_len); |
209 |
/*; first part */ |
210 |
memcpy(xp, s, len); |
211 |
xp += len; |
212 |
/* replacement */ |
213 |
memcpy(xp, rep, rep_len); |
214 |
xp += rep_len; |
215 |
} |
216 |
if (!any_subst) { |
217 |
bi_errorf("bad substitution"); |
218 |
return (1); |
219 |
} |
220 |
len = strlen(s) + 1; |
221 |
XcheckN(xs, xp, len); |
222 |
memcpy(xp, s, len); |
223 |
xp += len; |
224 |
line = Xclose(xs, xp); |
225 |
} |
226 |
return (hist_execute(line)); |
227 |
} |
228 |
|
229 |
if (editor && (lflag || nflag)) { |
230 |
bi_errorf("can't use -l, -n with -e"); |
231 |
return (1); |
232 |
} |
233 |
|
234 |
if (!first && (first = *wp)) |
235 |
wp++; |
236 |
if (!last && (last = *wp)) |
237 |
wp++; |
238 |
if (*wp) { |
239 |
bi_errorf("too many arguments"); |
240 |
return (1); |
241 |
} |
242 |
if (!first) { |
243 |
hfirst = lflag ? hist_get("-16", true, true) : |
244 |
hist_get_newest(false); |
245 |
if (!hfirst) |
246 |
return (1); |
247 |
/* can't fail if hfirst didn't fail */ |
248 |
hlast = hist_get_newest(false); |
249 |
} else { |
250 |
/* |
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. |
254 |
*/ |
255 |
hfirst = hist_get(first, tobool(lflag || last), lflag); |
256 |
if (!hfirst) |
257 |
return (1); |
258 |
hlast = last ? hist_get(last, true, lflag) : |
259 |
(lflag ? hist_get_newest(false) : hfirst); |
260 |
if (!hlast) |
261 |
return (1); |
262 |
} |
263 |
if (hfirst > hlast) { |
264 |
char **temp; |
265 |
|
266 |
temp = hfirst; hfirst = hlast; hlast = temp; |
267 |
/* POSIX */ |
268 |
rflag = !rflag; |
269 |
} |
270 |
|
271 |
/* List history */ |
272 |
if (lflag) { |
273 |
char *s, *t; |
274 |
|
275 |
for (hp = rflag ? hlast : hfirst; |
276 |
hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) { |
277 |
if (!nflag) |
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 */ |
282 |
s = *hp; |
283 |
while ((t = strchr(s, '\n'))) { |
284 |
*t = '\0'; |
285 |
shf_fprintf(shl_stdout, "%s\n\t", s); |
286 |
*t++ = '\n'; |
287 |
s = t; |
288 |
} |
289 |
shf_fprintf(shl_stdout, "%s\n", s); |
290 |
} |
291 |
shf_flush(shl_stdout); |
292 |
return (0); |
293 |
} |
294 |
|
295 |
/* Run editor on selected lines, then run resulting commands */ |
296 |
|
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)); |
301 |
return (1); |
302 |
} |
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)); |
309 |
return (1); |
310 |
} |
311 |
|
312 |
/* Ignore setstr errors here (arbitrary) */ |
313 |
setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR); |
314 |
|
315 |
/* XXX: source should not get trashed by this.. */ |
316 |
{ |
317 |
Source *sold = source; |
318 |
int ret; |
319 |
|
320 |
ret = command(editor ? editor : TFCEDIT_dollaru, 0); |
321 |
source = sold; |
322 |
if (ret) |
323 |
return (ret); |
324 |
} |
325 |
|
326 |
{ |
327 |
struct stat statb; |
328 |
XString xs; |
329 |
char *xp; |
330 |
ssize_t n; |
331 |
|
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)); |
335 |
return (1); |
336 |
} |
337 |
|
338 |
if (stat(tf->tffn, &statb) < 0) |
339 |
n = 128; |
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); |
343 |
goto errout; |
344 |
} else |
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) { |
348 |
xp += n; |
349 |
if (Xnleft(xs, xp) <= 0) |
350 |
XcheckN(xs, xp, Xlength(xs, xp)); |
351 |
} |
352 |
if (n < 0) { |
353 |
bi_errorf("can't %s temporary file %s: %s", |
354 |
"read", tf->tffn, cstrerror(shf_errno(shf))); |
355 |
errout: |
356 |
shf_close(shf); |
357 |
return (1); |
358 |
} |
359 |
shf_close(shf); |
360 |
*xp = '\0'; |
361 |
strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); |
362 |
return (hist_execute(Xstring(xs, xp))); |
363 |
} |
364 |
} |
365 |
|
366 |
/* Save cmd in history, execute cmd (cmd gets trashed) */ |
367 |
static int |
368 |
hist_execute(char *cmd) |
369 |
{ |
370 |
static int last_line = -1; |
371 |
Source *sold; |
372 |
int ret; |
373 |
char *p, *q; |
374 |
|
375 |
/* Back up over last histsave */ |
376 |
if (histptr >= history && last_line != hist_source->line) { |
377 |
hist_source->line--; |
378 |
afree(*histptr, APERM); |
379 |
histptr--; |
380 |
last_line = hist_source->line; |
381 |
} |
382 |
|
383 |
for (p = cmd; p; p = q) { |
384 |
if ((q = strchr(p, '\n'))) { |
385 |
/* kill the newline */ |
386 |
*q++ = '\0'; |
387 |
if (!*q) |
388 |
/* ignore trailing newline */ |
389 |
q = NULL; |
390 |
} |
391 |
histsave(&hist_source->line, p, true, true); |
392 |
|
393 |
/* POSIX doesn't say this is done... */ |
394 |
shellf("%s\n", p); |
395 |
if (q) |
396 |
/* restore \n (trailing \n not restored) */ |
397 |
q[-1] = '\n'; |
398 |
} |
399 |
|
400 |
/*- |
401 |
* Commands are executed here instead of pushing them onto the |
402 |
* input 'cause POSIX says the redirection and variable assignments |
403 |
* in |
404 |
* X=y fc -e - 42 2> /dev/null |
405 |
* are to effect the repeated commands environment. |
406 |
*/ |
407 |
/* XXX: source should not get trashed by this.. */ |
408 |
sold = source; |
409 |
ret = command(cmd, 0); |
410 |
source = sold; |
411 |
return (ret); |
412 |
} |
413 |
|
414 |
/* |
415 |
* get pointer to history given pattern |
416 |
* pattern is a number or string |
417 |
*/ |
418 |
static char ** |
419 |
hist_get(const char *str, bool approx, bool allow_cur) |
420 |
{ |
421 |
char **hp = NULL; |
422 |
int n; |
423 |
|
424 |
if (getn(str, &n)) { |
425 |
hp = histptr + (n < 0 ? n : (n - hist_source->line)); |
426 |
if ((ptrdiff_t)hp < (ptrdiff_t)history) { |
427 |
if (approx) |
428 |
hp = hist_get_oldest(); |
429 |
else { |
430 |
bi_errorf("%s: %s", str, Tnot_in_history); |
431 |
hp = NULL; |
432 |
} |
433 |
} else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) { |
434 |
if (approx) |
435 |
hp = hist_get_newest(allow_cur); |
436 |
else { |
437 |
bi_errorf("%s: %s", str, Tnot_in_history); |
438 |
hp = NULL; |
439 |
} |
440 |
} else if (!allow_cur && hp == histptr) { |
441 |
bi_errorf("%s: %s", str, "invalid range"); |
442 |
hp = NULL; |
443 |
} |
444 |
} else { |
445 |
bool anchored = *str == '?' ? (++str, false) : true; |
446 |
|
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); |
450 |
else |
451 |
hp = &history[n]; |
452 |
} |
453 |
return (hp); |
454 |
} |
455 |
|
456 |
/* Return a pointer to the newest command in the history */ |
457 |
char ** |
458 |
hist_get_newest(bool allow_cur) |
459 |
{ |
460 |
if (histptr < history || (!allow_cur && histptr == history)) { |
461 |
bi_errorf("no history (yet)"); |
462 |
return (NULL); |
463 |
} |
464 |
return (allow_cur ? histptr : histptr - 1); |
465 |
} |
466 |
|
467 |
/* Return a pointer to the oldest command in the history */ |
468 |
static char ** |
469 |
hist_get_oldest(void) |
470 |
{ |
471 |
if (histptr <= history) { |
472 |
bi_errorf("no history (yet)"); |
473 |
return (NULL); |
474 |
} |
475 |
return (history); |
476 |
} |
477 |
|
478 |
#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI |
479 |
/* current position in history[] */ |
480 |
static char **current; |
481 |
|
482 |
/* |
483 |
* Return the current position. |
484 |
*/ |
485 |
char ** |
486 |
histpos(void) |
487 |
{ |
488 |
return (current); |
489 |
} |
490 |
|
491 |
int |
492 |
histnum(int n) |
493 |
{ |
494 |
int last = histptr - history; |
495 |
|
496 |
if (n < 0 || n >= last) { |
497 |
current = histptr; |
498 |
return (last); |
499 |
} else { |
500 |
current = &history[n]; |
501 |
return (n); |
502 |
} |
503 |
} |
504 |
#endif |
505 |
|
506 |
/* |
507 |
* This will become unnecessary if hist_get is modified to allow |
508 |
* searching from positions other than the end, and in either |
509 |
* direction. |
510 |
*/ |
511 |
int |
512 |
findhist(int start, int fwd, const char *str, bool anchored) |
513 |
{ |
514 |
char **hp; |
515 |
int maxhist = histptr - history; |
516 |
int incr = fwd ? 1 : -1; |
517 |
size_t len = strlen(str); |
518 |
|
519 |
if (start < 0 || start >= maxhist) |
520 |
start = maxhist; |
521 |
|
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); |
527 |
|
528 |
return (-1); |
529 |
} |
530 |
|
531 |
/* |
532 |
* set history; this means reallocating the dataspace |
533 |
*/ |
534 |
void |
535 |
sethistsize(mksh_ari_t n) |
536 |
{ |
537 |
if (n > 0 && n != histsize) { |
538 |
int cursize = histptr - history; |
539 |
|
540 |
/* save most recent history */ |
541 |
if (n < cursize) { |
542 |
memmove(history, histptr - n + 1, n * sizeof(char *)); |
543 |
cursize = n - 1; |
544 |
} |
545 |
|
546 |
history = aresize2(history, n, sizeof(char *), APERM); |
547 |
|
548 |
histsize = n; |
549 |
histptr = history + cursize; |
550 |
} |
551 |
} |
552 |
|
553 |
#if HAVE_PERSISTENT_HISTORY |
554 |
/* |
555 |
* set history file; this can mean reloading/resetting/starting |
556 |
* history file maintenance |
557 |
*/ |
558 |
void |
559 |
sethistfile(const char *name) |
560 |
{ |
561 |
/* if not started then nothing to do */ |
562 |
if (hstarted == false) |
563 |
return; |
564 |
|
565 |
/* if the name is the same as the name we have */ |
566 |
if (hname && strcmp(hname, name) == 0) |
567 |
return; |
568 |
|
569 |
/* |
570 |
* it's a new name - possibly |
571 |
*/ |
572 |
if (histfd != -1) { |
573 |
/* yes the file is open */ |
574 |
(void)close(histfd); |
575 |
histfd = -1; |
576 |
histfsize = 0; |
577 |
afree(hname, APERM); |
578 |
hname = NULL; |
579 |
/* let's reset the history */ |
580 |
histptr = history - 1; |
581 |
hist_source->line = 0; |
582 |
} |
583 |
|
584 |
hist_init(hist_source); |
585 |
} |
586 |
#endif |
587 |
|
588 |
/* |
589 |
* initialise the history vector |
590 |
*/ |
591 |
void |
592 |
init_histvec(void) |
593 |
{ |
594 |
if (history == (char **)NULL) { |
595 |
histsize = MKSH_DEFHISTSIZE; |
596 |
history = alloc2(histsize, sizeof(char *), APERM); |
597 |
histptr = history - 1; |
598 |
} |
599 |
} |
600 |
|
601 |
|
602 |
/* |
603 |
* It turns out that there is a lot of ghastly hackery here |
604 |
*/ |
605 |
|
606 |
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY |
607 |
/* do not save command in history but possibly sync */ |
608 |
bool |
609 |
histsync(void) |
610 |
{ |
611 |
bool changed = false; |
612 |
|
613 |
if (histfd != -1) { |
614 |
int lno = hist_source->line; |
615 |
|
616 |
hist_source->line++; |
617 |
writehistfile(0, NULL); |
618 |
hist_source->line--; |
619 |
|
620 |
if (lno != hist_source->line) |
621 |
changed = true; |
622 |
} |
623 |
|
624 |
return (changed); |
625 |
} |
626 |
#endif |
627 |
|
628 |
/* |
629 |
* save command in history |
630 |
*/ |
631 |
void |
632 |
histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups) |
633 |
{ |
634 |
char **hp; |
635 |
char *c, *cp; |
636 |
|
637 |
mkssert(cmd != NULL); |
638 |
strdupx(c, cmd, APERM); |
639 |
if ((cp = strchr(c, '\n')) != NULL) |
640 |
*cp = '\0'; |
641 |
|
642 |
if (ignoredups && !strcmp(c, *histptr) |
643 |
#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY |
644 |
&& !histsync() |
645 |
#endif |
646 |
) { |
647 |
afree(c, APERM); |
648 |
return; |
649 |
} |
650 |
++*lnp; |
651 |
|
652 |
#if HAVE_PERSISTENT_HISTORY |
653 |
if (dowrite && histfd != -1) |
654 |
writehistfile(*lnp, c); |
655 |
#endif |
656 |
|
657 |
hp = histptr; |
658 |
|
659 |
if (++hp >= history + histsize) { |
660 |
/* remove oldest command */ |
661 |
afree(*history, APERM); |
662 |
for (hp = history; hp < history + histsize - 1; hp++) |
663 |
hp[0] = hp[1]; |
664 |
} |
665 |
*hp = c; |
666 |
histptr = hp; |
667 |
} |
668 |
|
669 |
/* |
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. |
675 |
* |
676 |
* This stuff uses mmap() |
677 |
* |
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 |
681 |
*/ |
682 |
|
683 |
/*- |
684 |
* Open a history file |
685 |
* Format is: |
686 |
* Bytes 1, 2: |
687 |
* HMAGIC - just to check that we are dealing with the correct object |
688 |
* Then follows a number of stored commands |
689 |
* Each command is |
690 |
* <command byte><command number(4 octets, big endian)><bytes><NUL> |
691 |
*/ |
692 |
#define HMAGIC1 0xAB |
693 |
#define HMAGIC2 0xCD |
694 |
#define COMMAND 0xFF |
695 |
|
696 |
#if HAVE_PERSISTENT_HISTORY |
697 |
static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 }; |
698 |
#endif |
699 |
|
700 |
void |
701 |
hist_init(Source *s) |
702 |
{ |
703 |
#if HAVE_PERSISTENT_HISTORY |
704 |
unsigned char *base; |
705 |
int lines, fd; |
706 |
enum { hist_init_first, hist_init_retry, hist_init_restore } hs; |
707 |
#endif |
708 |
|
709 |
if (Flag(FTALKING) == 0) |
710 |
return; |
711 |
|
712 |
hstarted = true; |
713 |
hist_source = s; |
714 |
|
715 |
#if HAVE_PERSISTENT_HISTORY |
716 |
if ((hname = str_val(global("HISTFILE"))) == NULL) |
717 |
return; |
718 |
strdupx(hname, hname, APERM); |
719 |
hs = hist_init_first; |
720 |
|
721 |
retry: |
722 |
/* we have a file and are interactive */ |
723 |
if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND | O_BINARY, |
724 |
0600)) < 0) |
725 |
return; |
726 |
|
727 |
histfd = savefd(fd); |
728 |
if (histfd != fd) |
729 |
close(fd); |
730 |
|
731 |
mksh_lockfd(histfd); |
732 |
|
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 */ |
737 |
goto hist_init_tail; |
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) |
743 |
goto hist_init_fail; |
744 |
if (base[0] != HMAGIC1 || base[1] != HMAGIC2) { |
745 |
munmap(caddr_cast(base), (size_t)histfsize); |
746 |
goto hist_init_fail; |
747 |
} |
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 */ |
754 |
char *nhname, **hp; |
755 |
struct stat sb; |
756 |
|
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; |
763 |
} |
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; |
768 |
} |
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 */ |
773 |
hp = history; |
774 |
while (hp < histptr) { |
775 |
if (!writehistline(fd, |
776 |
s->line - (histptr - hp), *hp)) |
777 |
goto hist_trunc_abort; |
778 |
++hp; |
779 |
} |
780 |
/* now unlock, close both, rename, rinse, repeat */ |
781 |
close(fd); |
782 |
fd = -1; |
783 |
hist_finish(); |
784 |
if (rename(nhname, hname) < 0) { |
785 |
hist_trunc_abort: |
786 |
if (fd != -1) |
787 |
close(fd); |
788 |
unlink(nhname); |
789 |
if (fd != -1) |
790 |
goto hist_trunc_dont; |
791 |
/* darn! restore histfd and pray */ |
792 |
} |
793 |
hs = hist_init_restore; |
794 |
hist_trunc_dont: |
795 |
afree(nhname, ATEMP); |
796 |
if (hs == hist_init_restore) |
797 |
goto retry; |
798 |
} |
799 |
} else if (histfsize != 0) { |
800 |
/* negative or too small... */ |
801 |
hist_init_fail: |
802 |
/* ... or mmap failed or illegal */ |
803 |
hist_finish(); |
804 |
/* nuke the bogus file then retry, at most once */ |
805 |
if (!unlink(hname) && hs != hist_init_retry) { |
806 |
hs = hist_init_retry; |
807 |
goto retry; |
808 |
} |
809 |
if (hs != hist_init_retry) |
810 |
bi_errorf("can't %s %s: %s", |
811 |
"unlink HISTFILE", hname, cstrerror(errno)); |
812 |
histfsize = 0; |
813 |
return; |
814 |
} else { |
815 |
/* size 0, add magic to the history file */ |
816 |
if (write(histfd, sprinkle, 2) != 2) { |
817 |
hist_finish(); |
818 |
return; |
819 |
} |
820 |
} |
821 |
histfsize = lseek(histfd, (off_t)0, SEEK_END); |
822 |
hist_init_tail: |
823 |
mksh_unlkfd(histfd); |
824 |
#endif |
825 |
} |
826 |
|
827 |
#if HAVE_PERSISTENT_HISTORY |
828 |
/* |
829 |
* load the history structure from the stored data |
830 |
*/ |
831 |
static int |
832 |
histload(Source *s, unsigned char *base, size_t bytes) |
833 |
{ |
834 |
int lno = 0, lines = 0; |
835 |
unsigned char *cp; |
836 |
|
837 |
histload_loop: |
838 |
/* !bytes check as some systems (older FreeBSDs) have buggy memchr */ |
839 |
if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL) |
840 |
return (lines); |
841 |
/* advance base pointer past COMMAND byte */ |
842 |
bytes -= ++cp - base; |
843 |
base = cp; |
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) |
846 |
return (lines); |
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]) */ |
851 |
++lines; |
852 |
if (histptr >= history && lno - 1 != s->line) { |
853 |
/* a replacement? */ |
854 |
char **hp; |
855 |
|
856 |
if (lno >= s->line - (histptr - history) && lno <= s->line) { |
857 |
hp = &histptr[lno - s->line]; |
858 |
if (*hp) |
859 |
afree(*hp, APERM); |
860 |
strdupx(*hp, (char *)(base + 4), APERM); |
861 |
} |
862 |
} else { |
863 |
s->line = lno--; |
864 |
histsave(&lno, (char *)(base + 4), false, false); |
865 |
} |
866 |
/* advance base pointer past NUL */ |
867 |
bytes -= ++cp - base; |
868 |
base = cp; |
869 |
/* repeat until no more */ |
870 |
goto histload_loop; |
871 |
} |
872 |
|
873 |
/* |
874 |
* write a command to the end of the history file |
875 |
* |
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 |
880 |
*/ |
881 |
static void |
882 |
writehistfile(int lno, const char *cmd) |
883 |
{ |
884 |
off_t sizenow; |
885 |
size_t bytes; |
886 |
unsigned char *base, *news; |
887 |
|
888 |
mksh_lockfd(histfd); |
889 |
sizenow = lseek(histfd, (off_t)0, SEEK_END); |
890 |
if (sizenow < histfsize) { |
891 |
/* the file has shrunk; give up */ |
892 |
goto bad; |
893 |
} |
894 |
if ( |
895 |
/* ignore changes when the file is too large */ |
896 |
sizenow <= MKSH_MAXHISTFSIZE |
897 |
&& |
898 |
/* the size has changed, we need to do read updates */ |
899 |
sizenow > histfsize |
900 |
) { |
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) |
906 |
goto bad; |
907 |
news = base + (size_t)histfsize; |
908 |
if (*news == COMMAND) { |
909 |
hist_source->line--; |
910 |
histload(hist_source, news, bytes); |
911 |
hist_source->line++; |
912 |
lno = hist_source->line; |
913 |
} else |
914 |
bytes = 0; |
915 |
munmap(caddr_cast(base), (size_t)sizenow); |
916 |
if (!bytes) |
917 |
goto bad; |
918 |
} |
919 |
if (cmd && !writehistline(histfd, lno, cmd)) { |
920 |
bad: |
921 |
hist_finish(); |
922 |
return; |
923 |
} |
924 |
histfsize = lseek(histfd, (off_t)0, SEEK_END); |
925 |
mksh_unlkfd(histfd); |
926 |
} |
927 |
|
928 |
static int |
929 |
writehistline(int fd, int lno, const char *cmd) |
930 |
{ |
931 |
ssize_t n; |
932 |
unsigned char hdr[5]; |
933 |
|
934 |
hdr[0] = COMMAND; |
935 |
hdr[1] = (lno >> 24) & 0xFF; |
936 |
hdr[2] = (lno >> 16) & 0xFF; |
937 |
hdr[3] = (lno >> 8) & 0xFF; |
938 |
hdr[4] = lno & 0xFF; |
939 |
n = strlen(cmd) + 1; |
940 |
return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n); |
941 |
} |
942 |
|
943 |
void |
944 |
hist_finish(void) |
945 |
{ |
946 |
if (histfd >= 0) { |
947 |
mksh_unlkfd(histfd); |
948 |
(void)close(histfd); |
949 |
} |
950 |
histfd = -1; |
951 |
} |
952 |
#endif |
953 |
|
954 |
|
955 |
#if !HAVE_SYS_SIGNAME |
956 |
static const struct mksh_sigpair { |
957 |
const char * const name; |
958 |
int nr; |
959 |
} mksh_sigpairs[] = { |
960 |
#include "signames.inc" |
961 |
{ NULL, 0 } |
962 |
}; |
963 |
#endif |
964 |
|
965 |
#if HAVE_SYS_SIGLIST |
966 |
#if !HAVE_SYS_SIGLIST_DECL |
967 |
extern const char * const sys_siglist[]; |
968 |
#endif |
969 |
#endif |
970 |
|
971 |
void |
972 |
inittraps(void) |
973 |
{ |
974 |
int i; |
975 |
const char *cs; |
976 |
|
977 |
trap_exstat = -1; |
978 |
|
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"; |
986 |
} else { |
987 |
#if HAVE_SYS_SIGNAME |
988 |
cs = sys_signame[i]; |
989 |
#else |
990 |
const struct mksh_sigpair *pair = mksh_sigpairs; |
991 |
while ((pair->nr != i) && (pair->name != NULL)) |
992 |
++pair; |
993 |
cs = pair->name; |
994 |
#endif |
995 |
if ((cs == NULL) || |
996 |
(cs[0] == '\0')) |
997 |
sigtraps[i].name = shf_smprintf("%d", i); |
998 |
else { |
999 |
char *s; |
1000 |
|
1001 |
/* this is not optimal, what about SIGSIG1? */ |
1002 |
if ((cs[0] & 0xDF) == 'S' && |
1003 |
(cs[1] & 0xDF) == 'I' && |
1004 |
(cs[2] & 0xDF) == 'G' && |
1005 |
cs[3] != '\0') { |
1006 |
/* skip leading "SIG" */ |
1007 |
cs += 3; |
1008 |
} |
1009 |
strdupx(s, cs, APERM); |
1010 |
sigtraps[i].name = s; |
1011 |
while ((*s = ksh_toupper(*s))) |
1012 |
++s; |
1013 |
} |
1014 |
#if HAVE_SYS_SIGLIST |
1015 |
sigtraps[i].mess = sys_siglist[i]; |
1016 |
#elif HAVE_STRSIGNAL |
1017 |
sigtraps[i].mess = strsignal(i); |
1018 |
#else |
1019 |
sigtraps[i].mess = NULL; |
1020 |
#endif |
1021 |
if ((sigtraps[i].mess == NULL) || |
1022 |
(sigtraps[i].mess[0] == '\0')) |
1023 |
sigtraps[i].mess = shf_smprintf("%s %d", |
1024 |
"Signal", i); |
1025 |
} |
1026 |
} |
1027 |
/* our name for signal 0 */ |
1028 |
sigtraps[ksh_SIGEXIT].name = "EXIT"; |
1029 |
|
1030 |
(void)sigemptyset(&Sigact_ign.sa_mask); |
1031 |
Sigact_ign.sa_flags = 0; /* interruptible */ |
1032 |
Sigact_ign.sa_handler = SIG_IGN; |
1033 |
|
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; |
1040 |
|
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); |
1046 |
} |
1047 |
|
1048 |
static void alarm_catcher(int sig); |
1049 |
|
1050 |
void |
1051 |
alarm_init(void) |
1052 |
{ |
1053 |
sigtraps[SIGALRM].flags |= TF_SHELL_USES; |
1054 |
setsig(&sigtraps[SIGALRM], alarm_catcher, |
1055 |
SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); |
1056 |
} |
1057 |
|
1058 |
/* ARGSUSED */ |
1059 |
static void |
1060 |
alarm_catcher(int sig MKSH_A_UNUSED) |
1061 |
{ |
1062 |
/* this runs inside interrupt context, with errno saved */ |
1063 |
|
1064 |
if (ksh_tmout_state == TMOUT_READING) { |
1065 |
int left = alarm(0); |
1066 |
|
1067 |
if (left == 0) { |
1068 |
ksh_tmout_state = TMOUT_LEAVING; |
1069 |
intrsig = 1; |
1070 |
} else |
1071 |
alarm(left); |
1072 |
} |
1073 |
} |
1074 |
|
1075 |
Trap * |
1076 |
gettrap(const char *cs, bool igncase) |
1077 |
{ |
1078 |
int i; |
1079 |
Trap *p; |
1080 |
char *as; |
1081 |
|
1082 |
if (ksh_isdigit(*cs)) { |
1083 |
return ((getn(cs, &i) && 0 <= i && i < NSIG) ? |
1084 |
(&sigtraps[i]) : NULL); |
1085 |
} |
1086 |
|
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' && |
1091 |
cs[3] != '\0') { |
1092 |
/* skip leading "SIG" */ |
1093 |
cs += 3; |
1094 |
} |
1095 |
if (igncase) { |
1096 |
char *s; |
1097 |
|
1098 |
strdupx(as, cs, ATEMP); |
1099 |
cs = s = as; |
1100 |
while ((*s = ksh_toupper(*s))) |
1101 |
++s; |
1102 |
} else |
1103 |
as = NULL; |
1104 |
|
1105 |
p = sigtraps; |
1106 |
for (i = 0; i <= NSIG; i++) { |
1107 |
if (!strcmp(p->name, cs)) |
1108 |
goto found; |
1109 |
++p; |
1110 |
} |
1111 |
p = NULL; |
1112 |
found: |
1113 |
afree(as, ATEMP); |
1114 |
return (p); |
1115 |
} |
1116 |
|
1117 |
/* |
1118 |
* trap signal handler |
1119 |
*/ |
1120 |
void |
1121 |
trapsig(int i) |
1122 |
{ |
1123 |
Trap *p = &sigtraps[i]; |
1124 |
int eno = errno; |
1125 |
|
1126 |
trap = p->set = 1; |
1127 |
if (p->flags & TF_DFL_INTR) |
1128 |
intrsig = 1; |
1129 |
if ((p->flags & TF_FATAL) && !p->trap) { |
1130 |
fatal_trap = 1; |
1131 |
intrsig = 1; |
1132 |
} |
1133 |
if (p->shtrap) |
1134 |
(*p->shtrap)(i); |
1135 |
errno = eno; |
1136 |
} |
1137 |
|
1138 |
/* |
1139 |
* called when we want to allow the user to ^C out of something - won't |
1140 |
* work if user has trapped SIGINT. |
1141 |
*/ |
1142 |
void |
1143 |
intrcheck(void) |
1144 |
{ |
1145 |
if (intrsig) |
1146 |
runtraps(TF_DFL_INTR|TF_FATAL); |
1147 |
} |
1148 |
|
1149 |
/* |
1150 |
* called after EINTR to check if a signal with normally causes process |
1151 |
* termination has been received. |
1152 |
*/ |
1153 |
int |
1154 |
fatal_trap_check(void) |
1155 |
{ |
1156 |
int i; |
1157 |
Trap *p; |
1158 |
|
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); |
1164 |
return (0); |
1165 |
} |
1166 |
|
1167 |
/* |
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 |
1170 |
* is set. |
1171 |
*/ |
1172 |
int |
1173 |
trap_pending(void) |
1174 |
{ |
1175 |
int i; |
1176 |
Trap *p; |
1177 |
|
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))) |
1181 |
return (p->signal); |
1182 |
return (0); |
1183 |
} |
1184 |
|
1185 |
/* |
1186 |
* run any pending traps. If intr is set, only run traps that |
1187 |
* can interrupt commands. |
1188 |
*/ |
1189 |
void |
1190 |
runtraps(int flag) |
1191 |
{ |
1192 |
int i; |
1193 |
Trap *p; |
1194 |
|
1195 |
if (ksh_tmout_state == TMOUT_LEAVING) { |
1196 |
ksh_tmout_state = TMOUT_EXECUTING; |
1197 |
warningf(false, "timed out waiting for input"); |
1198 |
unwind(LEXIT); |
1199 |
} else |
1200 |
/* |
1201 |
* XXX: this means the alarm will have no effect if a trap |
1202 |
* is caught after the alarm() was started...not good. |
1203 |
*/ |
1204 |
ksh_tmout_state = TMOUT_EXECUTING; |
1205 |
if (!flag) |
1206 |
trap = 0; |
1207 |
if (flag & TF_DFL_INTR) |
1208 |
intrsig = 0; |
1209 |
if (flag & TF_FATAL) |
1210 |
fatal_trap = 0; |
1211 |
++trap_nested; |
1212 |
for (p = sigtraps, i = NSIG+1; --i >= 0; p++) |
1213 |
if (p->set && (!flag || |
1214 |
((p->flags & flag) && p->trap == NULL))) |
1215 |
runtrap(p, false); |
1216 |
if (!--trap_nested) |
1217 |
runtrap(NULL, true); |
1218 |
} |
1219 |
|
1220 |
void |
1221 |
runtrap(Trap *p, bool is_last) |
1222 |
{ |
1223 |
int old_changed = 0, i; |
1224 |
char *trapstr; |
1225 |
|
1226 |
if (p == NULL) |
1227 |
/* just clean up, see runtraps() above */ |
1228 |
goto donetrap; |
1229 |
i = p->signal; |
1230 |
trapstr = p->trap; |
1231 |
p->set = 0; |
1232 |
if (trapstr == NULL) { |
1233 |
/* SIG_DFL */ |
1234 |
if (p->flags & TF_FATAL) { |
1235 |
/* eg, SIGHUP */ |
1236 |
exstat = (int)ksh_min(128U + (unsigned)i, 255U); |
1237 |
unwind(LLEAVE); |
1238 |
} |
1239 |
if (p->flags & TF_DFL_INTR) { |
1240 |
/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ |
1241 |
exstat = (int)ksh_min(128U + (unsigned)i, 255U); |
1242 |
unwind(LINTR); |
1243 |
} |
1244 |
goto donetrap; |
1245 |
} |
1246 |
if (trapstr[0] == '\0') |
1247 |
/* SIG_IGN */ |
1248 |
goto donetrap; |
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; |
1253 |
p->trap = NULL; |
1254 |
} |
1255 |
if (trap_exstat == -1) |
1256 |
trap_exstat = exstat & 0xFF; |
1257 |
/* |
1258 |
* Note: trapstr is fully parsed before anything is executed, thus |
1259 |
* no problem with afree(p->trap) in settrap() while still in use. |
1260 |
*/ |
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); |
1266 |
else |
1267 |
p->trap = trapstr; |
1268 |
p->flags |= old_changed; |
1269 |
} |
1270 |
|
1271 |
donetrap: |
1272 |
/* we're the last trap of a sequence executed */ |
1273 |
if (is_last && trap_exstat != -1) { |
1274 |
exstat = trap_exstat; |
1275 |
trap_exstat = -1; |
1276 |
} |
1277 |
} |
1278 |
|
1279 |
/* clear pending traps and reset user's trap handlers; used after fork(2) */ |
1280 |
void |
1281 |
cleartraps(void) |
1282 |
{ |
1283 |
int i; |
1284 |
Trap *p; |
1285 |
|
1286 |
trap = 0; |
1287 |
intrsig = 0; |
1288 |
fatal_trap = 0; |
1289 |
for (i = NSIG+1, p = sigtraps; --i >= 0; p++) { |
1290 |
p->set = 0; |
1291 |
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) |
1292 |
settrap(p, NULL); |
1293 |
} |
1294 |
} |
1295 |
|
1296 |
/* restore signals just before an exec(2) */ |
1297 |
void |
1298 |
restoresigs(void) |
1299 |
{ |
1300 |
int i; |
1301 |
Trap *p; |
1302 |
|
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); |
1307 |
} |
1308 |
|
1309 |
void |
1310 |
settrap(Trap *p, const char *s) |
1311 |
{ |
1312 |
sig_t f; |
1313 |
|
1314 |
if (p->trap) |
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; |
1320 |
|
1321 |
p->flags |= TF_USER_SET; |
1322 |
if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) |
1323 |
f = trapsig; |
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); |
1328 |
if (f == SIG_IGN) |
1329 |
p->flags |= TF_EXEC_IGN; |
1330 |
else |
1331 |
p->flags |= TF_EXEC_DFL; |
1332 |
} |
1333 |
|
1334 |
/* |
1335 |
* assumes handler already set to what shell wants it |
1336 |
* (normally trapsig, but could be j_sigchld() or SIG_IGN) |
1337 |
*/ |
1338 |
return; |
1339 |
} |
1340 |
|
1341 |
/* todo: should we let user know signal is ignored? how? */ |
1342 |
setsig(p, f, SS_RESTORE_CURR|SS_USER); |
1343 |
} |
1344 |
|
1345 |
/* |
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) |
1348 |
*/ |
1349 |
int |
1350 |
block_pipe(void) |
1351 |
{ |
1352 |
int restore_dfl = 0; |
1353 |
Trap *p = &sigtraps[SIGPIPE]; |
1354 |
|
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) |
1358 |
restore_dfl = 1; |
1359 |
} else if (p->cursig == SIG_DFL) { |
1360 |
setsig(p, SIG_IGN, SS_RESTORE_CURR); |
1361 |
/* restore to SIG_DFL */ |
1362 |
restore_dfl = 1; |
1363 |
} |
1364 |
return (restore_dfl); |
1365 |
} |
1366 |
|
1367 |
/* Called by c_print() to undo whatever block_pipe() did */ |
1368 |
void |
1369 |
restore_pipe(int restore_dfl) |
1370 |
{ |
1371 |
if (restore_dfl) |
1372 |
setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); |
1373 |
} |
1374 |
|
1375 |
/* |
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. |
1378 |
*/ |
1379 |
int |
1380 |
setsig(Trap *p, sig_t f, int flags) |
1381 |
{ |
1382 |
struct sigaction sigact; |
1383 |
|
1384 |
if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) |
1385 |
return (1); |
1386 |
|
1387 |
memset(&sigact, 0, sizeof(sigact)); |
1388 |
|
1389 |
/* |
1390 |
* First time setting this signal? If so, get and note the current |
1391 |
* setting. |
1392 |
*/ |
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; |
1398 |
} |
1399 |
|
1400 |
/*- |
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 |
1404 |
*/ |
1405 |
if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && |
1406 |
(!(flags & SS_USER) || !Flag(FTALKING))) |
1407 |
return (0); |
1408 |
|
1409 |
setexecsig(p, flags & SS_RESTORE_MASK); |
1410 |
|
1411 |
/* |
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). |
1416 |
*/ |
1417 |
if (!(flags & SS_USER)) |
1418 |
p->shtrap = (sig_t)NULL; |
1419 |
if (flags & SS_SHTRAP) { |
1420 |
p->shtrap = f; |
1421 |
f = trapsig; |
1422 |
} |
1423 |
|
1424 |
if (p->cursig != f) { |
1425 |
p->cursig = f; |
1426 |
(void)sigemptyset(&sigact.sa_mask); |
1427 |
/* interruptible */ |
1428 |
sigact.sa_flags = 0; |
1429 |
sigact.sa_handler = f; |
1430 |
sigaction(p->signal, &sigact, NULL); |
1431 |
} |
1432 |
|
1433 |
return (1); |
1434 |
} |
1435 |
|
1436 |
/* control what signal is set to before an exec() */ |
1437 |
void |
1438 |
setexecsig(Trap *p, int restore) |
1439 |
{ |
1440 |
/* XXX debugging */ |
1441 |
if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) |
1442 |
internal_errorf("setexecsig: unset signal %d(%s)", |
1443 |
p->signal, p->name); |
1444 |
|
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 */ |
1450 |
break; |
1451 |
case SS_RESTORE_ORIG: |
1452 |
p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; |
1453 |
break; |
1454 |
case SS_RESTORE_DFL: |
1455 |
p->flags |= TF_EXEC_DFL; |
1456 |
break; |
1457 |
case SS_RESTORE_IGN: |
1458 |
p->flags |= TF_EXEC_IGN; |
1459 |
break; |
1460 |
} |
1461 |
} |
1462 |
|
1463 |
#if HAVE_PERSISTENT_HISTORY || defined(DF) |
1464 |
/* |
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... |
1469 |
*/ |
1470 |
|
1471 |
void |
1472 |
mksh_lockfd(int fd) |
1473 |
{ |
1474 |
#if defined(__OpenBSD__) |
1475 |
/* flock is not interrupted by signals */ |
1476 |
(void)flock(fd, LOCK_EX); |
1477 |
#elif HAVE_FLOCK |
1478 |
int rv; |
1479 |
|
1480 |
/* e.g. on Linux */ |
1481 |
do { |
1482 |
rv = flock(fd, LOCK_EX); |
1483 |
} while (rv == 1 && errno == EINTR); |
1484 |
#elif HAVE_LOCK_FCNTL |
1485 |
int rv; |
1486 |
struct flock lks; |
1487 |
|
1488 |
memset(&lks, 0, sizeof(lks)); |
1489 |
lks.l_type = F_WRLCK; |
1490 |
do { |
1491 |
rv = fcntl(fd, F_SETLKW, &lks); |
1492 |
} while (rv == 1 && errno == EINTR); |
1493 |
#endif |
1494 |
} |
1495 |
|
1496 |
/* designed to not define mksh_unlkfd if none triggered */ |
1497 |
#if HAVE_FLOCK |
1498 |
void |
1499 |
mksh_unlkfd(int fd) |
1500 |
{ |
1501 |
(void)flock(fd, LOCK_UN); |
1502 |
} |
1503 |
#elif HAVE_LOCK_FCNTL |
1504 |
void |
1505 |
mksh_unlkfd(int fd) |
1506 |
{ |
1507 |
struct flock lks; |
1508 |
|
1509 |
memset(&lks, 0, sizeof(lks)); |
1510 |
lks.l_type = F_UNLCK; |
1511 |
(void)fcntl(fd, F_SETLKW, &lks); |
1512 |
} |
1513 |
#endif |
1514 |
#endif |