xref: /titanic_44/usr/src/cmd/sh/main.c (revision 134a1f4e3289b54e0f980e9cf05352e419a60bee)
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
main(int c,char * v[],char * e[])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
exfile(int prof)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
chkpr(void)434  chkpr(void)
435  {
436  	if ((flags & prompt) && standin->fstak == 0)
437  		prs(ps2nod.namval);
438  }
439  
440  void
settmp(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
Ldup(int fa,int fb)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
chkmail(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
setmail(unsigned char * mailpath)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
setmode(int prof)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