GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: main.c,v 1.83 2017/08/11 23:10:55 guenther Exp $ */ |
||
2 |
|||
3 |
/* |
||
4 |
* startup, main loop, environments and error handling |
||
5 |
*/ |
||
6 |
|||
7 |
#include <sys/stat.h> |
||
8 |
|||
9 |
#include <errno.h> |
||
10 |
#include <fcntl.h> |
||
11 |
#include <paths.h> |
||
12 |
#include <pwd.h> |
||
13 |
#include <stdio.h> |
||
14 |
#include <stdlib.h> |
||
15 |
#include <string.h> |
||
16 |
#include <unistd.h> |
||
17 |
|||
18 |
#include "sh.h" |
||
19 |
|||
20 |
extern char **environ; |
||
21 |
|||
22 |
/* |
||
23 |
* global data |
||
24 |
*/ |
||
25 |
|||
26 |
static void reclaim(void); |
||
27 |
static void remove_temps(struct temp *tp); |
||
28 |
static int is_restricted(char *name); |
||
29 |
static void init_username(void); |
||
30 |
|||
31 |
const char *kshname; |
||
32 |
pid_t kshpid; |
||
33 |
pid_t procpid; |
||
34 |
uid_t ksheuid; |
||
35 |
int exstat; |
||
36 |
int subst_exstat; |
||
37 |
const char *safe_prompt; |
||
38 |
|||
39 |
Area aperm; |
||
40 |
|||
41 |
struct env *genv; |
||
42 |
|||
43 |
char shell_flags[FNFLAGS]; |
||
44 |
|||
45 |
char null[] = ""; |
||
46 |
|||
47 |
int shl_stdout_ok; |
||
48 |
|||
49 |
unsigned int ksh_tmout; |
||
50 |
enum tmout_enum ksh_tmout_state = TMOUT_EXECUTING; |
||
51 |
|||
52 |
int really_exit; |
||
53 |
|||
54 |
int ifs0 = ' '; |
||
55 |
|||
56 |
volatile sig_atomic_t trap; |
||
57 |
volatile sig_atomic_t intrsig; |
||
58 |
volatile sig_atomic_t fatal_trap; |
||
59 |
|||
60 |
Getopt builtin_opt; |
||
61 |
Getopt user_opt; |
||
62 |
|||
63 |
struct coproc coproc; |
||
64 |
sigset_t sm_default, sm_sigchld; |
||
65 |
|||
66 |
char *builtin_argv0; |
||
67 |
int builtin_flag; |
||
68 |
|||
69 |
char *current_wd; |
||
70 |
int current_wd_size; |
||
71 |
|||
72 |
#ifdef EDIT |
||
73 |
int x_cols = 80; |
||
74 |
#endif /* EDIT */ |
||
75 |
|||
76 |
/* |
||
77 |
* shell initialization |
||
78 |
*/ |
||
79 |
|||
80 |
static const char initifs[] = "IFS= \t\n"; |
||
81 |
|||
82 |
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }"; |
||
83 |
|||
84 |
static const char *initcoms [] = { |
||
85 |
"typeset", "-r", "KSH_VERSION", NULL, |
||
86 |
"typeset", "-x", "SHELL", "PATH", "HOME", NULL, |
||
87 |
"typeset", "-ir", "PPID", NULL, |
||
88 |
"typeset", "-i", "OPTIND=1", NULL, |
||
89 |
"eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL, |
||
90 |
"alias", |
||
91 |
/* Standard ksh aliases */ |
||
92 |
"hash=alias -t", /* not "alias -t --": hash -r needs to work */ |
||
93 |
"type=whence -v", |
||
94 |
#ifdef JOBS |
||
95 |
"stop=kill -STOP", |
||
96 |
#endif |
||
97 |
"autoload=typeset -fu", |
||
98 |
"functions=typeset -f", |
||
99 |
#ifdef HISTORY |
||
100 |
"history=fc -l", |
||
101 |
#endif /* HISTORY */ |
||
102 |
"integer=typeset -i", |
||
103 |
"nohup=nohup ", |
||
104 |
"local=typeset", |
||
105 |
"r=fc -s", |
||
106 |
/* Aliases that are builtin commands in at&t */ |
||
107 |
"login=exec login", |
||
108 |
NULL, |
||
109 |
/* this is what at&t ksh seems to track, with the addition of emacs */ |
||
110 |
"alias", "-tU", |
||
111 |
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls", |
||
112 |
"mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who", |
||
113 |
NULL, |
||
114 |
NULL |
||
115 |
}; |
||
116 |
|||
117 |
char username[_PW_NAME_LEN + 1]; |
||
118 |
|||
119 |
#define version_param (initcoms[2]) |
||
120 |
|||
121 |
/* The shell uses its own variation on argv, to build variables like |
||
122 |
* $0 and $@. |
||
123 |
* Allocate a new array since modifying the original argv will modify |
||
124 |
* ps output. |
||
125 |
*/ |
||
126 |
static char ** |
||
127 |
make_argv(int argc, char *argv[]) |
||
128 |
{ |
||
129 |
int i; |
||
130 |
char **nargv; |
||
131 |
|||
132 |
435536 |
nargv = areallocarray(NULL, argc + 1, sizeof(char *), &aperm); |
|
133 |
217768 |
nargv[0] = (char *) kshname; |
|
134 |
✓✓ | 559788 |
for (i = 1; i < argc; i++) |
135 |
62126 |
nargv[i] = argv[i]; |
|
136 |
217768 |
nargv[i] = NULL; |
|
137 |
|||
138 |
217768 |
return nargv; |
|
139 |
} |
||
140 |
|||
141 |
int |
||
142 |
main(int argc, char *argv[]) |
||
143 |
{ |
||
144 |
int i; |
||
145 |
int argi; |
||
146 |
Source *s; |
||
147 |
struct block *l; |
||
148 |
int restricted, errexit; |
||
149 |
char **wp; |
||
150 |
435540 |
struct env env; |
|
151 |
pid_t ppid; |
||
152 |
|||
153 |
217770 |
kshname = argv[0]; |
|
154 |
|||
155 |
✗✓ | 435540 |
if (pledge("stdio rpath wpath cpath fattr flock getpw proc exec tty", |
156 |
217770 |
NULL) == -1) { |
|
157 |
perror("pledge"); |
||
158 |
exit(1); |
||
159 |
} |
||
160 |
|||
161 |
217770 |
ainit(&aperm); /* initialize permanent Area */ |
|
162 |
|||
163 |
/* set up base environment */ |
||
164 |
217770 |
memset(&env, 0, sizeof(env)); |
|
165 |
217770 |
env.type = E_NONE; |
|
166 |
217770 |
ainit(&env.area); |
|
167 |
217770 |
genv = &env; |
|
168 |
217770 |
newblock(); /* set up global l->vars and l->funs */ |
|
169 |
|||
170 |
/* Do this first so output routines (eg, errorf, shellf) can work */ |
||
171 |
217770 |
initio(); |
|
172 |
|||
173 |
217770 |
initvar(); |
|
174 |
|||
175 |
217770 |
initctypes(); |
|
176 |
|||
177 |
217770 |
inittraps(); |
|
178 |
|||
179 |
217770 |
coproc_init(); |
|
180 |
|||
181 |
/* set up variable and command dictionaries */ |
||
182 |
217770 |
ktinit(&taliases, APERM, 0); |
|
183 |
217770 |
ktinit(&aliases, APERM, 0); |
|
184 |
217770 |
ktinit(&homedirs, APERM, 0); |
|
185 |
|||
186 |
/* define shell keywords */ |
||
187 |
217770 |
initkeywords(); |
|
188 |
|||
189 |
/* define built-in commands */ |
||
190 |
217770 |
ktinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */ |
|
191 |
✓✓ | 10452960 |
for (i = 0; shbuiltins[i].name != NULL; i++) |
192 |
5008710 |
builtin(shbuiltins[i].name, shbuiltins[i].func); |
|
193 |
✓✓ | 8710800 |
for (i = 0; kshbuiltins[i].name != NULL; i++) |
194 |
4137630 |
builtin(kshbuiltins[i].name, kshbuiltins[i].func); |
|
195 |
|||
196 |
217770 |
init_histvec(); |
|
197 |
|||
198 |
217770 |
def_path = _PATH_DEFPATH; |
|
199 |
{ |
||
200 |
217770 |
size_t len = confstr(_CS_PATH, NULL, 0); |
|
201 |
char *new; |
||
202 |
|||
203 |
✓✗ | 217770 |
if (len > 0) { |
204 |
217770 |
confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1); |
|
205 |
217770 |
def_path = new; |
|
206 |
217770 |
} |
|
207 |
} |
||
208 |
|||
209 |
/* Set PATH to def_path (will set the path global variable). |
||
210 |
* (import of environment below will probably change this setting). |
||
211 |
*/ |
||
212 |
{ |
||
213 |
217770 |
struct tbl *vp = global("PATH"); |
|
214 |
/* setstr can't fail here */ |
||
215 |
217770 |
setstr(vp, def_path, KSH_RETURN_ERROR); |
|
216 |
} |
||
217 |
|||
218 |
|||
219 |
/* Turn on nohup by default for now - will change to off |
||
220 |
* by default once people are aware of its existence |
||
221 |
* (at&t ksh does not have a nohup option - it always sends |
||
222 |
* the hup). |
||
223 |
*/ |
||
224 |
217770 |
Flag(FNOHUP) = 1; |
|
225 |
|||
226 |
/* Turn on brace expansion by default. At&t ksh's that have |
||
227 |
* alternation always have it on. BUT, posix doesn't have |
||
228 |
* brace expansion, so set this before setting up FPOSIX |
||
229 |
* (change_flag() clears FBRACEEXPAND when FPOSIX is set). |
||
230 |
*/ |
||
231 |
#ifdef BRACE_EXPAND |
||
232 |
217770 |
Flag(FBRACEEXPAND) = 1; |
|
233 |
#endif /* BRACE_EXPAND */ |
||
234 |
|||
235 |
/* set posix flag just before environment so that it will have |
||
236 |
* exactly the same effect as the POSIXLY_CORRECT environment |
||
237 |
* variable. If this needs to be done sooner to ensure correct posix |
||
238 |
* operation, an initial scan of the environment will also have |
||
239 |
* done sooner. |
||
240 |
*/ |
||
241 |
#ifdef POSIXLY_CORRECT |
||
242 |
change_flag(FPOSIX, OF_SPECIAL, 1); |
||
243 |
#endif /* POSIXLY_CORRECT */ |
||
244 |
|||
245 |
/* Check to see if we're /bin/sh. */ |
||
246 |
✓✓✓✓ ✓✓ |
567659 |
if (!strcmp(kshname, "sh") || !strcmp(kshname, "-sh") || |
247 |
✓✗ | 174940 |
(strlen(kshname) >= 3 && |
248 |
174940 |
!strcmp(&kshname[strlen(kshname) - 3], "/sh"))) { |
|
249 |
205939 |
Flag(FSH) = 1; |
|
250 |
205939 |
version_param = "SH_VERSION"; |
|
251 |
205939 |
} |
|
252 |
|||
253 |
/* Set edit mode to emacs by default, may be overridden |
||
254 |
* by the environment or the user. Also, we want tab completion |
||
255 |
* on in vi by default. */ |
||
256 |
#if defined(EDIT) && defined(EMACS) |
||
257 |
217770 |
change_flag(FEMACS, OF_SPECIAL, 1); |
|
258 |
#endif /* EDIT && EMACS */ |
||
259 |
#if defined(EDIT) && defined(VI) |
||
260 |
217770 |
Flag(FVITABCOMPLETE) = 1; |
|
261 |
#endif /* EDIT && VI */ |
||
262 |
|||
263 |
/* import environment */ |
||
264 |
✓✗ | 217770 |
if (environ != NULL) |
265 |
✓✓ | 10719066 |
for (wp = environ; *wp != NULL; wp++) |
266 |
5141763 |
typeset(*wp, IMPORT|EXPORT, 0, 0, 0); |
|
267 |
|||
268 |
217770 |
kshpid = procpid = getpid(); |
|
269 |
217770 |
typeset(initifs, 0, 0, 0, 0); /* for security */ |
|
270 |
|||
271 |
/* assign default shell variable values */ |
||
272 |
217770 |
substitute(initsubs, 0); |
|
273 |
|||
274 |
/* Figure out the current working directory and set $PWD */ |
||
275 |
{ |
||
276 |
217770 |
struct stat s_pwd, s_dot; |
|
277 |
217770 |
struct tbl *pwd_v = global("PWD"); |
|
278 |
217770 |
char *pwd = str_val(pwd_v); |
|
279 |
char *pwdx = pwd; |
||
280 |
|||
281 |
/* Try to use existing $PWD if it is valid */ |
||
282 |
✓✓✗✓ |
426814 |
if (pwd[0] != '/' || |
283 |
✓✗✓✗ |
418090 |
stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 || |
284 |
✓✓ | 209045 |
s_pwd.st_dev != s_dot.st_dev || |
285 |
209044 |
s_pwd.st_ino != s_dot.st_ino) |
|
286 |
8726 |
pwdx = NULL; |
|
287 |
217770 |
set_current_wd(pwdx); |
|
288 |
✓✗ | 217770 |
if (current_wd[0]) |
289 |
217770 |
simplify_path(current_wd); |
|
290 |
/* Only set pwd if we know where we are or if it had a |
||
291 |
* bogus value |
||
292 |
*/ |
||
293 |
✓✗ | 217770 |
if (current_wd[0] || pwd != null) |
294 |
/* setstr can't fail here */ |
||
295 |
217770 |
setstr(pwd_v, current_wd, KSH_RETURN_ERROR); |
|
296 |
217770 |
} |
|
297 |
217770 |
ppid = getppid(); |
|
298 |
217770 |
setint(global("PPID"), (long) ppid); |
|
299 |
/* setstr can't fail here */ |
||
300 |
217770 |
setstr(global(version_param), ksh_version, KSH_RETURN_ERROR); |
|
301 |
|||
302 |
/* execute initialization statements */ |
||
303 |
✓✓ | 1959930 |
for (wp = (char**) initcoms; *wp != NULL; wp++) { |
304 |
1524390 |
shcomexec(wp); |
|
305 |
✓✓ | 13501740 |
for (; *wp != NULL; wp++) |
306 |
; |
||
307 |
} |
||
308 |
|||
309 |
|||
310 |
217770 |
ksheuid = geteuid(); |
|
311 |
217770 |
init_username(); |
|
312 |
217770 |
safe_prompt = ksheuid ? "$ " : "# "; |
|
313 |
{ |
||
314 |
217770 |
struct tbl *vp = global("PS1"); |
|
315 |
|||
316 |
/* Set PS1 if it isn't set */ |
||
317 |
✓✓ | 217770 |
if (!(vp->flag & ISSET)) { |
318 |
/* setstr can't fail here */ |
||
319 |
215045 |
setstr(vp, safe_prompt, KSH_RETURN_ERROR); |
|
320 |
215045 |
} |
|
321 |
} |
||
322 |
|||
323 |
/* Set this before parsing arguments */ |
||
324 |
✓✗ | 653310 |
Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid(); |
325 |
|||
326 |
/* this to note if monitor is set on command line (see below) */ |
||
327 |
217770 |
Flag(FMONITOR) = 127; |
|
328 |
217770 |
argi = parse_args(argv, OF_CMDLINE, NULL); |
|
329 |
✗✓ | 217770 |
if (argi < 0) |
330 |
exit(1); |
||
331 |
|||
332 |
✓✓ | 217770 |
if (Flag(FCOMMAND)) { |
333 |
175497 |
s = pushs(SSTRING, ATEMP); |
|
334 |
✗✓ | 175497 |
if (!(s->start = s->str = argv[argi++])) |
335 |
errorf("-c requires an argument"); |
||
336 |
✓✓ | 175497 |
if (argv[argi]) |
337 |
1 |
kshname = argv[argi++]; |
|
338 |
✓✓ | 42273 |
} else if (argi < argc && !Flag(FSTDIN)) { |
339 |
31418 |
s = pushs(SFILE, ATEMP); |
|
340 |
31418 |
s->file = argv[argi++]; |
|
341 |
31418 |
s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); |
|
342 |
✓✓ | 31418 |
if (s->u.shf == NULL) { |
343 |
exstat = 127; /* POSIX */ |
||
344 |
errorf("%s: %s", s->file, strerror(errno)); |
||
345 |
} |
||
346 |
31416 |
kshname = s->file; |
|
347 |
31416 |
} else { |
|
348 |
10855 |
Flag(FSTDIN) = 1; |
|
349 |
10855 |
s = pushs(SSTDIN, ATEMP); |
|
350 |
10855 |
s->file = "<stdin>"; |
|
351 |
10855 |
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0), NULL); |
|
352 |
✓✓✓✗ |
13604 |
if (isatty(0) && isatty(2)) { |
353 |
2749 |
Flag(FTALKING) = Flag(FTALKING_I) = 1; |
|
354 |
/* The following only if isatty(0) */ |
||
355 |
2749 |
s->flags |= SF_TTY; |
|
356 |
2749 |
s->u.shf->flags |= SHF_INTERRUPT; |
|
357 |
2749 |
s->file = NULL; |
|
358 |
2749 |
} |
|
359 |
} |
||
360 |
|||
361 |
/* This bizarreness is mandated by POSIX */ |
||
362 |
{ |
||
363 |
217768 |
struct stat s_stdin; |
|
364 |
|||
365 |
✓✗✓✓ ✓✓ |
644465 |
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) && |
366 |
208929 |
Flag(FTALKING)) |
|
367 |
2749 |
reset_nonblock(0); |
|
368 |
217768 |
} |
|
369 |
|||
370 |
/* initialize job control */ |
||
371 |
217768 |
i = Flag(FMONITOR) != 127; |
|
372 |
217768 |
Flag(FMONITOR) = 0; |
|
373 |
217768 |
j_init(i); |
|
374 |
#ifdef EDIT |
||
375 |
/* Do this after j_init(), as tty_fd is not initialized 'til then */ |
||
376 |
✓✓ | 217768 |
if (Flag(FTALKING)) |
377 |
3624 |
x_init(); |
|
378 |
#endif |
||
379 |
|||
380 |
217768 |
l = genv->loc; |
|
381 |
217768 |
l->argv = make_argv(argc - (argi - 1), &argv[argi - 1]); |
|
382 |
217768 |
l->argc = argc - argi; |
|
383 |
217768 |
getopts_reset(1); |
|
384 |
|||
385 |
/* Disable during .profile/ENV reading */ |
||
386 |
217768 |
restricted = Flag(FRESTRICTED); |
|
387 |
217768 |
Flag(FRESTRICTED) = 0; |
|
388 |
217768 |
errexit = Flag(FERREXIT); |
|
389 |
217768 |
Flag(FERREXIT) = 0; |
|
390 |
|||
391 |
/* Do this before profile/$ENV so that if it causes problems in them, |
||
392 |
* user will know why things broke. |
||
393 |
*/ |
||
394 |
✗✓✗✗ |
217768 |
if (!current_wd[0] && Flag(FTALKING)) |
395 |
warningf(false, "Cannot determine current working directory"); |
||
396 |
|||
397 |
✓✓ | 217768 |
if (Flag(FLOGIN)) { |
398 |
33 |
include(KSH_SYSTEM_PROFILE, 0, NULL, 1); |
|
399 |
✓✗ | 33 |
if (!Flag(FPRIVILEGED)) |
400 |
33 |
include(substitute("$HOME/.profile", 0), 0, NULL, 1); |
|
401 |
} |
||
402 |
|||
403 |
✗✓ | 217768 |
if (Flag(FPRIVILEGED)) |
404 |
include("/etc/suid_profile", 0, NULL, 1); |
||
405 |
✓✓ | 217768 |
else if (Flag(FTALKING)) { |
406 |
char *env_file; |
||
407 |
|||
408 |
/* include $ENV */ |
||
409 |
3624 |
env_file = str_val(global("ENV")); |
|
410 |
|||
411 |
#ifdef DEFAULT_ENV |
||
412 |
/* If env isn't set, include default environment */ |
||
413 |
if (env_file == null) |
||
414 |
env_file = DEFAULT_ENV; |
||
415 |
#endif /* DEFAULT_ENV */ |
||
416 |
3624 |
env_file = substitute(env_file, DOTILDE); |
|
417 |
✓✓ | 3624 |
if (*env_file != '\0') |
418 |
750 |
include(env_file, 0, NULL, 1); |
|
419 |
3624 |
} |
|
420 |
|||
421 |
✓✗✗✓ |
435536 |
if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL")))) |
422 |
restricted = 1; |
||
423 |
✓✓ | 217768 |
if (restricted) { |
424 |
static const char *const restr_com[] = { |
||
425 |
"typeset", "-r", "PATH", |
||
426 |
"ENV", "SHELL", |
||
427 |
NULL |
||
428 |
}; |
||
429 |
2725 |
shcomexec((char **) restr_com); |
|
430 |
/* After typeset command... */ |
||
431 |
2725 |
Flag(FRESTRICTED) = 1; |
|
432 |
2725 |
} |
|
433 |
✓✓ | 217768 |
if (errexit) |
434 |
113373 |
Flag(FERREXIT) = 1; |
|
435 |
|||
436 |
✓✓ | 217768 |
if (Flag(FTALKING)) { |
437 |
3624 |
hist_init(s); |
|
438 |
3624 |
alarm_init(); |
|
439 |
3624 |
} else |
|
440 |
214144 |
Flag(FTRACKALL) = 1; /* set after ENV */ |
|
441 |
|||
442 |
217768 |
shell(s, true); /* doesn't return */ |
|
443 |
217768 |
return 0; |
|
444 |
217768 |
} |
|
445 |
|||
446 |
static void |
||
447 |
init_username(void) |
||
448 |
{ |
||
449 |
char *p; |
||
450 |
435540 |
struct tbl *vp = global("USER"); |
|
451 |
|||
452 |
✓✗ | 217770 |
if (vp->flag & ISSET) |
453 |
✗✓ | 435540 |
p = ksheuid == 0 ? "root" : str_val(vp); |
454 |
else |
||
455 |
p = getlogin(); |
||
456 |
|||
457 |
217770 |
strlcpy(username, p != NULL ? p : "?", sizeof username); |
|
458 |
217770 |
} |
|
459 |
|||
460 |
int |
||
461 |
include(const char *name, int argc, char **argv, int intr_ok) |
||
462 |
{ |
||
463 |
14110 |
Source *volatile s = NULL; |
|
464 |
struct shf *shf; |
||
465 |
7055 |
char **volatile old_argv; |
|
466 |
7055 |
volatile int old_argc; |
|
467 |
int i; |
||
468 |
|||
469 |
7055 |
shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); |
|
470 |
✓✓ | 7055 |
if (shf == NULL) |
471 |
33 |
return -1; |
|
472 |
|||
473 |
✗✓ | 7022 |
if (argv) { |
474 |
old_argv = genv->loc->argv; |
||
475 |
old_argc = genv->loc->argc; |
||
476 |
} else { |
||
477 |
7022 |
old_argv = NULL; |
|
478 |
7022 |
old_argc = 0; |
|
479 |
} |
||
480 |
7483 |
newenv(E_INCL); |
|
481 |
7483 |
i = sigsetjmp(genv->jbuf, 0); |
|
482 |
✓✓ | 7483 |
if (i) { |
483 |
✓✗ | 1383 |
quitenv(s ? s->u.shf : NULL); |
484 |
✗✓ | 461 |
if (old_argv) { |
485 |
genv->loc->argv = old_argv; |
||
486 |
genv->loc->argc = old_argc; |
||
487 |
} |
||
488 |
✗✓✗✗ ✗✗✗ |
461 |
switch (i) { |
489 |
case LRETURN: |
||
490 |
case LERROR: |
||
491 |
461 |
return exstat & 0xff; /* see below */ |
|
492 |
case LINTR: |
||
493 |
/* intr_ok is set if we are including .profile or $ENV. |
||
494 |
* If user ^C's out, we don't want to kill the shell... |
||
495 |
*/ |
||
496 |
if (intr_ok && (exstat - 128) != SIGTERM) |
||
497 |
return 1; |
||
498 |
/* FALLTHROUGH */ |
||
499 |
case LEXIT: |
||
500 |
case LLEAVE: |
||
501 |
case LSHELL: |
||
502 |
unwind(i); |
||
503 |
/* NOTREACHED */ |
||
504 |
default: |
||
505 |
internal_errorf(1, "include: %d", i); |
||
506 |
/* NOTREACHED */ |
||
507 |
} |
||
508 |
} |
||
509 |
✗✓ | 7022 |
if (argv) { |
510 |
genv->loc->argv = argv; |
||
511 |
genv->loc->argc = argc; |
||
512 |
} |
||
513 |
6540 |
s = pushs(SFILE, ATEMP); |
|
514 |
6540 |
s->u.shf = shf; |
|
515 |
6540 |
s->file = str_save(name, ATEMP); |
|
516 |
6540 |
i = shell(s, false); |
|
517 |
6540 |
quitenv(s->u.shf); |
|
518 |
✗✓ | 6540 |
if (old_argv) { |
519 |
genv->loc->argv = old_argv; |
||
520 |
genv->loc->argc = old_argc; |
||
521 |
} |
||
522 |
6540 |
return i & 0xff; /* & 0xff to ensure value not -1 */ |
|
523 |
7034 |
} |
|
524 |
|||
525 |
/* |
||
526 |
* spawn a command into a shell optionally keeping track of line |
||
527 |
* number. |
||
528 |
*/ |
||
529 |
int |
||
530 |
command(const char *comm, int line) |
||
531 |
{ |
||
532 |
Source *s; |
||
533 |
|||
534 |
1390 |
s = pushs(SSTRING, ATEMP); |
|
535 |
695 |
s->start = s->str = comm; |
|
536 |
695 |
s->line = line; |
|
537 |
695 |
return shell(s, false); |
|
538 |
} |
||
539 |
|||
540 |
/* |
||
541 |
* run the commands from the input source, returning status. |
||
542 |
*/ |
||
543 |
int |
||
544 |
shell(Source *volatile s, volatile int toplevel) |
||
545 |
{ |
||
546 |
struct op *t; |
||
547 |
1119224 |
volatile int wastty = s->flags & SF_TTY; |
|
548 |
1119224 |
volatile int attempts = 13; |
|
549 |
✓✓ | 2243070 |
volatile int interactive = Flag(FTALKING) && toplevel; |
550 |
1119224 |
Source *volatile old_source = source; |
|
551 |
int i; |
||
552 |
|||
553 |
1119224 |
newenv(E_PARSE); |
|
554 |
✓✓ | 1119224 |
if (interactive) |
555 |
3624 |
really_exit = 0; |
|
556 |
1260792 |
i = sigsetjmp(genv->jbuf, 0); |
|
557 |
✓✓ | 1260792 |
if (i) { |
558 |
✗✗✓✗ ✗✓✗ |
142207 |
switch (i) { |
559 |
case LINTR: /* we get here if SIGINT not caught or ignored */ |
||
560 |
case LERROR: |
||
561 |
case LSHELL: |
||
562 |
✓✓ | 971 |
if (interactive) { |
563 |
✓✓ | 332 |
if (i == LINTR) |
564 |
145 |
shellf("\n"); |
|
565 |
/* Reset any eof that was read as part of a |
||
566 |
* multiline command. |
||
567 |
*/ |
||
568 |
✗✓✗✗ ✗✗ |
332 |
if (Flag(FIGNOREEOF) && s->type == SEOF && |
569 |
wastty) |
||
570 |
s->type = SSTDIN; |
||
571 |
/* Used by exit command to get back to |
||
572 |
* top level shell. Kind of strange since |
||
573 |
* interactive is set if we are reading from |
||
574 |
* a tty, but to have stopped jobs, one only |
||
575 |
* needs FMONITOR set (not FTALKING/SF_TTY)... |
||
576 |
*/ |
||
577 |
/* toss any input we have so far */ |
||
578 |
332 |
s->start = s->str = null; |
|
579 |
332 |
break; |
|
580 |
} |
||
581 |
/* FALLTHROUGH */ |
||
582 |
case LEXIT: |
||
583 |
case LLEAVE: |
||
584 |
case LRETURN: |
||
585 |
source = old_source; |
||
586 |
quitenv(NULL); |
||
587 |
unwind(i); /* keep on going */ |
||
588 |
/* NOTREACHED */ |
||
589 |
default: |
||
590 |
source = old_source; |
||
591 |
quitenv(NULL); |
||
592 |
internal_errorf(1, "shell: %d", i); |
||
593 |
/* NOTREACHED */ |
||
594 |
} |
||
595 |
} |
||
596 |
|||
597 |
4664998 |
while (1) { |
|
598 |
✓✓ | 5784554 |
if (trap) |
599 |
358811 |
runtraps(0); |
|
600 |
|||
601 |
✓✗ | 5784399 |
if (s->next == NULL) { |
602 |
11568798 |
if (Flag(FVERBOSE)) |
|
603 |
5784399 |
s->flags |= SF_ECHO; |
|
604 |
else |
||
605 |
5784399 |
s->flags &= ~SF_ECHO; |
|
606 |
5784399 |
} |
|
607 |
|||
608 |
✓✓ | 5784399 |
if (interactive) { |
609 |
7538 |
got_sigwinch = 1; |
|
610 |
7538 |
j_notify(); |
|
611 |
7538 |
mcheck(); |
|
612 |
7538 |
set_prompt(PS1, s); |
|
613 |
7538 |
} |
|
614 |
|||
615 |
5781486 |
t = compile(s); |
|
616 |
✓✓✓✓ |
9445305 |
if (t != NULL && t->type == TEOF) { |
617 |
✓✓✗✓ ✗✗ |
1035403 |
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) { |
618 |
shellf("Use `exit' to leave ksh\n"); |
||
619 |
s->type = SSTDIN; |
||
620 |
✓✓✗✓ |
1035403 |
} else if (wastty && !really_exit && |
621 |
16 |
j_stopped_running()) { |
|
622 |
really_exit = 1; |
||
623 |
s->type = SSTDIN; |
||
624 |
} else { |
||
625 |
/* this for POSIX, which says EXIT traps |
||
626 |
* shall be taken in the environment |
||
627 |
* immediately after the last command |
||
628 |
* executed. |
||
629 |
*/ |
||
630 |
✓✓ | 1035387 |
if (toplevel) |
631 |
unwind(LEXIT); |
||
632 |
break; |
||
633 |
} |
||
634 |
} |
||
635 |
|||
636 |
✓✓✓✓ ✗✓ |
7374559 |
if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY))) |
637 |
2628404 |
exstat = execute(t, 0, NULL); |
|
638 |
|||
639 |
✓✓✓✗ ✗✓ |
9759660 |
if (t != NULL && t->type != TEOF && interactive && really_exit) |
640 |
really_exit = 0; |
||
641 |
|||
642 |
4664998 |
reclaim(); |
|
643 |
} |
||
644 |
900504 |
quitenv(NULL); |
|
645 |
900504 |
source = old_source; |
|
646 |
1801008 |
return exstat; |
|
647 |
900504 |
} |
|
648 |
|||
649 |
/* return to closest error handler or shell(), exit if none found */ |
||
650 |
void |
||
651 |
unwind(int i) |
||
652 |
{ |
||
653 |
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */ |
||
654 |
✓✓✓✓ ✓✓ |
2391233 |
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) && |
655 |
114124 |
sigtraps[SIGEXIT_].trap)) { |
|
656 |
✓✓ | 143986 |
if (trap) |
657 |
1630 |
runtraps(0); |
|
658 |
143981 |
runtrap(&sigtraps[SIGEXIT_]); |
|
659 |
i = LLEAVE; |
||
660 |
✓✓✓✓ |
883076 |
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) { |
661 |
✓✓ | 914 |
if (trap) |
662 |
878 |
runtraps(0); |
|
663 |
888 |
runtrap(&sigtraps[SIGERR_]); |
|
664 |
i = LLEAVE; |
||
665 |
888 |
} |
|
666 |
1400750 |
while (1) { |
|
667 |
✗✗✗✗ ✓✓✓ |
2092387 |
switch (genv->type) { |
668 |
case E_PARSE: |
||
669 |
case E_FUNC: |
||
670 |
case E_INCL: |
||
671 |
case E_LOOP: |
||
672 |
case E_ERRH: |
||
673 |
siglongjmp(genv->jbuf, i); |
||
674 |
/* NOTREACHED */ |
||
675 |
|||
676 |
case E_NONE: |
||
677 |
✓✓ | 140566 |
if (i == LINTR) |
678 |
29 |
genv->flags |= EF_FAKE_SIGDIE; |
|
679 |
/* FALLTHROUGH */ |
||
680 |
|||
681 |
default: |
||
682 |
1400750 |
quitenv(NULL); |
|
683 |
/* |
||
684 |
* quitenv() may have reclaimed the memory |
||
685 |
* used by source which will end badly when |
||
686 |
* we jump to a function that expects it to |
||
687 |
* be valid |
||
688 |
*/ |
||
689 |
1400750 |
source = NULL; |
|
690 |
} |
||
691 |
} |
||
692 |
} |
||
693 |
|||
694 |
void |
||
695 |
newenv(int type) |
||
696 |
{ |
||
697 |
struct env *ep; |
||
698 |
|||
699 |
69418634 |
ep = alloc(sizeof(*ep), ATEMP); |
|
700 |
34709317 |
ep->type = type; |
|
701 |
34709317 |
ep->flags = 0; |
|
702 |
34709317 |
ainit(&ep->area); |
|
703 |
34709317 |
ep->loc = genv->loc; |
|
704 |
34709317 |
ep->savefd = NULL; |
|
705 |
34709317 |
ep->oenv = genv; |
|
706 |
34709317 |
ep->temps = NULL; |
|
707 |
34709317 |
genv = ep; |
|
708 |
34709317 |
} |
|
709 |
|||
710 |
void |
||
711 |
quitenv(struct shf *shf) |
||
712 |
{ |
||
713 |
69176398 |
struct env *ep = genv; |
|
714 |
int fd; |
||
715 |
|||
716 |
✓✓✓✓ |
68958628 |
if (ep->oenv && ep->oenv->loc != ep->loc) |
717 |
8349588 |
popblock(); |
|
718 |
✓✓ | 34588199 |
if (ep->savefd != NULL) { |
719 |
✓✓ | 222613842 |
for (fd = 0; fd < NUFILE; fd++) |
720 |
/* if ep->savefd[fd] < 0, means fd was closed */ |
||
721 |
✓✓ | 107933984 |
if (ep->savefd[fd]) |
722 |
3650903 |
restfd(fd, ep->savefd[fd]); |
|
723 |
✓✓ | 3372937 |
if (ep->savefd[2]) /* Clear any write errors */ |
724 |
185877 |
shf_reopen(2, SHF_WR, shl_out); |
|
725 |
} |
||
726 |
|||
727 |
/* Bottom of the stack. |
||
728 |
* Either main shell is exiting or cleanup_parents_env() was called. |
||
729 |
*/ |
||
730 |
✓✓ | 34588199 |
if (ep->oenv == NULL) { |
731 |
✓✓ | 217770 |
if (ep->type == E_NONE) { /* Main shell exiting? */ |
732 |
✓✓ | 140566 |
if (Flag(FTALKING)) |
733 |
3516 |
hist_finish(); |
|
734 |
140566 |
j_exit(); |
|
735 |
✓✓ | 140566 |
if (ep->flags & EF_FAKE_SIGDIE) { |
736 |
29 |
int sig = exstat - 128; |
|
737 |
|||
738 |
/* ham up our death a bit (at&t ksh |
||
739 |
* only seems to do this for SIGTERM) |
||
740 |
* Don't do it for SIGQUIT, since we'd |
||
741 |
* dump a core.. |
||
742 |
*/ |
||
743 |
✓✗✗✓ |
58 |
if ((sig == SIGINT || sig == SIGTERM) && |
744 |
29 |
getpgrp() == kshpid) { |
|
745 |
setsig(&sigtraps[sig], SIG_DFL, |
||
746 |
SS_RESTORE_CURR|SS_FORCE); |
||
747 |
kill(0, sig); |
||
748 |
} |
||
749 |
29 |
} |
|
750 |
} |
||
751 |
✓✓ | 217770 |
if (shf) |
752 |
151 |
shf_close(shf); |
|
753 |
reclaim(); |
||
754 |
exit(exstat); |
||
755 |
} |
||
756 |
✓✓ | 34370429 |
if (shf) |
757 |
7001 |
shf_close(shf); |
|
758 |
34370429 |
reclaim(); |
|
759 |
|||
760 |
34370429 |
genv = genv->oenv; |
|
761 |
34370429 |
afree(ep, ATEMP); |
|
762 |
34370429 |
} |
|
763 |
|||
764 |
/* Called after a fork to cleanup stuff left over from parents environment */ |
||
765 |
void |
||
766 |
cleanup_parents_env(void) |
||
767 |
{ |
||
768 |
struct env *ep; |
||
769 |
int fd; |
||
770 |
|||
771 |
/* Don't clean up temporary files - parent will probably need them. |
||
772 |
* Also, can't easily reclaim memory since variables, etc. could be |
||
773 |
* anywhere. |
||
774 |
*/ |
||
775 |
|||
776 |
/* close all file descriptors hiding in savefd */ |
||
777 |
✓✓ | 1087601 |
for (ep = genv; ep; ep = ep->oenv) { |
778 |
✓✓ | 420853 |
if (ep->savefd) { |
779 |
✓✓ | 4133448 |
for (fd = 0; fd < NUFILE; fd++) |
780 |
✓✓ | 2004096 |
if (ep->savefd[fd] > 0) |
781 |
122848 |
close(ep->savefd[fd]); |
|
782 |
62628 |
afree(ep->savefd, &ep->area); |
|
783 |
62628 |
ep->savefd = NULL; |
|
784 |
62628 |
} |
|
785 |
} |
||
786 |
81965 |
genv->oenv = NULL; |
|
787 |
81965 |
} |
|
788 |
|||
789 |
/* Called just before an execve cleanup stuff temporary files */ |
||
790 |
void |
||
791 |
cleanup_proc_env(void) |
||
792 |
{ |
||
793 |
struct env *ep; |
||
794 |
|||
795 |
for (ep = genv; ep; ep = ep->oenv) |
||
796 |
remove_temps(ep->temps); |
||
797 |
} |
||
798 |
|||
799 |
/* remove temp files and free ATEMP Area */ |
||
800 |
static void |
||
801 |
reclaim(void) |
||
802 |
{ |
||
803 |
78506394 |
remove_temps(genv->temps); |
|
804 |
39253197 |
genv->temps = NULL; |
|
805 |
39253197 |
afreeall(&genv->area); |
|
806 |
39253197 |
} |
|
807 |
|||
808 |
static void |
||
809 |
remove_temps(struct temp *tp) |
||
810 |
{ |
||
811 |
|||
812 |
✓✓ | 118165139 |
for (; tp != NULL; tp = tp->next) |
813 |
✓✗ | 202774 |
if (tp->pid == procpid) { |
814 |
202774 |
unlink(tp->name); |
|
815 |
202774 |
} |
|
816 |
39253197 |
} |
|
817 |
|||
818 |
/* Returns true if name refers to a restricted shell */ |
||
819 |
static int |
||
820 |
is_restricted(char *name) |
||
821 |
{ |
||
822 |
char *p; |
||
823 |
|||
824 |
✓✓ | 871072 |
if ((p = strrchr(name, '/'))) |
825 |
392677 |
name = p + 1; |
|
826 |
/* accepts rsh, rksh, rpdksh, pdrksh */ |
||
827 |
✓✗✓✗ |
871072 |
if (strcmp(name, "rsh") && \ |
828 |
✓✗ | 435536 |
strcmp(name, "rksh") && \ |
829 |
✓✗ | 435536 |
strcmp(name, "rpdksh") && \ |
830 |
435536 |
strcmp(name, "pdrksh")) |
|
831 |
435536 |
return(0); |
|
832 |
else |
||
833 |
return(1); |
||
834 |
|||
835 |
435536 |
} |
Generated by: GCOVR (Version 3.3) |