xref: /titanic_44/usr/src/cmd/sh/main.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 /*
35  * UNIX shell
36  */
37 
38 #include	"defs.h"
39 #include	"sym.h"
40 #include	"timeout.h"
41 #include	<sys/types.h>
42 #include	<sys/stat.h>
43 #include	<sys/wait.h>
44 #include	"dup.h"
45 #include	"sh_policy.h"
46 
47 #ifdef RES
48 #include	<sgtty.h>
49 #endif
50 
51 pid_t mypid, mypgid, mysid;
52 
53 static BOOL	beenhere = FALSE;
54 unsigned char		tmpout[20] = "/tmp/sh-";
55 struct fileblk	stdfile;
56 struct fileblk *standin = &stdfile;
57 int mailchk = 0;
58 
59 static unsigned char	*mailp;
60 static long	*mod_time = 0;
61 static BOOL login_shell = FALSE;
62 
63 #if vax
64 char **execargs = (char **)(0x7ffffffc);
65 #endif
66 
67 #if pdp11
68 char **execargs = (char **)(-2);
69 #endif
70 
71 
72 static int	exfile();
73 extern unsigned char 	*simple();
74 
75 
76 main(c, v, e)
77 int	c;
78 char	*v[];
79 char	*e[];
80 {
81 	register int	rflag = ttyflg;
82 	int		rsflag = 1;	/* local restricted flag */
83 	register unsigned char *flagc = flagadr;
84 	struct namnod	*n;
85 
86 	mypid = getpid();
87 	mypgid = getpgid(mypid);
88 	mysid = getsid(mypid);
89 
90 	/*
91 	 * Do locale processing only if /usr is mounted.
92 	 */
93 	localedir_exists = (access(localedir, F_OK) == 0);
94 
95 	/*
96 	 * initialize storage allocation
97 	 */
98 
99 	if (stakbot == 0) {
100 	addblok((unsigned)0);
101 	}
102 
103 	/*
104 	 * If the first character of the last path element of v[0] is "-"
105 	 * (ex. -sh, or /bin/-sh), this is a login shell
106 	 */
107 	if (*simple(v[0]) == '-') {
108 		signal(SIGXCPU, SIG_DFL);
109 		signal(SIGXFSZ, SIG_DFL);
110 
111 		/*
112 		 * As the previous comment states, this is a login shell.
113 		 * Therefore, we set the login_shell flag to explicitly
114 		 * indicate this condition.
115 		 */
116 		login_shell = TRUE;
117 	}
118 
119 	stdsigs();
120 
121 	/*
122 	 * set names from userenv
123 	 */
124 
125 	setup_env();
126 
127 	/*
128 	 * LC_MESSAGES is set here so that early error messages will
129 	 * come out in the right style.
130 	 * Note that LC_CTYPE is done later on and is *not*
131 	 * taken from the previous environ
132 	 */
133 
134 	/*
135 	 * Do locale processing only if /usr is mounted.
136 	 */
137 	if (localedir_exists)
138 		(void) setlocale(LC_ALL, "");
139 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
140 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
141 #endif
142 	(void) textdomain(TEXT_DOMAIN);
143 
144 	/*
145 	 * This is a profile shell if the simple name of argv[0] is
146 	 * pfsh or -pfsh
147 	 */
148 	if (c > 0 && (eq("pfsh", simple(*v)) || eq("-pfsh", simple(*v)))) {
149 		flags |= pfshflg;
150 		secpolicy_init();
151 	}
152 
153 	/*
154 	 * 'rsflag' is zero if SHELL variable is
155 	 *  set in environment and
156 	 *  the simple file part of the value.
157 	 *  is rsh
158 	 */
159 	if (n = findnam("SHELL"))
160 	{
161 		if (eq("rsh", simple(n->namval)))
162 			rsflag = 0;
163 	}
164 
165 	/*
166 	 * a shell is also restricted if the simple name of argv(0) is
167 	 * rsh or -rsh in its simple name
168 	 */
169 
170 #ifndef RES
171 
172 	if (c > 0 && (eq("rsh", simple(*v)) || eq("-rsh", simple(*v))))
173 		rflag = 0;
174 
175 #endif
176 
177 	if (eq("jsh", simple(*v)) || eq("-jsh", simple(*v)))
178 		flags |= monitorflg;
179 
180 	hcreate();
181 	set_dotpath();
182 
183 
184 	/*
185 	 * look for options
186 	 * dolc is $#
187 	 */
188 	dolc = options(c, v);
189 
190 	if (dolc < 2)
191 	{
192 		flags |= stdflg;
193 		{
194 
195 			while (*flagc)
196 				flagc++;
197 			*flagc++ = STDFLG;
198 			*flagc = 0;
199 		}
200 	}
201 	if ((flags & stdflg) == 0)
202 		dolc--;
203 
204 	if ((flags & privflg) == 0) {
205 		register uid_t euid;
206 		register gid_t egid;
207 		register uid_t ruid;
208 		register gid_t rgid;
209 
210 		/*
211 		 * Determine all of the user's id #'s for this process and
212 		 * then decide if this shell is being entered as a result
213 		 * of a fork/exec.
214 		 * If the effective uid/gid do NOT match and the euid/egid
215 		 * is < 100 and the egid is NOT 1, reset the uid and gid to
216 		 * the user originally calling this process.
217 		 */
218 		euid = geteuid();
219 		ruid = getuid();
220 		egid = getegid();
221 		rgid = getgid();
222 		if ((euid != ruid) && (euid < 100))
223 			setuid(ruid);   /* reset the uid to the orig user */
224 		if ((egid != rgid) && ((egid < 100) && (egid != 1)))
225 			setgid(rgid);   /* reset the gid to the orig user */
226 	}
227 
228 	dolv = (unsigned char **)v + c - dolc;
229 	dolc--;
230 
231 	/*
232 	 * return here for shell file execution
233 	 * but not for parenthesis subshells
234 	 */
235 	if (setjmp(subshell)) {
236 		freejobs();
237 		flags |= subsh;
238 	}
239 
240 	/*
241 	 * number of positional parameters
242 	 */
243 	replace(&cmdadr, dolv[0]);	/* cmdadr is $0 */
244 
245 	/*
246 	 * set pidname '$$'
247 	 */
248 	assnum(&pidadr, (long)mypid);
249 
250 	/*
251 	 * set up temp file names
252 	 */
253 	settmp();
254 
255 	/*
256 	 * default internal field separators
257 	 * Do not allow importing of IFS from parent shell.
258 	 * setup_env() may have set anything from parent shell to IFS.
259 	 * Always set the default ifs to IFS.
260 	 */
261 	assign(&ifsnod, sptbnl);
262 
263 	dfault(&mchknod, MAILCHECK);
264 	mailchk = stoi(mchknod.namval);
265 
266 	/* initialize OPTIND for getopt */
267 
268 	n = lookup("OPTIND");
269 	assign(n, "1");
270 	/*
271 	 * make sure that option parsing starts
272 	 * at first character
273 	 */
274 	_sp = 1;
275 
276 	/* initialize multibyte information */
277 	setwidth();
278 
279 	if ((beenhere++) == FALSE)	/* ? profile */
280 	{
281 		if ((login_shell == TRUE) && (flags & privflg) == 0) {
282 
283 			/* system profile */
284 
285 #ifndef RES
286 
287 			if ((input = pathopen(nullstr, sysprofile)) >= 0)
288 				exfile(rflag);		/* file exists */
289 
290 #endif
291 			/* user profile */
292 
293 			if ((input = pathopen(homenod.namval, profile)) >= 0)
294 			{
295 				exfile(rflag);
296 				flags &= ~ttyflg;
297 			}
298 		}
299 		if (rsflag == 0 || rflag == 0) {
300 			if ((flags & rshflg) == 0) {
301 				while (*flagc)
302 					flagc++;
303 				*flagc++ = 'r';
304 				*flagc = '\0';
305 			}
306 			flags |= rshflg;
307 		}
308 
309 		/*
310 		 * open input file if specified
311 		 */
312 		if (comdiv)
313 		{
314 			estabf(comdiv);
315 			input = -1;
316 		}
317 		else
318 		{
319 			if (flags & stdflg) {
320 				input = 0;
321 			} else {
322 			/*
323 			 * If the command file specified by 'cmdadr'
324 			 * doesn't exist, chkopen() will fail calling
325 			 * exitsh(). If this is a login shell and
326 			 * the $HOME/.profile file does not exist, the
327 			 * above statement "flags &= ~ttyflg" does not
328 			 * get executed and this makes exitsh() call
329 			 * longjmp() instead of exiting. longjmp() will
330 			 * return to the location specified by the last
331 			 * active jmpbuffer, which is the one set up in
332 			 * the function exfile() called after the system
333 			 * profile file is executed (see lines above).
334 			 * This would cause an infinite loop, because
335 			 * chkopen() will continue to fail and exitsh()
336 			 * to call longjmp(). To make exitsh() exit instead
337 			 * of calling longjmp(), we then set the flag forcexit
338 			 * at this stage.
339 			 */
340 
341 				flags |= forcexit;
342 				input = chkopen(cmdadr, 0);
343 				flags &= ~forcexit;
344 			}
345 
346 #ifdef ACCT
347 			if (input != 0)
348 				preacct(cmdadr);
349 #endif
350 			comdiv--;
351 		}
352 	}
353 #ifdef pdp11
354 	else
355 		*execargs = (char *)dolv;	/* for `ps' cmd */
356 #endif
357 
358 
359 	exfile(0);
360 	done(0);
361 }
362 
363 static int
364 exfile(prof)
365 BOOL	prof;
366 {
367 	time_t	mailtime = 0;	/* Must not be a register variable */
368 	time_t 	curtime = 0;
369 
370 	/*
371 	 * move input
372 	 */
373 	if (input > 0)
374 	{
375 		Ldup(input, INIO);
376 		input = INIO;
377 	}
378 
379 
380 	setmode(prof);
381 
382 	if (setjmp(errshell) && prof)
383 	{
384 		close(input);
385 		(void) endjobs(0);
386 		return;
387 	}
388 	/*
389 	 * error return here
390 	 */
391 
392 	loopcnt = peekc = peekn = 0;
393 	fndef = 0;
394 	nohash = 0;
395 	iopend = 0;
396 
397 	if (input >= 0)
398 		initf(input);
399 	/*
400 	 * command loop
401 	 */
402 	for (;;)
403 	{
404 		tdystak(0);
405 		stakchk();	/* may reduce sbrk */
406 		exitset();
407 
408 		if ((flags & prompt) && standin->fstak == 0 && !eof)
409 		{
410 
411 			if (mailp)
412 			{
413 				time(&curtime);
414 
415 				if ((curtime - mailtime) >= mailchk)
416 				{
417 					chkmail();
418 					mailtime = curtime;
419 				}
420 			}
421 
422 			/* necessary to print jobs in a timely manner */
423 			if (trapnote & TRAPSET)
424 				chktrap();
425 
426 			prs(ps1nod.namval);
427 
428 #ifdef TIME_OUT
429 			alarm(TIMEOUT);
430 #endif
431 
432 		}
433 
434 		trapnote = 0;
435 		peekc = readwc();
436 		if (eof) {
437 			if (endjobs(JOB_STOPPED))
438 				return;
439 			eof = 0;
440 		}
441 
442 #ifdef TIME_OUT
443 		alarm(0);
444 #endif
445 
446 		{
447 			register struct trenod *t;
448 			t = cmd(NL, MTFLG);
449 			if (t == NULL && flags & ttyflg)
450 				freejobs();
451 			else
452 				execute(t, 0, eflag);
453 		}
454 
455 		eof |= (flags & oneflg);
456 
457 	}
458 }
459 
460 chkpr()
461 {
462 	if ((flags & prompt) && standin->fstak == 0)
463 		prs(ps2nod.namval);
464 }
465 
466 settmp()
467 {
468 	int i;
469 	i = ltos(mypid);
470 	serial = 0;
471 	tmpname = movstr(numbuf + i, &tmpout[TMPNAM]);
472 }
473 
474 Ldup(fa, fb)
475 register int	fa, fb;
476 {
477 #ifdef RES
478 
479 	dup(fa | DUPFLG, fb);
480 	close(fa);
481 	ioctl(fb, FIOCLEX, 0);
482 
483 #else
484 
485 	if (fa >= 0) {
486 		if (fa != fb)
487 		{
488 			close(fb);
489 			fcntl(fa, 0, fb); /* normal dup */
490 			close(fa);
491 		}
492 		fcntl(fb, 2, 1);	/* autoclose for fb */
493 	}
494 
495 #endif
496 }
497 
498 
499 chkmail()
500 {
501 	register unsigned char 	*s = mailp;
502 	register unsigned char	*save;
503 
504 	long	*ptr = mod_time;
505 	unsigned char	*start;
506 	BOOL	flg;
507 	struct stat	statb;
508 
509 	while (*s) {
510 		start = s;
511 		save = 0;
512 		flg = 0;
513 
514 		while (*s) {
515 			if (*s != COLON) {
516 				if (*s == '%' && save == 0)
517 					save = s;
518 
519 				s++;
520 			} else {
521 				flg = 1;
522 				*s = 0;
523 			}
524 		}
525 
526 		if (save)
527 			*save = 0;
528 
529 		if (*start && stat((const char *)start, &statb) >= 0) {
530 			if (statb.st_size && *ptr &&
531 			    statb.st_mtime != *ptr) {
532 				if (save) {
533 					prs(save+1);
534 					newline();
535 				}
536 				else
537 					prs(mailmsg);
538 			}
539 			*ptr = statb.st_mtime;
540 		} else if (*ptr == 0)
541 			*ptr = 1;
542 
543 		if (save)
544 			*save = '%';
545 
546 		if (flg)
547 			*s++ = COLON;
548 
549 		ptr++;
550 	}
551 }
552 
553 
554 setmail(mailpath)
555 	unsigned char *mailpath;
556 {
557 	register unsigned char	*s = mailpath;
558 	register int 	cnt = 1;
559 
560 	long	*ptr;
561 
562 	free(mod_time);
563 	if (mailp = mailpath)
564 	{
565 		while (*s)
566 		{
567 			if (*s == COLON)
568 				cnt += 1;
569 
570 			s++;
571 		}
572 
573 		ptr = mod_time = (long *)alloc(sizeof (long) * cnt);
574 
575 		while (cnt)
576 		{
577 			*ptr = 0;
578 			ptr++;
579 			cnt--;
580 		}
581 	}
582 }
583 
584 void
585 setwidth()
586 {
587 	unsigned char *name = lookup("LC_CTYPE")->namval;
588 	if (!name || !*name)
589 		name = lookup("LANG")->namval;
590 	/*
591 	 * Do locale processing only if /usr is mounted.
592 	 */
593 	if (localedir_exists) {
594 		if (!name || !*name)
595 			(void) setlocale(LC_CTYPE, "C");
596 		else
597 			(void) setlocale(LC_CTYPE, (const char *)name);
598 	}
599 }
600 
601 setmode(prof)
602 {
603 	/*
604 	 * decide whether interactive
605 	 */
606 
607 	if ((flags & intflg) ||
608 	    ((flags&oneflg) == 0 &&
609 	    isatty(output) &&
610 	    isatty(input)))
611 
612 	{
613 		dfault(&ps1nod, (geteuid() ? stdprompt : supprompt));
614 		dfault(&ps2nod, readmsg);
615 		flags |= ttyflg | prompt;
616 		if (mailpnod.namflg != N_DEFAULT)
617 			setmail(mailpnod.namval);
618 		else
619 			setmail(mailnod.namval);
620 		startjobs();
621 	}
622 	else
623 	{
624 		flags |= prof;
625 		flags &= ~prompt;
626 	}
627 }
628 
629 /*
630  * A generic call back routine to output error messages from the
631  * policy backing functions called by pfsh.
632  *
633  * msg must contain '\n' if a new line is to be printed.
634  */
635 void
636 secpolicy_print(int level, const char *msg)
637 {
638 	switch (level) {
639 	case SECPOLICY_WARN:
640 	default:
641 		prs(msg);	/* prs() does gettext() */
642 		return;
643 	case SECPOLICY_ERROR:
644 		error(msg);
645 		break;
646 	}
647 }
648