1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1997-2005
5  *	Herbert Xu <herbert@gondor.apana.org.au>.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <zircon/process.h>
36 #include <zircon/processargs.h>
37 #include <zircon/syscalls.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <signal.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 
45 #include "shell.h"
46 #include "main.h"
47 #include "options.h"
48 #include "output.h"
49 #include "parser.h"
50 #include "nodes.h"
51 #include "expand.h"
52 #include "eval.h"
53 #include "jobs.h"
54 #include "input.h"
55 #include "trap.h"
56 #include "var.h"
57 #include "show.h"
58 #include "memalloc.h"
59 #include "error.h"
60 #include "init.h"
61 #include "mystring.h"
62 #include "exec.h"
63 #include "cd.h"
64 
65 #define PROFILE 0
66 
67 int rootpid;
68 int shlvl;
69 #ifdef __GLIBC__
70 int *dash_errno;
71 #endif
72 #if PROFILE
73 short profile_buf[16384];
74 extern int etext();
75 #endif
76 
77 STATIC void read_profile(const char *);
78 STATIC char *find_dot_file(char *);
79 STATIC void evalifsubshell(zx_handle_t);
80 static int cmdloop(int);
81 int main(int, char **);
82 
83 /*
84  * Main routine.  We initialize things, parse the arguments, execute
85  * profiles if we're a login shell, and then call cmdloop to execute
86  * commands.  The setjmp call sets up the location to jump to when an
87  * exception occurs.  When an exception occurs the variable "state"
88  * is used to figure out how far we had gotten.
89  */
90 
91 int
main(int argc,char ** argv)92 main(int argc, char **argv)
93 {
94 	char *shinit;
95 	volatile int state;
96 	struct jmploc jmploc;
97 	struct stackmark smark;
98 	int login;
99 
100 #ifdef __GLIBC__
101 	dash_errno = __errno_location();
102 #endif
103 
104 #if PROFILE
105 	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
106 #endif
107 	state = 0;
108 	if (unlikely(setjmp(jmploc.loc))) {
109 		int e;
110 		int s;
111 
112 		reset();
113 
114 		e = exception;
115 
116 		s = state;
117 		if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
118 			exitshell();
119 
120 		if (e == EXINT
121 #if ATTY
122 		 && (! attyset() || equal(termval(), "emacs"))
123 #endif
124 		 ) {
125 			out2c('\n');
126 #ifdef FLUSHERR
127 			flushout(out2);
128 #endif
129 		}
130 		popstackmark(&smark);
131 		FORCEINTON;				/* enable interrupts */
132 		if (s == 1)
133 			goto state1;
134 		else if (s == 2)
135 			goto state2;
136 		else if (s == 3)
137 			goto state3;
138 		else
139 			goto state4;
140 	}
141 	handler = &jmploc;
142 #ifdef DEBUG
143 	opentrace();
144 	trputs("Shell args:  ");  trargs(argv);
145 #endif
146 	rootpid = getpid();
147 	init();
148 	setstackmark(&smark);
149 
150 	zx_handle_t ast_vmo = zx_take_startup_handle(PA_HND(PA_USER0, 0));
151 
152 	login = procargs(argc, argv, ast_vmo != ZX_HANDLE_INVALID);
153 
154         // Fuchsia: recognize if we have been invoked for the purpose of evaluating
155 	// an expression (i.e., node) and exiting immediately.
156 	evalifsubshell(ast_vmo);
157 
158 	if (login) {
159 		state = 1;
160 		read_profile("/etc/profile");
161 state1:
162 		state = 2;
163 		read_profile("$HOME/.profile");
164 	}
165 	settitle("sh");
166 state2:
167 	state = 3;
168 	if (
169 #ifndef linux
170 		getuid() == geteuid() && getgid() == getegid() &&
171 #endif
172 		iflag
173 	) {
174 		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
175 			read_profile(shinit);
176 		}
177 	}
178 	popstackmark(&smark);
179 state3:
180 	state = 4;
181 	if (minusc)
182 		evalstring(minusc, sflag ? 0 : EV_EXIT);
183 
184 	if (sflag || minusc == NULL) {
185 state4:	/* XXX ??? - why isn't this before the "if" statement */
186 		cmdloop(1);
187 	}
188 #if PROFILE
189 	monitor(0);
190 #endif
191 #if GPROF
192 	{
193 		extern void _mcleanup(void);
194 		_mcleanup();
195 	}
196 #endif
197 	exitshell();
198 	/* NOTREACHED */
199 }
200 
201 STATIC void
evalifsubshell(zx_handle_t ast_vmo)202 evalifsubshell(zx_handle_t ast_vmo)
203 {
204 	if (ast_vmo == ZX_HANDLE_INVALID)
205 		return;
206 
207 	uint64_t size;
208 	zx_status_t status = zx_vmo_get_size(ast_vmo, &size);
209 	if (status != ZX_OK)
210 		exit(status);
211 
212 	char buffer[size];
213 
214 	status = zx_vmo_read(ast_vmo, buffer, 0, size);
215 	if (status < 0)
216 		exit(status);
217 
218 	struct nodelist *nlist = codec_decode(buffer, size);
219 	if (nlist == NULL) {
220 		return;
221 	}
222 	while (nlist->next) {
223 		evaltree(nlist->n, 0);
224 		nlist = nlist->next;
225 	}
226 	evaltree(nlist->n, EV_EXIT);
227 	/* NOTREACHED */
228 }
229 
230 
231 /*
232  * Read and execute commands.  "Top" is nonzero for the top level command
233  * loop; it turns on prompting if the shell is interactive.
234  */
235 
236 static int
cmdloop(int top)237 cmdloop(int top)
238 {
239 	union node *n;
240 	struct stackmark smark;
241 	int inter;
242 	int status = 0;
243 	int numeof = 0;
244 
245 	TRACE(("cmdloop(%d) called\n", top));
246 	for (;;) {
247 		int skip;
248 
249 		setstackmark(&smark);
250 		if (jobctl)
251 			showjobs(out2, SHOW_CHANGED);
252 		inter = 0;
253 		if (iflag && top)
254 			inter++;
255 		n = parsecmd(inter);
256 		/* showtree(n); DEBUG */
257 		if (n == NEOF) {
258 			if (!top || numeof >= 50)
259 				break;
260 			if (!stoppedjobs()) {
261 				if (!Iflag)
262 					break;
263 				out2str("\nUse \"exit\" to leave shell.\n");
264 			}
265 			numeof++;
266 		} else if (nflag == 0) {
267 			int i;
268 
269 			job_warning = (job_warning == 2) ? 1 : 0;
270 			numeof = 0;
271 			i = evaltree(n, 0);
272 			if (n)
273 				status = i;
274 		}
275 		popstackmark(&smark);
276 
277 		skip = evalskip;
278 		if (skip) {
279 			evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
280 			break;
281 		}
282 	}
283 
284 	return status;
285 }
286 
287 
288 
289 /*
290  * Read /etc/profile or .profile.  Return on error.
291  */
292 
293 STATIC void
read_profile(const char * name)294 read_profile(const char *name)
295 {
296 	name = expandstr(name);
297 	if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
298 		return;
299 
300 	cmdloop(0);
301 	popfile();
302 }
303 
304 
305 
306 /*
307  * Read a file containing shell functions.
308  */
309 
310 void
readcmdfile(char * name)311 readcmdfile(char *name)
312 {
313 	setinputfile(name, INPUT_PUSH_FILE);
314 	cmdloop(0);
315 	popfile();
316 }
317 
318 
319 
320 /*
321  * Take commands from a file.  To be compatible we should do a path
322  * search for the file, which is necessary to find sub-commands.
323  */
324 
325 
326 STATIC char *
find_dot_file(char * basename)327 find_dot_file(char *basename)
328 {
329 	char *fullname;
330 	const char *path = pathval();
331 	struct stat statb;
332 
333 	/* don't try this for absolute or relative paths */
334 	if (strchr(basename, '/'))
335 		return basename;
336 
337 	while ((fullname = padvance(&path, basename)) != NULL) {
338 		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
339 			/*
340 			 * Don't bother freeing here, since it will
341 			 * be freed by the caller.
342 			 */
343 			return fullname;
344 		}
345 		stunalloc(fullname);
346 	}
347 
348 	/* not found in the PATH */
349 	sh_error("%s: not found", basename);
350 	/* NOTREACHED */
351 }
352 
353 int
dotcmd(int argc,char ** argv)354 dotcmd(int argc, char **argv)
355 {
356 	int status = 0;
357 
358 	nextopt(nullstr);
359 	argv = argptr;
360 
361 	if (*argv) {
362 		char *fullname;
363 
364 		fullname = find_dot_file(*argv);
365 		setinputfile(fullname, INPUT_PUSH_FILE);
366 		commandname = fullname;
367 		status = cmdloop(0);
368 		popfile();
369 	}
370 
371 	return status;
372 }
373 
374 
375 int
exitcmd(int argc,char ** argv)376 exitcmd(int argc, char **argv)
377 {
378 	if (stoppedjobs())
379 		return 0;
380 
381 	if (argc > 1) {
382 		int status = number(argv[1]);
383 
384 		exitstatus = status;
385 		if (savestatus >= 0)
386 			savestatus = status;
387 	}
388 
389 	exraise(EXEXIT);
390 	/* NOTREACHED */
391 }
392