GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: main.c,v 1.79 2016/03/04 15:11:06 deraadt 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", "-i", "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 |
3525 |
{ |
|
129 |
int i; |
||
130 |
char **nargv; |
||
131 |
|||
132 |
3525 |
nargv = areallocarray(NULL, argc + 1, sizeof(char *), &aperm); |
|
133 |
3525 |
nargv[0] = (char *) kshname; |
|
134 |
✓✓ | 3741 |
for (i = 1; i < argc; i++) |
135 |
216 |
nargv[i] = argv[i]; |
|
136 |
3525 |
nargv[i] = NULL; |
|
137 |
|||
138 |
3525 |
return nargv; |
|
139 |
} |
||
140 |
|||
141 |
int |
||
142 |
main(int argc, char *argv[]) |
||
143 |
3525 |
{ |
|
144 |
int i; |
||
145 |
int argi; |
||
146 |
Source *s; |
||
147 |
struct block *l; |
||
148 |
int restricted, errexit; |
||
149 |
char **wp; |
||
150 |
struct env env; |
||
151 |
pid_t ppid; |
||
152 |
|||
153 |
3525 |
kshname = argv[0]; |
|
154 |
|||
155 |
✗✓ | 3525 |
if (pledge("stdio rpath wpath cpath fattr flock getpw proc exec tty", |
156 |
NULL) == -1) { |
||
157 |
perror("pledge"); |
||
158 |
exit(1); |
||
159 |
} |
||
160 |
|||
161 |
3525 |
ainit(&aperm); /* initialize permanent Area */ |
|
162 |
|||
163 |
/* set up base environment */ |
||
164 |
3525 |
memset(&env, 0, sizeof(env)); |
|
165 |
3525 |
env.type = E_NONE; |
|
166 |
3525 |
ainit(&env.area); |
|
167 |
3525 |
genv = &env; |
|
168 |
3525 |
newblock(); /* set up global l->vars and l->funs */ |
|
169 |
|||
170 |
/* Do this first so output routines (eg, errorf, shellf) can work */ |
||
171 |
3525 |
initio(); |
|
172 |
|||
173 |
3525 |
initvar(); |
|
174 |
|||
175 |
3525 |
initctypes(); |
|
176 |
|||
177 |
3525 |
inittraps(); |
|
178 |
|||
179 |
3525 |
coproc_init(); |
|
180 |
|||
181 |
/* set up variable and command dictionaries */ |
||
182 |
3525 |
ktinit(&taliases, APERM, 0); |
|
183 |
3525 |
ktinit(&aliases, APERM, 0); |
|
184 |
3525 |
ktinit(&homedirs, APERM, 0); |
|
185 |
|||
186 |
/* define shell keywords */ |
||
187 |
3525 |
initkeywords(); |
|
188 |
|||
189 |
/* define built-in commands */ |
||
190 |
3525 |
ktinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */ |
|
191 |
✓✓ | 84600 |
for (i = 0; shbuiltins[i].name != NULL; i++) |
192 |
81075 |
builtin(shbuiltins[i].name, shbuiltins[i].func); |
|
193 |
✓✓ | 70500 |
for (i = 0; kshbuiltins[i].name != NULL; i++) |
194 |
66975 |
builtin(kshbuiltins[i].name, kshbuiltins[i].func); |
|
195 |
|||
196 |
3525 |
init_histvec(); |
|
197 |
|||
198 |
3525 |
def_path = _PATH_DEFPATH; |
|
199 |
{ |
||
200 |
3525 |
size_t len = confstr(_CS_PATH, NULL, 0); |
|
201 |
char *new; |
||
202 |
|||
203 |
✓✗ | 3525 |
if (len > 0) { |
204 |
3525 |
confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1); |
|
205 |
3525 |
def_path = new; |
|
206 |
} |
||
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 |
3525 |
struct tbl *vp = global("PATH"); |
|
214 |
/* setstr can't fail here */ |
||
215 |
3525 |
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 |
3525 |
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 |
3525 |
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 |
✓✓✓✗ ✓✗✓✓ |
3525 |
if (!strcmp(kshname, "sh") || !strcmp(kshname, "-sh") || |
247 |
(strlen(kshname) >= 3 && |
||
248 |
!strcmp(&kshname[strlen(kshname) - 3], "/sh"))) { |
||
249 |
3055 |
Flag(FSH) = 1; |
|
250 |
3055 |
version_param = "SH_VERSION"; |
|
251 |
} |
||
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 |
3525 |
change_flag(FEMACS, OF_SPECIAL, 1); |
|
258 |
#endif /* EDIT && EMACS */ |
||
259 |
#if defined(EDIT) && defined(VI) |
||
260 |
3525 |
Flag(FVITABCOMPLETE) = 1; |
|
261 |
#endif /* EDIT && VI */ |
||
262 |
|||
263 |
/* import environment */ |
||
264 |
✓✗ | 3525 |
if (environ != NULL) |
265 |
✓✓ | 62680 |
for (wp = environ; *wp != NULL; wp++) |
266 |
59155 |
typeset(*wp, IMPORT|EXPORT, 0, 0, 0); |
|
267 |
|||
268 |
3525 |
kshpid = procpid = getpid(); |
|
269 |
3525 |
typeset(initifs, 0, 0, 0, 0); /* for security */ |
|
270 |
|||
271 |
/* assign default shell variable values */ |
||
272 |
3525 |
substitute(initsubs, 0); |
|
273 |
|||
274 |
/* Figure out the current working directory and set $PWD */ |
||
275 |
{ |
||
276 |
struct stat s_pwd, s_dot; |
||
277 |
3525 |
struct tbl *pwd_v = global("PWD"); |
|
278 |
3525 |
char *pwd = str_val(pwd_v); |
|
279 |
3525 |
char *pwdx = pwd; |
|
280 |
|||
281 |
/* Try to use existing $PWD if it is valid */ |
||
282 |
✓✓✓✗ ✓✗✓✗ ✗✓ |
3525 |
if (pwd[0] != '/' || |
283 |
stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 || |
||
284 |
s_pwd.st_dev != s_dot.st_dev || |
||
285 |
s_pwd.st_ino != s_dot.st_ino) |
||
286 |
464 |
pwdx = NULL; |
|
287 |
3525 |
set_current_wd(pwdx); |
|
288 |
✓✗ | 3525 |
if (current_wd[0]) |
289 |
3525 |
simplify_path(current_wd); |
|
290 |
/* Only set pwd if we know where we are or if it had a |
||
291 |
* bogus value |
||
292 |
*/ |
||
293 |
✗✓✗✗ |
3525 |
if (current_wd[0] || pwd != null) |
294 |
/* setstr can't fail here */ |
||
295 |
3525 |
setstr(pwd_v, current_wd, KSH_RETURN_ERROR); |
|
296 |
} |
||
297 |
3525 |
ppid = getppid(); |
|
298 |
3525 |
setint(global("PPID"), (long) ppid); |
|
299 |
/* setstr can't fail here */ |
||
300 |
3525 |
setstr(global(version_param), ksh_version, KSH_RETURN_ERROR); |
|
301 |
|||
302 |
/* execute initialization statements */ |
||
303 |
✓✓ | 28200 |
for (wp = (char**) initcoms; *wp != NULL; wp++) { |
304 |
24675 |
shcomexec(wp); |
|
305 |
✓✓ | 24675 |
for (; *wp != NULL; wp++) |
306 |
; |
||
307 |
} |
||
308 |
|||
309 |
|||
310 |
3525 |
ksheuid = geteuid(); |
|
311 |
3525 |
init_username(); |
|
312 |
✗✓ | 3525 |
safe_prompt = ksheuid ? "$ " : "# "; |
313 |
{ |
||
314 |
3525 |
struct tbl *vp = global("PS1"); |
|
315 |
|||
316 |
/* Set PS1 if it isn't set, or we are root and prompt doesn't |
||
317 |
* contain a # or \$ (only in ksh mode). |
||
318 |
*/ |
||
319 |
✗✓✗✗ ✗✗✗✗ ✗✗ |
3525 |
if (!(vp->flag & ISSET) || |
320 |
(!ksheuid && !strchr(str_val(vp), '#') && |
||
321 |
(Flag(FSH) || !strstr(str_val(vp), "\\$")))) |
||
322 |
/* setstr can't fail here */ |
||
323 |
3525 |
setstr(vp, safe_prompt, KSH_RETURN_ERROR); |
|
324 |
} |
||
325 |
|||
326 |
/* Set this before parsing arguments */ |
||
327 |
✓✗✗✓ |
3525 |
Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid(); |
328 |
|||
329 |
/* this to note if monitor is set on command line (see below) */ |
||
330 |
3525 |
Flag(FMONITOR) = 127; |
|
331 |
3525 |
argi = parse_args(argv, OF_CMDLINE, NULL); |
|
332 |
✗✓ | 3525 |
if (argi < 0) |
333 |
exit(1); |
||
334 |
|||
335 |
✓✓ | 3525 |
if (Flag(FCOMMAND)) { |
336 |
2985 |
s = pushs(SSTRING, ATEMP); |
|
337 |
✗✓ | 2985 |
if (!(s->start = s->str = argv[argi++])) |
338 |
errorf("-c requires an argument"); |
||
339 |
✗✓ | 2985 |
if (argv[argi]) |
340 |
kshname = argv[argi++]; |
||
341 |
✓✓✓✗ |
621 |
} else if (argi < argc && !Flag(FSTDIN)) { |
342 |
81 |
s = pushs(SFILE, ATEMP); |
|
343 |
81 |
s->file = argv[argi++]; |
|
344 |
81 |
s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); |
|
345 |
✗✓ | 81 |
if (s->u.shf == NULL) { |
346 |
exstat = 127; /* POSIX */ |
||
347 |
errorf("%s: %s", s->file, strerror(errno)); |
||
348 |
} |
||
349 |
81 |
kshname = s->file; |
|
350 |
} else { |
||
351 |
459 |
Flag(FSTDIN) = 1; |
|
352 |
459 |
s = pushs(SSTDIN, ATEMP); |
|
353 |
459 |
s->file = "<stdin>"; |
|
354 |
459 |
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0), NULL); |
|
355 |
✓✓✓✗ |
459 |
if (isatty(0) && isatty(2)) { |
356 |
7 |
Flag(FTALKING) = Flag(FTALKING_I) = 1; |
|
357 |
/* The following only if isatty(0) */ |
||
358 |
7 |
s->flags |= SF_TTY; |
|
359 |
7 |
s->u.shf->flags |= SHF_INTERRUPT; |
|
360 |
7 |
s->file = NULL; |
|
361 |
} |
||
362 |
} |
||
363 |
|||
364 |
/* This bizarreness is mandated by POSIX */ |
||
365 |
{ |
||
366 |
struct stat s_stdin; |
||
367 |
|||
368 |
✓✗✓✓ ✓✓ |
3525 |
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) && |
369 |
Flag(FTALKING)) |
||
370 |
7 |
reset_nonblock(0); |
|
371 |
} |
||
372 |
|||
373 |
/* initialize job control */ |
||
374 |
3525 |
i = Flag(FMONITOR) != 127; |
|
375 |
3525 |
Flag(FMONITOR) = 0; |
|
376 |
3525 |
j_init(i); |
|
377 |
#ifdef EDIT |
||
378 |
/* Do this after j_init(), as tty_fd is not initialized 'til then */ |
||
379 |
✓✓ | 3525 |
if (Flag(FTALKING)) |
380 |
63 |
x_init(); |
|
381 |
#endif |
||
382 |
|||
383 |
3525 |
l = genv->loc; |
|
384 |
3525 |
l->argv = make_argv(argc - (argi - 1), &argv[argi - 1]); |
|
385 |
3525 |
l->argc = argc - argi; |
|
386 |
3525 |
getopts_reset(1); |
|
387 |
|||
388 |
/* Disable during .profile/ENV reading */ |
||
389 |
3525 |
restricted = Flag(FRESTRICTED); |
|
390 |
3525 |
Flag(FRESTRICTED) = 0; |
|
391 |
3525 |
errexit = Flag(FERREXIT); |
|
392 |
3525 |
Flag(FERREXIT) = 0; |
|
393 |
|||
394 |
/* Do this before profile/$ENV so that if it causes problems in them, |
||
395 |
* user will know why things broke. |
||
396 |
*/ |
||
397 |
✗✓✗✗ |
3525 |
if (!current_wd[0] && Flag(FTALKING)) |
398 |
warningf(false, "Cannot determine current working directory"); |
||
399 |
|||
400 |
✓✓ | 3525 |
if (Flag(FLOGIN)) { |
401 |
7 |
include(KSH_SYSTEM_PROFILE, 0, NULL, 1); |
|
402 |
✓✗ | 7 |
if (!Flag(FPRIVILEGED)) |
403 |
7 |
include(substitute("$HOME/.profile", 0), 0, NULL, 1); |
|
404 |
} |
||
405 |
|||
406 |
✗✓ | 3525 |
if (Flag(FPRIVILEGED)) |
407 |
include("/etc/suid_profile", 0, NULL, 1); |
||
408 |
✓✓ | 3525 |
else if (Flag(FTALKING)) { |
409 |
char *env_file; |
||
410 |
|||
411 |
/* include $ENV */ |
||
412 |
63 |
env_file = str_val(global("ENV")); |
|
413 |
|||
414 |
#ifdef DEFAULT_ENV |
||
415 |
/* If env isn't set, include default environment */ |
||
416 |
if (env_file == null) |
||
417 |
env_file = DEFAULT_ENV; |
||
418 |
#endif /* DEFAULT_ENV */ |
||
419 |
63 |
env_file = substitute(env_file, DOTILDE); |
|
420 |
✓✓ | 63 |
if (*env_file != '\0') |
421 |
50 |
include(env_file, 0, NULL, 1); |
|
422 |
} |
||
423 |
|||
424 |
✓✗✗✓ |
3525 |
if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL")))) |
425 |
restricted = 1; |
||
426 |
✗✓ | 3525 |
if (restricted) { |
427 |
static const char *const restr_com[] = { |
||
428 |
"typeset", "-r", "PATH", |
||
429 |
"ENV", "SHELL", |
||
430 |
NULL |
||
431 |
}; |
||
432 |
shcomexec((char **) restr_com); |
||
433 |
/* After typeset command... */ |
||
434 |
Flag(FRESTRICTED) = 1; |
||
435 |
} |
||
436 |
✓✓ | 3525 |
if (errexit) |
437 |
1712 |
Flag(FERREXIT) = 1; |
|
438 |
|||
439 |
✓✓ | 3525 |
if (Flag(FTALKING)) { |
440 |
63 |
hist_init(s); |
|
441 |
63 |
alarm_init(); |
|
442 |
} else |
||
443 |
3462 |
Flag(FTRACKALL) = 1; /* set after ENV */ |
|
444 |
|||
445 |
3525 |
shell(s, true); /* doesn't return */ |
|
446 |
return 0; |
||
447 |
} |
||
448 |
|||
449 |
static void |
||
450 |
init_username(void) |
||
451 |
3525 |
{ |
|
452 |
char *p; |
||
453 |
3525 |
struct tbl *vp = global("USER"); |
|
454 |
|||
455 |
✓✗ | 3525 |
if (vp->flag & ISSET) |
456 |
✗✓ | 3525 |
p = ksheuid == 0 ? "root" : str_val(vp); |
457 |
else |
||
458 |
p = getlogin(); |
||
459 |
|||
460 |
✓✗ | 3525 |
strlcpy(username, p != NULL ? p : "?", sizeof username); |
461 |
3525 |
} |
|
462 |
|||
463 |
int |
||
464 |
include(const char *name, int argc, char **argv, int intr_ok) |
||
465 |
74 |
{ |
|
466 |
74 |
Source *volatile s = NULL; |
|
467 |
struct shf *shf; |
||
468 |
char **volatile old_argv; |
||
469 |
volatile int old_argc; |
||
470 |
int i; |
||
471 |
|||
472 |
74 |
shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC); |
|
473 |
✓✓ | 74 |
if (shf == NULL) |
474 |
7 |
return -1; |
|
475 |
|||
476 |
✗✓ | 67 |
if (argv) { |
477 |
old_argv = genv->loc->argv; |
||
478 |
old_argc = genv->loc->argc; |
||
479 |
} else { |
||
480 |
67 |
old_argv = NULL; |
|
481 |
67 |
old_argc = 0; |
|
482 |
} |
||
483 |
67 |
newenv(E_INCL); |
|
484 |
67 |
i = sigsetjmp(genv->jbuf, 0); |
|
485 |
✗✓ | 67 |
if (i) { |
486 |
quitenv(s ? s->u.shf : NULL); |
||
487 |
if (old_argv) { |
||
488 |
genv->loc->argv = old_argv; |
||
489 |
genv->loc->argc = old_argc; |
||
490 |
} |
||
491 |
switch (i) { |
||
492 |
case LRETURN: |
||
493 |
case LERROR: |
||
494 |
return exstat & 0xff; /* see below */ |
||
495 |
case LINTR: |
||
496 |
/* intr_ok is set if we are including .profile or $ENV. |
||
497 |
* If user ^C's out, we don't want to kill the shell... |
||
498 |
*/ |
||
499 |
if (intr_ok && (exstat - 128) != SIGTERM) |
||
500 |
return 1; |
||
501 |
/* FALLTHROUGH */ |
||
502 |
case LEXIT: |
||
503 |
case LLEAVE: |
||
504 |
case LSHELL: |
||
505 |
unwind(i); |
||
506 |
/* NOTREACHED */ |
||
507 |
default: |
||
508 |
internal_errorf(1, "include: %d", i); |
||
509 |
/* NOTREACHED */ |
||
510 |
} |
||
511 |
} |
||
512 |
✗✓ | 67 |
if (argv) { |
513 |
genv->loc->argv = argv; |
||
514 |
genv->loc->argc = argc; |
||
515 |
} |
||
516 |
67 |
s = pushs(SFILE, ATEMP); |
|
517 |
67 |
s->u.shf = shf; |
|
518 |
67 |
s->file = str_save(name, ATEMP); |
|
519 |
67 |
i = shell(s, false); |
|
520 |
67 |
quitenv(s->u.shf); |
|
521 |
✗✓ | 67 |
if (old_argv) { |
522 |
genv->loc->argv = old_argv; |
||
523 |
genv->loc->argc = old_argc; |
||
524 |
} |
||
525 |
67 |
return i & 0xff; /* & 0xff to ensure value not -1 */ |
|
526 |
} |
||
527 |
|||
528 |
/* |
||
529 |
* spawn a command into a shell optionally keeping track of line |
||
530 |
* number. |
||
531 |
*/ |
||
532 |
int |
||
533 |
command(const char *comm, int line) |
||
534 |
35 |
{ |
|
535 |
Source *s; |
||
536 |
|||
537 |
35 |
s = pushs(SSTRING, ATEMP); |
|
538 |
35 |
s->start = s->str = comm; |
|
539 |
35 |
s->line = line; |
|
540 |
35 |
return shell(s, false); |
|
541 |
} |
||
542 |
|||
543 |
/* |
||
544 |
* run the commands from the input source, returning status. |
||
545 |
*/ |
||
546 |
int |
||
547 |
shell(Source *volatile s, volatile int toplevel) |
||
548 |
11873 |
{ |
|
549 |
struct op *t; |
||
550 |
11873 |
volatile int wastty = s->flags & SF_TTY; |
|
551 |
11873 |
volatile int attempts = 13; |
|
552 |
✓✓✓✓ |
11873 |
volatile int interactive = Flag(FTALKING) && toplevel; |
553 |
11873 |
Source *volatile old_source = source; |
|
554 |
int i; |
||
555 |
|||
556 |
11873 |
newenv(E_PARSE); |
|
557 |
✓✓ | 11873 |
if (interactive) |
558 |
63 |
really_exit = 0; |
|
559 |
11873 |
i = sigsetjmp(genv->jbuf, 0); |
|
560 |
✓✓ | 15398 |
if (i) { |
561 |
✓✓✗ | 3525 |
switch (i) { |
562 |
case LINTR: /* we get here if SIGINT not caught or ignored */ |
||
563 |
case LERROR: |
||
564 |
case LSHELL: |
||
565 |
✓✓ | 32 |
if (interactive) { |
566 |
✗✓ | 4 |
if (i == LINTR) |
567 |
shellf("\n"); |
||
568 |
/* Reset any eof that was read as part of a |
||
569 |
* multiline command. |
||
570 |
*/ |
||
571 |
✗✓✗✗ ✗✗ |
4 |
if (Flag(FIGNOREEOF) && s->type == SEOF && |
572 |
wastty) |
||
573 |
s->type = SSTDIN; |
||
574 |
/* Used by exit command to get back to |
||
575 |
* top level shell. Kind of strange since |
||
576 |
* interactive is set if we are reading from |
||
577 |
* a tty, but to have stopped jobs, one only |
||
578 |
* needs FMONITOR set (not FTALKING/SF_TTY)... |
||
579 |
*/ |
||
580 |
/* toss any input we have so far */ |
||
581 |
4 |
s->start = s->str = null; |
|
582 |
4 |
break; |
|
583 |
} |
||
584 |
/* FALLTHROUGH */ |
||
585 |
case LEXIT: |
||
586 |
case LLEAVE: |
||
587 |
case LRETURN: |
||
588 |
3521 |
source = old_source; |
|
589 |
3521 |
quitenv(NULL); |
|
590 |
3521 |
unwind(i); /* keep on going */ |
|
591 |
/* NOTREACHED */ |
||
592 |
default: |
||
593 |
source = old_source; |
||
594 |
quitenv(NULL); |
||
595 |
internal_errorf(1, "shell: %d", i); |
||
596 |
/* NOTREACHED */ |
||
597 |
} |
||
598 |
} |
||
599 |
|||
600 |
while (1) { |
||
601 |
✓✓ | 32539 |
if (trap) |
602 |
4615 |
runtraps(0); |
|
603 |
|||
604 |
✓✗ | 32539 |
if (s->next == NULL) { |
605 |
✓✓ | 32539 |
if (Flag(FVERBOSE)) |
606 |
4 |
s->flags |= SF_ECHO; |
|
607 |
else |
||
608 |
32535 |
s->flags &= ~SF_ECHO; |
|
609 |
} |
||
610 |
|||
611 |
✓✓ | 32539 |
if (interactive) { |
612 |
295 |
got_sigwinch = 1; |
|
613 |
295 |
j_notify(); |
|
614 |
295 |
mcheck(); |
|
615 |
295 |
set_prompt(PS1, s); |
|
616 |
} |
||
617 |
|||
618 |
32539 |
t = compile(s); |
|
619 |
✓✓✓✓ |
32535 |
if (t != NULL && t->type == TEOF) { |
620 |
✓✓✗✓ ✗✗ |
11721 |
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) { |
621 |
shellf("Use `exit' to leave ksh\n"); |
||
622 |
s->type = SSTDIN; |
||
623 |
✓✓✓✗ ✗✓ |
11721 |
} else if (wastty && !really_exit && |
624 |
j_stopped_running()) { |
||
625 |
really_exit = 1; |
||
626 |
s->type = SSTDIN; |
||
627 |
} else { |
||
628 |
/* this for POSIX, which says EXIT traps |
||
629 |
* shall be taken in the environment |
||
630 |
* immediately after the last command |
||
631 |
* executed. |
||
632 |
*/ |
||
633 |
✓✓ | 11721 |
if (toplevel) |
634 |
3379 |
unwind(LEXIT); |
|
635 |
break; |
||
636 |
} |
||
637 |
} |
||
638 |
|||
639 |
✓✓✗✓ ✗✗ |
20814 |
if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY))) |
640 |
16007 |
exstat = execute(t, 0, NULL); |
|
641 |
|||
642 |
✓✓✓✗ ✓✓✗✓ |
20662 |
if (t != NULL && t->type != TEOF && interactive && really_exit) |
643 |
really_exit = 0; |
||
644 |
|||
645 |
20662 |
reclaim(); |
|
646 |
20662 |
} |
|
647 |
8342 |
quitenv(NULL); |
|
648 |
8342 |
source = old_source; |
|
649 |
8342 |
return exstat; |
|
650 |
} |
||
651 |
|||
652 |
/* return to closest error handler or shell(), exit if none found */ |
||
653 |
void |
||
654 |
unwind(int i) |
||
655 |
11051 |
{ |
|
656 |
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */ |
||
657 |
✓✓✓✓ ✓✓✓✓ |
15129 |
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) && |
658 |
sigtraps[SIGEXIT_].trap)) { |
||
659 |
✓✓ | 4083 |
if (trap) |
660 |
250 |
runtraps(0); |
|
661 |
4083 |
runtrap(&sigtraps[SIGEXIT_]); |
|
662 |
4078 |
i = LLEAVE; |
|
663 |
✓✓✓✓ |
6968 |
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) { |
664 |
✓✗ | 61 |
if (trap) |
665 |
61 |
runtraps(0); |
|
666 |
61 |
runtrap(&sigtraps[SIGERR_]); |
|
667 |
61 |
i = LLEAVE; |
|
668 |
} |
||
669 |
while (1) { |
||
670 |
✓✓✓ | 14978 |
switch (genv->type) { |
671 |
case E_PARSE: |
||
672 |
case E_FUNC: |
||
673 |
case E_INCL: |
||
674 |
case E_LOOP: |
||
675 |
case E_ERRH: |
||
676 |
4040 |
siglongjmp(genv->jbuf, i); |
|
677 |
/* NOTREACHED */ |
||
678 |
|||
679 |
case E_NONE: |
||
680 |
✗✓ | 3515 |
if (i == LINTR) |
681 |
genv->flags |= EF_FAKE_SIGDIE; |
||
682 |
/* FALLTHROUGH */ |
||
683 |
|||
684 |
default: |
||
685 |
10938 |
quitenv(NULL); |
|
686 |
/* |
||
687 |
* quitenv() may have reclaimed the memory |
||
688 |
* used by source which will end badly when |
||
689 |
* we jump to a function that expects it to |
||
690 |
* be valid |
||
691 |
*/ |
||
692 |
3932 |
source = NULL; |
|
693 |
} |
||
694 |
3932 |
} |
|
695 |
} |
||
696 |
|||
697 |
void |
||
698 |
newenv(int type) |
||
699 |
277638 |
{ |
|
700 |
struct env *ep; |
||
701 |
|||
702 |
277638 |
ep = alloc(sizeof(*ep), ATEMP); |
|
703 |
277638 |
ep->type = type; |
|
704 |
277638 |
ep->flags = 0; |
|
705 |
277638 |
ainit(&ep->area); |
|
706 |
277638 |
ep->loc = genv->loc; |
|
707 |
277638 |
ep->savefd = NULL; |
|
708 |
277638 |
ep->oenv = genv; |
|
709 |
277638 |
ep->temps = NULL; |
|
710 |
277638 |
genv = ep; |
|
711 |
277638 |
} |
|
712 |
|||
713 |
void |
||
714 |
quitenv(struct shf *shf) |
||
715 |
270354 |
{ |
|
716 |
270354 |
struct env *ep = genv; |
|
717 |
int fd; |
||
718 |
|||
719 |
✓✓✓✓ |
270354 |
if (ep->oenv && ep->oenv->loc != ep->loc) |
720 |
136164 |
popblock(); |
|
721 |
✓✓ | 270354 |
if (ep->savefd != NULL) { |
722 |
✓✓ | 249183 |
for (fd = 0; fd < NUFILE; fd++) |
723 |
/* if ep->savefd[fd] < 0, means fd was closed */ |
||
724 |
✓✓ | 241632 |
if (ep->savefd[fd]) |
725 |
8370 |
restfd(fd, ep->savefd[fd]); |
|
726 |
✓✓ | 7551 |
if (ep->savefd[2]) /* Clear any write errors */ |
727 |
800 |
shf_reopen(2, SHF_WR, shl_out); |
|
728 |
} |
||
729 |
|||
730 |
/* Bottom of the stack. |
||
731 |
* Either main shell is exiting or cleanup_parents_env() was called. |
||
732 |
*/ |
||
733 |
✓✓ | 270354 |
if (ep->oenv == NULL) { |
734 |
✓✓ | 7043 |
if (ep->type == E_NONE) { /* Main shell exiting? */ |
735 |
✓✓ | 3515 |
if (Flag(FTALKING)) |
736 |
62 |
hist_finish(); |
|
737 |
3515 |
j_exit(); |
|
738 |
✗✓ | 3515 |
if (ep->flags & EF_FAKE_SIGDIE) { |
739 |
int sig = exstat - 128; |
||
740 |
|||
741 |
/* ham up our death a bit (at&t ksh |
||
742 |
* only seems to do this for SIGTERM) |
||
743 |
* Don't do it for SIGQUIT, since we'd |
||
744 |
* dump a core.. |
||
745 |
*/ |
||
746 |
if ((sig == SIGINT || sig == SIGTERM) && |
||
747 |
getpgrp() == kshpid) { |
||
748 |
setsig(&sigtraps[sig], SIG_DFL, |
||
749 |
SS_RESTORE_CURR|SS_FORCE); |
||
750 |
kill(0, sig); |
||
751 |
} |
||
752 |
} |
||
753 |
} |
||
754 |
✓✓ | 7043 |
if (shf) |
755 |
37 |
shf_close(shf); |
|
756 |
7043 |
reclaim(); |
|
757 |
7043 |
exit(exstat); |
|
758 |
} |
||
759 |
✓✓ | 263311 |
if (shf) |
760 |
67 |
shf_close(shf); |
|
761 |
263311 |
reclaim(); |
|
762 |
|||
763 |
263311 |
genv = genv->oenv; |
|
764 |
263311 |
afree(ep, ATEMP); |
|
765 |
263311 |
} |
|
766 |
|||
767 |
/* Called after a fork to cleanup stuff left over from parents environment */ |
||
768 |
void |
||
769 |
cleanup_parents_env(void) |
||
770 |
14164 |
{ |
|
771 |
struct env *ep; |
||
772 |
int fd; |
||
773 |
|||
774 |
/* Don't clean up temporary files - parent will probably need them. |
||
775 |
* Also, can't easily reclaim memory since variables, etc. could be |
||
776 |
* anywhere. |
||
777 |
*/ |
||
778 |
|||
779 |
/* close all file descriptors hiding in savefd */ |
||
780 |
✓✓ | 105434 |
for (ep = genv; ep; ep = ep->oenv) { |
781 |
✓✓ | 91270 |
if (ep->savefd) { |
782 |
✓✓ | 260667 |
for (fd = 0; fd < NUFILE; fd++) |
783 |
✓✓ | 252768 |
if (ep->savefd[fd] > 0) |
784 |
11017 |
close(ep->savefd[fd]); |
|
785 |
7899 |
afree(ep->savefd, &ep->area); |
|
786 |
7899 |
ep->savefd = NULL; |
|
787 |
} |
||
788 |
} |
||
789 |
14164 |
genv->oenv = NULL; |
|
790 |
14164 |
} |
|
791 |
|||
792 |
/* Called just before an execve cleanup stuff temporary files */ |
||
793 |
void |
||
794 |
cleanup_proc_env(void) |
||
795 |
10645 |
{ |
|
796 |
struct env *ep; |
||
797 |
|||
798 |
✓✓ | 35614 |
for (ep = genv; ep; ep = ep->oenv) |
799 |
24969 |
remove_temps(ep->temps); |
|
800 |
10645 |
} |
|
801 |
|||
802 |
/* remove temp files and free ATEMP Area */ |
||
803 |
static void |
||
804 |
reclaim(void) |
||
805 |
291016 |
{ |
|
806 |
291016 |
remove_temps(genv->temps); |
|
807 |
291016 |
genv->temps = NULL; |
|
808 |
291016 |
afreeall(&genv->area); |
|
809 |
291016 |
} |
|
810 |
|||
811 |
static void |
||
812 |
remove_temps(struct temp *tp) |
||
813 |
315985 |
{ |
|
814 |
|||
815 |
✓✓ | 317659 |
for (; tp != NULL; tp = tp->next) |
816 |
✓✓ | 1674 |
if (tp->pid == procpid) { |
817 |
843 |
unlink(tp->name); |
|
818 |
} |
||
819 |
315985 |
} |
|
820 |
|||
821 |
/* Returns true if name refers to a restricted shell */ |
||
822 |
static int |
||
823 |
is_restricted(char *name) |
||
824 |
7050 |
{ |
|
825 |
char *p; |
||
826 |
|||
827 |
✓✓ | 7050 |
if ((p = strrchr(name, '/'))) |
828 |
6071 |
name = p + 1; |
|
829 |
/* accepts rsh, rksh, rpdksh, pdrksh */ |
||
830 |
✓✗✓✗ ✓✗✓✗ |
7050 |
if (strcmp(name, "rsh") && \ |
831 |
strcmp(name, "rksh") && \ |
||
832 |
strcmp(name, "rpdksh") && \ |
||
833 |
strcmp(name, "pdrksh")) |
||
834 |
7050 |
return(0); |
|
835 |
else |
||
836 |
return(1); |
||
837 |
|||
838 |
} |
Generated by: GCOVR (Version 3.3) |