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
67int rootpid;
68int shlvl;
69#ifdef __GLIBC__
70int *dash_errno;
71#endif
72#if PROFILE
73short profile_buf[16384];
74extern int etext();
75#endif
76
77STATIC void read_profile(const char *);
78STATIC char *find_dot_file(char *);
79STATIC void evalifsubshell(zx_handle_t);
80static int cmdloop(int);
81int 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
91int
92main(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");
161state1:
162		state = 2;
163		read_profile("$HOME/.profile");
164	}
165	settitle("sh");
166state2:
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);
179state3:
180	state = 4;
181	if (minusc)
182		evalstring(minusc, sflag ? 0 : EV_EXIT);
183
184	if (sflag || minusc == NULL) {
185state4:	/* 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
201STATIC void
202evalifsubshell(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
236static int
237cmdloop(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
293STATIC void
294read_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
310void
311readcmdfile(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
326STATIC char *
327find_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
353int
354dotcmd(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
375int
376exitcmd(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