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