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