oksh-noxz

[fork] Portable OpenBSD ksh, based on the Public Domain Korn Shell (pdksh).
git clone https://noxz.tech/git/oksh-noxz.git
Log | Files | Tags

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}