856 lines · 19.3 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * startup, main loop, enviroments and error handling */ #define	EXTERN				/* define EXTERNs in sh.h */ #include "sh.h"#include "ksh_stat.h"#include "ksh_time.h" extern char **environ; /* * global data */ static void	reclaim ARGS((void));static void	remove_temps ARGS((struct temp *tp));static int	is_restricted ARGS((char *name)); /* * shell initialization */ static const char initifs[] = "IFS= \t\n"; static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }"; static const char version_param[] =#ifdef KSH	"KSH_VERSION"#else /* KSH */	"SH_VERSION"#endif /* KSH */	; static const char *const initcoms [] = {	"typeset", "-x", "SHELL", "PATH", "HOME", NULL,	"typeset", "-r", version_param, NULL,	"typeset", "-i", "PPID", NULL,	"typeset", "-i", "OPTIND=1", NULL,#ifdef KSH	"eval", "typeset -i RANDOM MAILCHECK=\"${MAILCHECK-600}\" SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL,#endif /* KSH */	"alias",	 /* Standard ksh aliases */	  "hash=alias -t",	/* not "alias -t --": hash -r needs to work */	  "type=whence -v",#ifdef JOBS	  "stop=kill -STOP",	  "suspend=kill -STOP $$",#endif#ifdef KSH	  "autoload=typeset -fu",	  "functions=typeset -f",# ifdef HISTORY	  "history=fc -l",# endif /* HISTORY */	  "integer=typeset -i",	  "nohup=nohup ",	  "local=typeset",	  "r=fc -e -",#endif /* KSH */#ifdef KSH	 /* Aliases that are builtin commands in at&t */	  "login=exec login",	  "newgrp=exec newgrp",#endif /* KSH */	  NULL,	/* this is what at&t ksh seems to track, with the addition of emacs */	"alias", "-tU",	  "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",	  "mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",	  NULL,#ifdef EXTRA_INITCOMS	EXTRA_INITCOMS, NULL,#endif /* EXTRA_INITCOMS */	NULL}; intmain(argc, argv)	int argc;	register char **argv;{	register int i;	int argi;	Source *s;	struct block *l;	int restricted, errexit;	char **wp;	struct env env;	pid_t ppid; #ifdef MEM_DEBUG	chmem_set_defaults("ct", 1);	/* chmem_push("+c", 1); */#endif /* MEM_DEBUG */ #ifdef OS2	setmode (0, O_BINARY);	setmode (1, O_TEXT);#endif 	/* make sure argv[] is sane */	if (!*argv) {		static const char	*empty_argv[] = {					    "pdksh", (char *) 0					}; 		argv = (char **) empty_argv;		argc = 1;	}	kshname = *argv; 	ainit(&aperm);		/* initialize permanent Area */ 	/* set up base enviroment */	memset(&env, 0, sizeof(env));	env.type = E_NONE;	ainit(&env.area);	e = &env;	newblock();		/* set up global l->vars and l->funs */ 	/* Do this first so output routines (eg, errorf, shellf) can work */	initio(); 	initvar(); 	initctypes(); 	inittraps(); #ifdef KSH	coproc_init();#endif /* KSH */ 	/* set up variable and command dictionaries */	tinit(&taliases, APERM, 0);	tinit(&aliases, APERM, 0);	tinit(&homedirs, APERM, 0); 	/* define shell keywords */	initkeywords(); 	/* define built-in commands */	tinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */	for (i = 0; shbuiltins[i].name != NULL; i++)		builtin(shbuiltins[i].name, shbuiltins[i].func);	for (i = 0; kshbuiltins[i].name != NULL; i++)		builtin(kshbuiltins[i].name, kshbuiltins[i].func); 	init_histvec(); 	def_path = DEFAULT__PATH;#if defined(HAVE_CONFSTR) && defined(_CS_PATH)	{		size_t len = confstr(_CS_PATH, (char *) 0, 0);		char *new; 		if (len > 0) {			confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1);			def_path = new;		}	}#endif /* HAVE_CONFSTR && _CS_PATH */ 	/* Set PATH to def_path (will set the path global variable).	 * (import of environment below will probably change this setting).	 */	{		struct tbl *vp = global("PATH");		/* setstr can't fail here */		setstr(vp, def_path, KSH_RETURN_ERROR);	}  	/* Turn on nohup by default for how - will change to off	 * by default once people are aware of its existance	 * (at&t ksh does not have a nohup option - it always sends	 * the hup).	 */	Flag(FNOHUP) = 1; 	/* Turn on brace expansion by default.  At&t ksh's that have	 * alternation always have it on.  BUT, posix doesn't have	 * brace expansion, so set this before setting up FPOSIX	 * (change_flag() clears FBRACEEXPAND when FPOSIX is set).	 */#ifdef BRACE_EXPAND	Flag(FBRACEEXPAND) = 1;#endif /* BRACE_EXPAND */ 	/* set posix flag just before environment so that it will have	 * exactly the same effect as the POSIXLY_CORRECT environment	 * variable.  If this needs to be done sooner to ensure correct posix	 * operation, an initial scan of the environment will also have	 * done sooner.	 */#ifdef POSIXLY_CORRECT	change_flag(FPOSIX, OF_SPECIAL, 1);#endif /* POSIXLY_CORRECT */ 	/* import enviroment */	if (environ != NULL)		for (wp = environ; *wp != NULL; wp++)			typeset(*wp, IMPORT|EXPORT, 0, 0, 0); 	kshpid = procpid = getpid();	typeset(initifs, 0, 0, 0, 0);	/* for security */ 	/* assign default shell variable values */	substitute(initsubs, 0); 	/* Figure out the current working directory and set $PWD */	{		struct stat s_pwd, s_dot;		struct tbl *pwd_v = global("PWD");		char *pwd = str_val(pwd_v);		char *pwdx = pwd; 		/* Try to use existing $PWD if it is valid */		if (!ISABSPATH(pwd)		    || stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0		    || s_pwd.st_dev != s_dot.st_dev		    || s_pwd.st_ino != s_dot.st_ino)			pwdx = (char *) 0;		set_current_wd(pwdx);		if (current_wd[0])			simplify_path(current_wd);		/* Only set pwd if we know where we are or if it had a		 * bogus value		 */		if (current_wd[0] || pwd != null)			/* setstr can't fail here */			setstr(pwd_v, current_wd, KSH_RETURN_ERROR);	}	ppid = getppid();	setint(global("PPID"), (long) ppid);#ifdef KSH	setint(global("RANDOM"), (long) (time((time_t *)0) * kshpid * ppid));#endif /* KSH */	/* setstr can't fail here */	setstr(global(version_param), ksh_version, KSH_RETURN_ERROR); 	/* execute initialization statements */	for (wp = (char**) initcoms; *wp != NULL; wp++) {		shcomexec(wp);		for (; *wp != NULL; wp++)			;	}  	ksheuid = geteuid();	safe_prompt = ksheuid ? "$ " : "# ";	{		struct tbl *vp = global("PS1"); 		/* Set PS1 if it isn't set, or we are root and prompt doesn't		 * contain a #.		 */		if (!(vp->flag & ISSET)		    || (!ksheuid && !strchr(str_val(vp), '#')))			/* setstr can't fail here */			setstr(vp, safe_prompt, KSH_RETURN_ERROR);	} 	/* Set this before parsing arguments */	Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid(); 	/* this to note if monitor is set on command line (see below) */	Flag(FMONITOR) = 127;	argi = parse_args(argv, OF_CMDLINE, (int *) 0);	if (argi < 0)		exit(1); 	if (Flag(FCOMMAND)) {		s = pushs(SSTRING, ATEMP);		if (!(s->start = s->str = argv[argi++]))			errorf("-c requires an argument");		if (argv[argi])			kshname = argv[argi++];	} else if (argi < argc && !Flag(FSTDIN)) {		s = pushs(SFILE, ATEMP);#ifdef OS2		/* a bug in os2 extproc shell processing doesn't		 * pass full pathnames so we have to search for it.		 * This changes the behavior of 'ksh arg' to search		 * the users search path but it can't be helped.		 */		s->file = search(argv[argi++], path, R_OK, (int *) 0);		if (!s->file || !*s->file)		        s->file = argv[argi - 1];#else		s->file = argv[argi++];#endif /* OS2 */		s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);		if (s->u.shf == NULL) {			exstat = 127; /* POSIX */			errorf("%s: %s", s->file, strerror(errno));		}		kshname = s->file;	} else {		Flag(FSTDIN) = 1;		s = pushs(SSTDIN, ATEMP);		s->file = "<stdin>";		s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),				      (struct shf *) 0);		if (!Flag(FNOTTALKING) && isatty(0) && isatty(2)) {			Flag(FTALKING) = Flag(FTALKING_I) = 1;			/* The following only if isatty(0) */			s->flags |= SF_TTY;			s->u.shf->flags |= SHF_INTERRUPT;			s->file = (char *) 0;		}	} 	/* This bizarreness is mandated by POSIX */	{		struct stat s_stdin; 		if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode))			reset_nonblock(0);	} 	/* initialize job control */	i = Flag(FMONITOR) != 127;	Flag(FMONITOR) = 0;	j_init(i);#ifdef EDIT	/* Do this after j_init(), as tty_fd is not initialized 'til then */	if (Flag(FTALKING))		x_init();#endif 	l = e->loc;	l->argv = &argv[argi - 1];	l->argc = argc - argi;	l->argv[0] = (char *) kshname;	getopts_reset(1); 	/* Disable during .profile/ENV reading */	restricted = Flag(FRESTRICTED);	Flag(FRESTRICTED) = 0;	errexit = Flag(FERREXIT);	Flag(FERREXIT) = 0; 	/* Do this before profile/$ENV so that if it causes problems in them,	 * user will know why things broke.	 */	if (!current_wd[0] && Flag(FTALKING))		warningf(FALSE, "Cannot determine current working directory"); 	if (Flag(FLOGIN)) {#ifdef OS2		char *profile; 		/* Try to find a profile - first see if $INIT has a value,		 * then try /etc/profile.ksh, then c:/usr/etc/profile.ksh.		 */		if (!Flag(FPRIVILEGED)		    && strcmp(profile = substitute("$INIT/profile.ksh", 0),			      "/profile.ksh"))			include(profile, 0, (char **) 0, 1);		else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0)			include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1);		if (!Flag(FPRIVILEGED))			include(substitute("$HOME/profile.ksh", 0), 0,				(char **) 0, 1);#else /* OS2 */		include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1);		if (!Flag(FPRIVILEGED))			include(substitute("$HOME/.profile", 0), 0,				(char **) 0, 1);#endif /* OS2 */	} 	if (Flag(FPRIVILEGED))		include("/etc/suid_profile", 0, (char **) 0, 1);	else {		char *env_file; #ifndef KSH		if (!Flag(FPOSIX))			env_file = null;		else#endif /* !KSH */			/* include $ENV */			env_file = str_val(global("ENV")); #ifdef DEFAULT_ENV		/* If env isn't set, include default environment */		if (env_file == null)			env_file = DEFAULT_ENV;#endif /* DEFAULT_ENV */		env_file = substitute(env_file, DOTILDE);		if (*env_file != '\0')			include(env_file, 0, (char **) 0, 1);#ifdef OS2		else if (Flag(FTALKING))			include(substitute("$HOME/kshrc.ksh", 0), 0,				(char **) 0, 1);#endif /* OS2 */	} 	if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))		restricted = 1;	if (restricted) {		static const char *const restr_com[] = {						"typeset", "-r", "PATH",						    "ENV", "SHELL",						(char *) 0					    };		shcomexec((char **) restr_com);		/* After typeset command... */		Flag(FRESTRICTED) = 1;	}	if (errexit)		Flag(FERREXIT) = 1; 	if (Flag(FTALKING)) {		hist_init(s);#ifdef KSH		alarm_init();#endif /* KSH */	} else		Flag(FTRACKALL) = 1;	/* set after ENV */ 	shell(s, TRUE);	/* doesn't return */	return 0;} intinclude(name, argc, argv, intr_ok)	const char *name;	int argc;	char **argv;	int intr_ok;{	register Source *volatile s = NULL;	Source *volatile sold;	struct shf *shf;	char **volatile old_argv;	volatile int old_argc;	int i; 	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);	if (shf == NULL)		return -1; 	if (argv) {		old_argv = e->loc->argv;		old_argc = e->loc->argc;	} else {		old_argv = (char **) 0;		old_argc = 0;	}	sold = source;	newenv(E_INCL);	i = ksh_sigsetjmp(e->jbuf, 0);	if (i) {		source = sold;		if (s) /* Do this before quitenv(), which frees the memory */			shf_close(s->u.shf);		quitenv();		if (old_argv) {			e->loc->argv = old_argv;			e->loc->argc = old_argc;		}		switch (i) {		  case LRETURN:		  case LERROR:			return exstat & 0xff; /* see below */		  case LINTR:			/* intr_ok is set if we are including .profile or $ENV.			 * If user ^C's out, we don't want to kill the shell...			 */			if (intr_ok && (exstat - 128) != SIGTERM)				return 1;			/* fall through... */		  case LEXIT:		  case LLEAVE:		  case LSHELL:			unwind(i);			/*NOREACHED*/		  default:			internal_errorf(1, "include: %d", i);			/*NOREACHED*/		}	}	if (argv) {

Lines 1–500 of 856.