1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #if defined(sun)
33 #pragma ident	"@(#)main.c	1.37	06/06/16 SMI"
34 #endif
35 
36 #include "defs.h"
37 
38 /*
39  * Copyright 2008-2020 J. Schilling
40  *
41  * @(#)main.c	1.80 20/10/07 2008-2020 J. Schilling
42  */
43 #ifndef lint
44 static	UConst char sccsid[] =
45 	"@(#)main.c	1.80 20/10/07 2008-2020 J. Schilling";
46 #endif
47 
48 /*
49  * UNIX shell
50  */
51 #ifdef	SCHILY_INCLUDES
52 #include	"sym.h"
53 #include	"hash.h"
54 #include	"timeout.h"
55 #include	<schily/types.h>
56 #include	<schily/stat.h>
57 #include	<schily/fcntl.h>
58 #include	<schily/wait.h>
59 #ifdef	INTERACTIVE
60 #include	<schily/shedit.h>
61 #endif
62 #include	"dup.h"
63 #include	"sh_policy.h"
64 #ifdef	DO_SYSALIAS
65 #include	"abbrev.h"
66 #endif
67 #undef	feof
68 #else
69 #include	"sym.h"
70 #include	"hash.h"
71 #include	"timeout.h"
72 #include	<stdio.h>
73 #include	<sys/types.h>
74 #include	<sys/stat.h>
75 #include	<fcntl.h>
76 #include	<sys/wait.h>
77 #include	"dup.h"
78 #include	"sh_policy.h"
79 #ifdef	DO_SYSALIAS
80 #include	"abbrev.h"
81 #endif
82 #endif
83 
84 #ifdef RES
85 #include	<sgtty.h>
86 #endif
87 
88 #define	no_pipe	(int *)0
89 
90 pid_t mypid, mypgid, mysid;
91 
92 static BOOL	beenhere = FALSE;
93 unsigned char	tmpout[TMPOUTSZ];
94 struct fileblk	stdfile;
95 struct fileblk *standin = &stdfile;
96 int mailchk = 0;
97 
98 static int	posix = 0;
99 
100 static unsigned char	*mailp;
101 static long	*mod_time = 0;
102 static BOOL login_shell = FALSE;
103 
104 #if vax
105 char **execargs = (char **)(0x7ffffffc);
106 #endif
107 
108 #if pdp11
109 char **execargs = (char **)(-2);
110 #endif
111 
112 	int	main		__PR((int c, char *v[], char *e[]));
113 static void	exfile		__PR((int prof));
114 	void	chkpr		__PR((void));
115 #ifdef	INTERACTIVE
116 static void	editpr		__PR((int idx));
117 #endif
118 	void	settmp		__PR((void));
119 static void	Ldup		__PR((int, int));
120 	void	chkmail		__PR((void));
121 	void	setmail		__PR((unsigned char *));
122 	void	setmode		__PR((int prof));
123 	void	secpolicy_print	__PR((int level, const char *msg));
124 static	void	bosh_init	__PR((void));
125 
126 int
main(c,v,e)127 main(c, v, e)
128 	int	c;
129 	char	*v[];
130 	char	*e[];
131 {
132 	int		rflag = ttyflg;
133 	int		rsflag = 1;	/* local restricted flag */
134 	struct namnod	*n;
135 
136 	init_sigval();
137 	mypid = getpid();
138 	mypgid = getpgid(mypid);	/* get process group for this shell */
139 	mysid = getsid(mypid);		/* get process group id of leader */
140 
141 #if	defined(IS_SUN) || defined(DO_SPLIT_ROOT)
142 	/*
143 	 * Do locale processing only if /usr is mounted.
144 	 * This is to help the single user shell to work.
145 	 * Since localedir may not be the same for all target architectures
146 	 * we just disable this check for a non-Solaris environment.
147 	 */
148 	localedir_exists = (access(localedir, F_OK) == 0);
149 #else
150 	localedir_exists = TRUE;
151 #endif
152 
153 	/*
154 	 * initialize storage allocation
155 	 */
156 
157 	if (stakbot == 0) {
158 		addblok((unsigned)0);
159 	}
160 
161 	/*
162 	 * Initialize global data structure.
163 	 */
164 	bosh_init();
165 
166 	/*
167 	 * If the first character of the last path element of v[0] is "-"
168 	 * (ex. -sh, or /bin/-sh), this is a login shell
169 	 */
170 	if (*simple((unsigned char *)v[0]) == '-') {
171 #ifdef	SIGXCPU
172 		signal(SIGXCPU, SIG_DFL);
173 #endif
174 #ifdef	SIGXFSZ
175 		signal(SIGXFSZ, SIG_DFL);
176 #endif
177 		/*
178 		 * As the previous comment states, this is a login shell.
179 		 * Therefore, we set the login_shell flag to explicitly
180 		 * indicate this condition.
181 		 */
182 		login_shell = TRUE;
183 	}
184 
185 	stdsigs();
186 
187 	/*
188 	 * set names from userenv
189 	 */
190 
191 	setup_env();
192 
193 	/*
194 	 * LC_MESSAGES is set here so that early error messages will
195 	 * come out in the right style.
196 	 * Note that LC_CTYPE is done later on and is *not*
197 	 * taken from the previous environ
198 	 */
199 
200 	/*
201 	 * Do locale processing only if /usr is mounted.
202 	 */
203 	if (localedir_exists)
204 		(void) setlocale(LC_ALL, "");
205 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
206 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
207 #endif
208 	(void) textdomain(TEXT_DOMAIN);
209 
210 	/*
211 	 * This is a profile shell if the simple name of argv[0] is
212 	 * pfsh or -pfsh
213 	 */
214 #ifdef	EXECATTR_FILENAME
215 	if (c > 0 && (eq("pfsh", simple((unsigned char *)*v)) ||
216 	    eq("-pfsh", simple((unsigned char *)*v)) ||
217 	    eq("pfbosh", simple((unsigned char *)*v)) ||
218 	    eq("-pfbosh", simple((unsigned char *)*v)))) {
219 		flags |= pfshflg;
220 		secpolicy_init();
221 	}
222 #endif
223 
224 	/*
225 	 * 'rsflag' is zero if SHELL variable is
226 	 *  set in environment and
227 	 *  the simple file part of the value.
228 	 *  is rsh
229 	 */
230 	if ((n = findnam((unsigned char *)"SHELL")) != NULL) {
231 		if (eq("rsh", simple(n->namval)) ||
232 		    eq("rbosh", simple(n->namval)))
233 			rsflag = 0;
234 	}
235 
236 	/*
237 	 * a shell is also restricted if the simple name of argv(0) is
238 	 * rsh or -rsh in its simple name
239 	 */
240 
241 #ifndef RES
242 
243 	if (c > 0 && (eq("rsh", simple((unsigned char *)*v)) ||
244 	    eq("-rsh", simple((unsigned char *)*v)) ||
245 	    eq("rbosh", simple((unsigned char *)*v)) ||
246 	    eq("-rbosh", simple((unsigned char *)*v))))
247 		rflag = 0;
248 
249 #endif
250 
251 	if (eq("jsh", simple((unsigned char *)*v)) ||
252 	    eq("-jsh", simple((unsigned char *)*v)) ||
253 	    eq("jbosh", simple((unsigned char *)*v)) ||
254 	    eq("-jbosh", simple((unsigned char *)*v)))
255 		flags |= monitorflg;
256 
257 #ifdef	DO_GLOBSKIPDOT_DEF
258 	flags2 |= globskipdot;
259 #endif
260 #ifdef	DO_ALWAYS_POSIX_SH
261 	flags2 |= posixflg;
262 #else
263 #ifdef	DO_POSIX_SH
264 	/*
265 	 * If the last path name component is "sh", set -o posix by default.
266 	 * This may be the right way for Linux, but probably not for Solaris.
267 	 */
268 	if (eq("sh", simple((unsigned char *)*v)))
269 		flags2 |= posixflg;
270 #else
271 #ifdef	DO_POSIX_PATH
272 	/*
273 	 * If the last path name component is not "sh", it may behave different.
274 	 */
275 	if (eq("sh", simple((unsigned char *)*v))) {
276 #ifdef	HAVE_GETEXECNAME
277 		const char	*exname = getexecname();
278 #else
279 			char	*exname = getexecpath();
280 #endif
281 		if (exname) {
282 			if (strstr(exname, "/xpg4"))	/* X-Open interface? */
283 				flags2 |= posixflg;
284 
285 #ifdef	POSIX_BOSH_PATH
286 			if (eq(POSIX_BOSH_PATH, exname)) {
287 				flags2 |= posixflg;
288 			} else if (**v == '/' &&
289 			    eq(POSIX_BOSH_PATH, (unsigned char *)*v)) {
290 				flags2 |= posixflg;
291 			} else {
292 				char	*p;
293 
294 				/*
295 				 * We like to recognise symlinks as well,
296 				 * so we cannot base results on getexecname() or
297 				 * getexecpath(). Use SIP_ONLY_PATH to only do
298 				 * a PATH based search.
299 				 */
300 				p = searchfileinpath(*v, X_OK,
301 					SIP_PLAIN_FILE |
302 					SIP_ONLY_PATH |
303 					SIP_NO_STRIPBIN, NULL);
304 				if (p && eq(POSIX_BOSH_PATH, p))
305 					flags2 |= posixflg;
306 				if (p)
307 					libc_free(p);
308 			}
309 #endif	/* POSIX_BOSH_PATH */
310 #ifndef	HAVE_GETEXECNAME
311 			libc_free(exname);
312 #endif
313 		}
314 	}
315 #endif	/* DO_POSIX_PATH */
316 #endif	/* DO_POSIX_SH */
317 #endif	/* DO_ALWAYS_POSIX_SH */
318 	posix = flags2 & posixflg;	/* remember "auto-posix" value */
319 
320 	hcreate();
321 	set_dotpath();
322 #ifdef	DO_DOL_SLASH
323 	shmcreate();
324 #endif
325 
326 	/*
327 	 * look for options
328 	 * dolc is $#
329 	 */
330 	dolc = options(c, (unsigned char **)v);
331 
332 	if (dolc < 2) {
333 		flags |= stdflg;
334 		setopts();				/* set flagadr */
335 	}
336 	if ((flags & stdflg) == 0)
337 		dolc--;
338 
339 #ifdef	DO_EXPORT_ENV
340 	namscan(exportenv);
341 #else
342 #ifdef	DO_POSIX_EXPORT_ENV
343 	if (flags2 & posixflg)
344 		namscan(exportenv);
345 #endif
346 #endif
347 
348 	if ((flags & privflg) == 0) {
349 		uid_t euid;
350 		gid_t egid;
351 		uid_t ruid;
352 		gid_t rgid;
353 
354 		/*
355 		 * Determine all of the user's id #'s for this process and
356 		 * then decide if this shell is being entered as a result
357 		 * of a fork/exec.
358 		 * If the effective uid/gid do NOT match and the euid/egid
359 		 * is < 100 and the egid is NOT 1, reset the uid and gid to
360 		 * the user originally calling this process.
361 		 */
362 		euid = geteuid();
363 		ruid = getuid();
364 		egid = getegid();
365 		rgid = getgid();
366 		if ((euid != ruid) && (euid < 100))
367 			setuid(ruid);   /* reset the uid to the orig user */
368 		if ((egid != rgid) && ((egid < 100) && (egid != 1)))
369 			setgid(rgid);   /* reset the gid to the orig user */
370 	}
371 
372 	dolv = (unsigned char **)v + c - dolc;
373 	dolc--;
374 
375 	/*
376 	 * return here for shell file execution
377 	 * but not for parenthesis subshells
378 	 */
379 	if (setjmp(subshell)) {
380 		freejobs();
381 #ifdef	DO_SYSALIAS
382 		/*
383 		 * Shell scripts start with empty alias definitions.
384 		 * Turn off all aliases and disable persistent aliases.
385 		 */
386 		ab_use(GLOBAL_AB, NULL);
387 		ab_use(LOCAL_AB, NULL);
388 #endif
389 		flags |= subsh;
390 		flags2 |= posix;	/* restore "auto-posix" value */
391 #ifdef	DO_GLOBSKIPDOT_DEF
392 		flags2 |= globskipdot;
393 #endif
394 
395 		mypgid = getpgid(0);	/* get process group of script */
396 	}
397 
398 	/*
399 	 * number of positional parameters
400 	 */
401 	replace(&cmdadr, dolv[0]);	/* cmdadr is $0 */
402 
403 	/*
404 	 * set pidname '$$'
405 	 */
406 	assnum(&pidadr, (long)mypid);
407 
408 	/*
409 	 * set up temp file names
410 	 */
411 	settmp();
412 
413 	/*
414 	 * default internal field separators
415 	 * Do not allow importing of IFS from parent shell.
416 	 * setup_env() may have set anything from parent shell to IFS.
417 	 * Always set the default ifs to IFS.
418 	 */
419 	assign(&ifsnod, (unsigned char *)sptbnl);
420 
421 	dfault(&mchknod, (unsigned char *)MAILCHECK);
422 	mailchk = stoi(mchknod.namval);
423 #ifdef	DO_SYSFC
424 	dfault(&fcenod, (unsigned char *)fcedit);
425 #endif
426 #ifdef	DO_PS34
427 	dfault(&ps3nod, (unsigned char *)selectmsg);
428 	dfault(&ps4nod, (unsigned char *)execpmsg);
429 #endif
430 #ifdef	DO_PPID
431 	itos(getppid());
432 	dfault(&ppidnod, numbuf);
433 #ifdef	__readonly_ppid__
434 	attrib((&ppidnod), N_RDONLY);
435 #endif
436 #endif
437 #ifdef	DO_POSIX_CD
438 	/*
439 	 * XXX Should we set a timeout here in order to allow people to log in
440 	 * XXX when the home directory hangs?
441 	 * XXX Root should not have these problems as root's homedir is local.
442 	 */
443 	cwdget(CHDIR_P);		/* Verify/set PWD as with pwd -P */
444 	attrib(&pwdnod, N_EXPORT);	/* Speedup nested "sh" calls	*/
445 #endif
446 
447 	/* initialize OPTIND for getopt */
448 
449 	n = lookup((unsigned char *)"OPTIND");
450 #ifdef	DO_GETOPT_POSIX
451 	optindnodep = n;
452 #endif
453 	assign(n, (unsigned char *)"1");
454 	/*
455 	 * make sure that option parsing starts
456 	 * at first character
457 	 */
458 	_sp = 1;
459 
460 	if ((beenhere++) == FALSE) {	/* ? profile */
461 		if ((login_shell == TRUE) && (flags & privflg) == 0) {
462 
463 			/* system profile */
464 
465 #ifndef RES
466 
467 			if ((input = pathopen((unsigned char *)nullstr,
468 					(unsigned char *)sysprofile)) >= 0)
469 				exfile(rflag);		/* file exists */
470 
471 #endif
472 			/* user profile */
473 
474 			if ((input = pathopen(homenod.namval?
475 					homenod.namval:UC "",
476 					(unsigned char *)profile)) >= 0) {
477 				exfile(rflag);
478 				flags &= ~ttyflg;
479 			}
480 		}
481 		if (rsflag == 0 || rflag == 0) {
482 			if ((flags & rshflg) == 0) {
483 				flags |= rshflg;
484 				setopts();		/* set flagadr */
485 			}
486 		}
487 #if	defined(INT_DOLMINUS) || defined(INTERACTIVE)
488 		if ((flags & stdflg) && (flags & oneflg) == 0 && comdiv == 0) {
489 			/*
490 			 * This is an interactive shell, mark it as interactive.
491 			 */
492 			if ((flags & intflg) == 0) {
493 				/*
494 				 * Do not switch on interactive mode in case
495 				 * "sh < file" was called.
496 				 */
497 				if (isatty(STDIN_FILENO))
498 					flags |= intflg;
499 			}
500 #ifdef	DO_BGNICE
501 			flags2 |= bgniceflg;
502 #endif
503 #ifdef	INTERACTIVE
504 			flags2 |= vedflg;
505 #endif
506 #ifdef	DO_POSIX_M
507 			/*
508 			 * POSIX requires to auto-enable -m when
509 			 * in interactive mode.
510 			 */
511 			if ((flags & intflg) != 0)
512 				flags |= monitorflg;
513 #endif
514 			setopts();			/* set flagadr */
515 		}
516 #endif
517 		if ((flags & intflg) && (flags & privflg) == 0) {
518 #ifdef	DO_SHRCFILES
519 			unsigned char	*env = envnod.namval;
520 			BOOL		dosysrc = TRUE;
521 
522 			if (env == NULL)
523 				envnod.namval = env = UC rcfile;
524 			env = make(macro(env));
525 
526 			if (env[0] == '/' && env[1] == '.' && env[2] == '/')
527 				dosysrc = FALSE;
528 			else if (env[0] == '.' && env[1] == '/')
529 				dosysrc = FALSE;
530 
531 			flags &= ~intflg;	/* rcfiles: non-interactive */
532 			/* system rcfile */
533 			if (dosysrc &&
534 			    (input = pathopen((unsigned char *)nullstr,
535 					(unsigned char *)sysrcfile)) >= 0)
536 				exfile(rflag);		/* file exists */
537 
538 			/* user rcfile */
539 			if ((input = pathopen((unsigned char *)nullstr,
540 					env)) >= 0) {
541 				exfile(rflag);
542 				flags &= ~ttyflg;
543 			}
544 			flags |= intflg;	/* restore interactive	*/
545 			setopts();		/* and flagadr		*/
546 			free(env);
547 #endif
548 #ifdef	DO_SYSALIAS
549 #ifdef	__never__
550 			/*
551 			 * The only way to have the global and local alias flag
552 			 * set is via the set(1) command and the set command
553 			 * code already reads the global and local alias files
554 			 * when the related flags are set.
555 			 */
556 			if ((flags2 & globalaliasflg) && homenod.namval) {
557 				catpath(homenod.namval, UC globalname);
558 				ab_use(GLOBAL_AB, (char *)make(curstak()));
559 			}
560 			if (flags2 & localaliasflg) {
561 				ab_use(LOCAL_AB, (char *)localname);
562 			}
563 #endif
564 #endif
565 		}
566 
567 		/*
568 		 * open input file if specified
569 		 */
570 		if (comdiv) {		/* comdiv is -c arg */
571 			estabf(comdiv);
572 			input = -1;
573 		} else {
574 			if (flags & stdflg) {
575 				input = 0;
576 			} else {
577 			/*
578 			 * If the command file specified by 'cmdadr'
579 			 * doesn't exist, chkopen() will fail calling
580 			 * exitsh(). If this is a login shell and
581 			 * the $HOME/.profile file does not exist, the
582 			 * above statement "flags &= ~ttyflg" does not
583 			 * get executed and this makes exitsh() call
584 			 * longjmp() instead of exiting. longjmp() will
585 			 * return to the location specified by the last
586 			 * active jmpbuffer, which is the one set up in
587 			 * the function exfile() called after the system
588 			 * profile file is executed (see lines above).
589 			 * This would cause an infinite loop, because
590 			 * chkopen() will continue to fail and exitsh()
591 			 * to call longjmp(). To make exitsh() exit instead
592 			 * of calling longjmp(), we then set the flag forcexit
593 			 * at this stage.
594 			 */
595 
596 				flags |= forcexit;
597 				input = chkopen(cmdadr, O_RDONLY);
598 				flags &= ~forcexit;
599 			}
600 
601 #ifdef ACCT
602 			if (input != 0)
603 				preacct(cmdadr);
604 #endif
605 			comdiv = UC -1;		/* disable "set -c cmd" */
606 		}
607 	}
608 #ifdef pdp11
609 	else
610 		*execargs = (char *)dolv;	/* for `ps' cmd */
611 #endif
612 
613 
614 	exfile(0);
615 	done(0);
616 	return (exitval);	/* Keep lint happy */
617 }
618 
619 static void
exfile(prof)620 exfile(prof)
621 	int	prof;
622 {
623 	time_t	mailtime = 0;	/* Must not be a register variable */
624 	time_t	curtime = 0;
625 
626 	/*
627 	 * move input
628 	 */
629 	if (input > 0) {
630 		Ldup(input, INIO);
631 		input = INIO;
632 	}
633 
634 
635 	setmode(prof);
636 
637 	if (setjmp(errshell)) {
638 		/*
639 		 * clear special "command -p" flag.
640 		 */
641 		flags &= ~ppath;
642 #ifdef	DO_SYSLOCAL
643 		if (localp) {
644 			localp = NULL;
645 			poplvars();
646 		}
647 #endif
648 		if (prof) {
649 			close(input);
650 			/*
651 			 * Reset process group to saved value.
652 			 */
653 			(void) endjobs(0);
654 			return;
655 		}
656 	}
657 	/*
658 	 * error return here
659 	 */
660 
661 	loopcnt = peekc = peekn = 0;
662 	fndef = 0;
663 	nohash = 0;
664 	iopend = 0;
665 
666 	/*
667 	 * initf() initializes the input filehdr or flushes the content of the
668 	 * buffer. Flushing is needed when we come from the errshell longjmp().
669 	 */
670 	if (input >= 0)
671 		initf(input);
672 
673 #ifdef	DO_CHECKBINARY
674 	/*
675 	 * Check wether the script may be a binary file, e.g. from a different
676 	 * architecture and caused a ENOEXEC error.
677 	 */
678 	if (isbinary(standin))
679 		failedx(ERR_NOEXEC, cmdadr, badexec);
680 #endif
681 
682 	/*
683 	 * command loop
684 	 */
685 	for (;;) {
686 		bosh.intrcnt = 0; /* Reset interrupt counter */
687 		tdystak(0, 0);
688 		stakchk();	/* may reduce sbrk */
689 		clearcurjob();	/* clear thisjob */
690 		exitset();
691 
692 		if ((flags & prompt) && standin->fstak == 0 && !eof) {
693 
694 			if (mailp) {
695 				time(&curtime);
696 
697 				if ((curtime - mailtime) >= mailchk) {
698 					chkmail();
699 					mailtime = curtime;
700 				}
701 			}
702 
703 			/* necessary to print jobs in a timely manner */
704 			if (trapnote & TRAPSET)
705 				chktrap();
706 
707 #ifdef	INTERACTIVE
708 			/*
709 			 * Make sure not to use variables from a shared
710 			 * library. This will not work on Mac OS X and
711 			 * on Solaris, it will pull in libshedit even
712 			 * when linked with -zlazyload.
713 			 * This is why we set up the prompt in libshedit
714 			 * indirectly here.
715 			 */
716 #define	EDIT_RPOMPTS	2
717 			if (flags2 & vedflg) {
718 				editpr(0);
719 			} else {
720 #endif
721 #ifdef	DO_PS34
722 				prs(ps_macro(ps1nod.namval, FALSE));
723 #else
724 				prs(ps1nod.namval);	/* Ignores NULL ptr */
725 #endif
726 #ifdef	INTERACTIVE
727 			}
728 #endif
729 
730 #ifdef TIME_OUT
731 			alarm(TIMEOUT);
732 #endif
733 
734 		}
735 
736 		trapnote = 0;
737 		traprecurse = 0;
738 		peekc = readwc();
739 		if (eof) {
740 			/*
741 			 * Reset process group to saved value.
742 			 */
743 			if (endjobs(JOB_STOPPED))
744 				return;
745 			eof = 0;
746 		}
747 
748 #ifdef TIME_OUT
749 		alarm(0);
750 #endif
751 
752 #ifdef	DO_HASHCMDS
753 		if (peekc == '#' && (flags2 & hashcmdsflg)) {
754 			peekc = 0;
755 			hashcmd();
756 		} else
757 #endif
758 		{
759 			struct trenod *t;
760 			t = cmd(NL, MTFLG | SEMIFLG);
761 #ifdef	PARSE_DEBUG
762 			prtree(t, "Commandline: ");
763 #endif
764 			if (t == NULL && flags & ttyflg) {
765 				freejobs();
766 			} else {
767 				execbrk = dotbrk = 0;
768 				execute(t, 0, eflag, no_pipe, no_pipe);
769 			}
770 		}
771 
772 		eof |= (flags & oneflg);
773 
774 	}
775 }
776 
777 /*
778  * Print secondary prompt if not using the history editor.
779  */
780 void
chkpr()781 chkpr()
782 {
783 	if ((flags & prompt) && standin->fstak == 0) {
784 #ifdef	INTERACTIVE
785 		if (flags2 & vedflg) {
786 			editpr(1);
787 		} else {
788 #endif
789 #ifdef	DO_PS34
790 			prs(ps_macro(ps2nod.namval, FALSE));
791 #else
792 			prs(ps2nod.namval);	/* Ignores NULL ptr */
793 #endif
794 #ifdef	INTERACTIVE
795 		}
796 #endif
797 	}
798 }
799 
800 #ifdef	INTERACTIVE
801 static void
editpr(idx)802 editpr(idx)
803 	int	idx;
804 {
805 	if (flags2 & vedflg) {
806 	static	char *prompts[EDIT_RPOMPTS];
807 	static	char palloc[EDIT_RPOMPTS];
808 
809 		if (palloc[idx])
810 			free(prompts[idx]);
811 
812 		if (idx == 0 || prompts[0] == NULL) {
813 			if ((prompts[0] = C ps1nod.namval) == NULL)
814 				prompts[0] = C nullstr;
815 			palloc[0] = FALSE;
816 		}
817 		if (idx == 1 || prompts[1] == NULL) {
818 			if ((prompts[1] = C ps2nod.namval) == NULL)
819 				prompts[1] = C nullstr;
820 			palloc[1] = FALSE;
821 		}
822 #ifdef	DO_PS34
823 		prompts[idx] = C ps_macro(UC prompts[idx], TRUE);
824 		palloc[idx] = TRUE;
825 #endif
826 		shedit_setprompts(idx, EDIT_RPOMPTS, prompts);
827 	}
828 }
829 #endif
830 
831 void
settmp()832 settmp()
833 {
834 	int len;
835 	serial = 0;
836 	/*
837 	 * Should better use %ju and cast to maxint_t,
838 	 * but then we need to call js_snprintf() for portability.
839 	 */
840 	if ((len = snprintf((char *)tmpout, TMPOUTSZ, "/tmp/sh%lu",
841 	    (unsigned long)mypid)) >= TMPOUTSZ) {
842 		/*
843 		 * TMPOUTSZ should be big enough, but if it isn't,
844 		 * we'll at least try to create tmp files with
845 		 * a truncated tmpfile name at tmpout.
846 		 */
847 		tmpout_offset = TMPOUTSZ - 1;
848 	} else {
849 		tmpout_offset = len;
850 	}
851 }
852 
853 /*
854  * dup file descriptor fa to fb, close fa and set fb to close-on-exec
855  */
856 static void
Ldup(fa,fb)857 Ldup(fa, fb)
858 	int	fa;
859 	int	fb;
860 {
861 #ifdef RES
862 
863 	dup(fa | DUPFLG, fb);
864 	close(fa);
865 	ioctl(fb, FIOCLEX, 0);
866 
867 #else
868 
869 	if (fa >= 0) {
870 		if (fa != fb) {
871 			close(fb);
872 			(void) fcntl(fa, F_DUPFD, fb);	/* normal dup */
873 			close(fa);
874 		}
875 		(void) fcntl(fb, F_SETFD, FD_CLOEXEC);	/* autoclose for fb */
876 	}
877 
878 #endif
879 }
880 
881 void
chkmail()882 chkmail()
883 {
884 	unsigned char	*s = mailp;
885 	unsigned char	*save;
886 
887 	long	*ptr = mod_time;
888 	unsigned char	*start;
889 	BOOL	flg;
890 	struct stat	statb;
891 
892 	while (*s) {
893 		start = s;
894 		save = 0;
895 		flg = 0;
896 
897 		while (*s) {
898 			if (*s != COLON) {
899 				if (*s == '%' && save == 0)
900 					save = s;
901 
902 				s++;
903 			} else {
904 				flg = 1;
905 				*s = 0;
906 			}
907 		}
908 
909 		if (save)
910 			*save = 0;
911 
912 		if (*start && stat((const char *)start, &statb) >= 0) {
913 			if (statb.st_size && *ptr &&
914 			    statb.st_mtime != *ptr) {
915 				if (save) {
916 					prs(save+1);
917 					newline();
918 				}
919 				else
920 					prs(_gettext(mailmsg));
921 			}
922 			*ptr = statb.st_mtime;
923 		} else if (*ptr == 0)
924 			*ptr = 1;
925 
926 		if (save)
927 			*save = '%';
928 
929 		if (flg)
930 			*s++ = COLON;
931 
932 		ptr++;
933 	}
934 }
935 
936 void
setmail(mailpath)937 setmail(mailpath)
938 	unsigned char	*mailpath;
939 {
940 	unsigned char	*s = mailpath;
941 	int		cnt = 1;
942 
943 	long	*ptr;
944 
945 	free(mod_time);
946 	if ((mailp = mailpath) != NULL) {
947 		while (*s) {
948 			if (*s == COLON)
949 				cnt += 1;
950 
951 			s++;
952 		}
953 
954 		ptr = mod_time = (long *)alloc(sizeof (long) * cnt);
955 
956 		while (cnt) {
957 			*ptr = 0;
958 			ptr++;
959 			cnt--;
960 		}
961 	}
962 }
963 
964 void
setmode(prof)965 setmode(prof)
966 	int	prof;
967 {
968 	/*
969 	 * decide whether interactive
970 	 */
971 
972 	if ((flags & intflg) ||
973 	    ((flags&oneflg) == 0 &&
974 	    isatty(output) &&
975 	    isatty(input))) {
976 		dfault(&ps1nod, (unsigned char *)(geteuid() ?
977 						stdprompt : supprompt));
978 		dfault(&ps2nod, (unsigned char *)readmsg);
979 		flags |= ttyflg | prompt;
980 		if (mailpnod.namflg != N_DEFAULT)
981 			setmail(mailpnod.namval);
982 		else
983 			setmail(mailnod.namval);
984 		/*
985 		 * Set job control and make me a process group leader
986 		 */
987 		startjobs();
988 	} else {
989 		flags |= prof;
990 		flags &= ~prompt;
991 	}
992 }
993 
994 /*
995  * A generic call back routine to output error messages from the
996  * policy backing functions called by pfsh.
997  *
998  * msg must contain '\n' if a new line is to be printed.
999  */
1000 void
secpolicy_print(level,msg)1001 secpolicy_print(level, msg)
1002 	int		level;
1003 	const char	*msg;
1004 {
1005 	switch (level) {
1006 	case SECPOLICY_WARN:
1007 	default:
1008 		prs(_gettext(msg));
1009 		return;
1010 	case SECPOLICY_ERROR:
1011 		error(msg);
1012 		break;
1013 	}
1014 }
1015 
1016 static void
bosh_init()1017 bosh_init()
1018 {
1019 	bosh.intrcnt	= 0;
1020 	bosh.flagsp	= &flags;
1021 	bosh.flagsp2	= &flags2;
1022 	bosh.get_envptr	= get_envptr;
1023 	bosh.callsh	= callsh;
1024 }
1025