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
46pid_t mypid, mypgid, mysid;
47
48static BOOL	beenhere = FALSE;
49unsigned char	tmpout[TMPOUTSZ];
50struct fileblk	stdfile;
51struct fileblk *standin = &stdfile;
52int mailchk = 0;
53
54static unsigned char	*mailp;
55static long	*mod_time = 0;
56static BOOL login_shell = FALSE;
57
58#if vax
59char **execargs = (char **)(0x7ffffffc);
60#endif
61
62#if pdp11
63char **execargs = (char **)(-2);
64#endif
65
66
67static void	exfile();
68extern unsigned char 	*simple();
69static void Ldup(int, int);
70void settmp(void);
71void chkmail(void);
72void setmail(unsigned char *);
73
74int
75main(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
343static void
344exfile(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
433void
434chkpr(void)
435{
436	if ((flags & prompt) && standin->fstak == 0)
437		prs(ps2nod.namval);
438}
439
440void
441settmp(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
458static void
459Ldup(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
481void
482chkmail(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
536void
537setmail(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
563void
564setmode(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