main.c revision 201053
1270096Strasz/*-
2270096Strasz * Copyright (c) 1991, 1993
3270096Strasz *	The Regents of the University of California.  All rights reserved.
4270096Strasz *
5270096Strasz * This code is derived from software contributed to Berkeley by
6270096Strasz * Kenneth Almquist.
7270096Strasz *
8270096Strasz * Redistribution and use in source and binary forms, with or without
9270096Strasz * modification, are permitted provided that the following conditions
10270096Strasz * are met:
11270096Strasz * 1. Redistributions of source code must retain the above copyright
12270096Strasz *    notice, this list of conditions and the following disclaimer.
13270096Strasz * 2. Redistributions in binary form must reproduce the above copyright
14270096Strasz *    notice, this list of conditions and the following disclaimer in the
15270096Strasz *    documentation and/or other materials provided with the distribution.
16270096Strasz * 4. Neither the name of the University nor the names of its contributors
17270096Strasz *    may be used to endorse or promote products derived from this software
18270096Strasz *    without specific prior written permission.
19270096Strasz *
20270096Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21270096Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22270096Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23270096Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24270096Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25270096Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26270096Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27270096Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28270096Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29270096Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30270096Strasz * SUCH DAMAGE.
31270096Strasz */
32270096Strasz
33270096Strasz#ifndef lint
34270096Straszstatic char const copyright[] =
35270096Strasz"@(#) Copyright (c) 1991, 1993\n\
36270096Strasz	The Regents of the University of California.  All rights reserved.\n";
37270096Strasz#endif /* not lint */
38270096Strasz
39270096Strasz#ifndef lint
40270096Strasz#if 0
41270096Straszstatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/28/95";
42270096Strasz#endif
43270096Strasz#endif /* not lint */
44270096Strasz#include <sys/cdefs.h>
45272403Strasz__FBSDID("$FreeBSD: head/bin/sh/main.c 201053 2009-12-27 18:04:05Z jilles $");
46270096Strasz
47270096Strasz#include <stdio.h>
48270096Strasz#include <signal.h>
49270096Strasz#include <sys/stat.h>
50270281Strasz#include <unistd.h>
51270096Strasz#include <fcntl.h>
52270096Strasz#include <locale.h>
53270096Strasz#include <errno.h>
54270096Strasz
55270402Strasz#include "shell.h"
56270402Strasz#include "main.h"
57270096Strasz#include "mail.h"
58270096Strasz#include "options.h"
59270096Strasz#include "output.h"
60270096Strasz#include "parser.h"
61270096Strasz#include "nodes.h"
62270096Strasz#include "expand.h"
63270096Strasz#include "eval.h"
64270096Strasz#include "jobs.h"
65270096Strasz#include "input.h"
66270096Strasz#include "trap.h"
67270096Strasz#include "var.h"
68270096Strasz#include "show.h"
69270096Strasz#include "memalloc.h"
70270096Strasz#include "error.h"
71270096Strasz#include "init.h"
72270096Strasz#include "mystring.h"
73270096Strasz#include "exec.h"
74270096Strasz#include "cd.h"
75270096Strasz
76270096Straszint rootpid;
77270096Straszint rootshell;
78270096Straszstruct jmploc main_handler;
79270096Strasz
80270096StraszSTATIC void read_profile(const char *);
81270096StraszSTATIC char *find_dot_file(char *);
82270096Strasz
83270096Strasz/*
84270096Strasz * Main routine.  We initialize things, parse the arguments, execute
85270096Strasz * profiles if we're a login shell, and then call cmdloop to execute
86270096Strasz * commands.  The setjmp call sets up the location to jump to when an
87270096Strasz * exception occurs.  When an exception occurs the variable "state"
88270096Strasz * is used to figure out how far we had gotten.
89270096Strasz */
90270096Strasz
91270096Straszint
92270096Straszmain(int argc, char *argv[])
93270096Strasz{
94270096Strasz	struct stackmark smark;
95270096Strasz	volatile int state;
96270096Strasz	char *shinit;
97270096Strasz
98270096Strasz	(void) setlocale(LC_ALL, "");
99270096Strasz	state = 0;
100270096Strasz	if (setjmp(main_handler.loc)) {
101270096Strasz		/*
102270096Strasz		 * When a shell procedure is executed, we raise the
103270096Strasz		 * exception EXSHELLPROC to clean up before executing
104270096Strasz		 * the shell procedure.
105270096Strasz		 */
106270096Strasz		switch (exception) {
107270096Strasz		case EXSHELLPROC:
108270096Strasz			rootpid = getpid();
109270096Strasz			rootshell = 1;
110270096Strasz			minusc = NULL;
111270096Strasz			state = 3;
112270096Strasz			break;
113270096Strasz
114270096Strasz		case EXEXEC:
115270096Strasz			exitstatus = exerrno;
116270096Strasz			break;
117270096Strasz
118270096Strasz		case EXERROR:
119270096Strasz			exitstatus = 2;
120270096Strasz			break;
121270096Strasz
122270096Strasz		default:
123270096Strasz			break;
124270096Strasz		}
125270096Strasz
126270096Strasz		if (exception != EXSHELLPROC) {
127270096Strasz		    if (state == 0 || iflag == 0 || ! rootshell)
128270096Strasz			    exitshell(exitstatus);
129270096Strasz		}
130270096Strasz		reset();
131270096Strasz		if (exception == EXINT) {
132270096Strasz			out2c('\n');
133270096Strasz			flushout(&errout);
134270096Strasz		}
135270096Strasz		popstackmark(&smark);
136270096Strasz		FORCEINTON;				/* enable interrupts */
137270096Strasz		if (state == 1)
138270096Strasz			goto state1;
139270096Strasz		else if (state == 2)
140270096Strasz			goto state2;
141270096Strasz		else if (state == 3)
142270096Strasz			goto state3;
143270096Strasz		else
144270096Strasz			goto state4;
145270096Strasz	}
146270096Strasz	handler = &main_handler;
147270096Strasz#ifdef DEBUG
148270096Strasz	opentrace();
149270096Strasz	trputs("Shell args:  ");  trargs(argv);
150270096Strasz#endif
151270096Strasz	rootpid = getpid();
152270096Strasz	rootshell = 1;
153270096Strasz	init();
154270096Strasz	setstackmark(&smark);
155270402Strasz	procargs(argc, argv);
156270096Strasz	if (getpwd() == NULL && iflag)
157270096Strasz		out2fmt_flush("sh: cannot determine working directory\n");
158270096Strasz	if (getpwd() != NULL)
159270096Strasz		setvar ("PWD", getpwd(), VEXPORT);
160270096Strasz	if (argv[0] && argv[0][0] == '-') {
161270096Strasz		state = 1;
162270096Strasz		read_profile("/etc/profile");
163270096Straszstate1:
164270096Strasz		state = 2;
165270096Strasz		if (privileged == 0)
166270096Strasz			read_profile(".profile");
167270402Strasz		else
168270096Strasz			read_profile("/etc/suid_profile");
169270096Strasz	}
170270096Straszstate2:
171270096Strasz	state = 3;
172270096Strasz	if (!privileged && iflag) {
173270096Strasz		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
174270096Strasz			state = 3;
175270096Strasz			read_profile(shinit);
176270096Strasz		}
177270096Strasz	}
178270096Straszstate3:
179270096Strasz	state = 4;
180270096Strasz	if (minusc) {
181270096Strasz		evalstring(minusc, sflag ? 0 : EV_EXIT);
182270096Strasz	}
183270096Strasz	if (sflag || minusc == NULL) {
184270096Straszstate4:	/* XXX ??? - why isn't this before the "if" statement */
185270096Strasz		cmdloop(1);
186270096Strasz	}
187270096Strasz	exitshell(exitstatus);
188270096Strasz	/*NOTREACHED*/
189270096Strasz	return 0;
190270096Strasz}
191270096Strasz
192270096Strasz
193270096Strasz/*
194270096Strasz * Read and execute commands.  "Top" is nonzero for the top level command
195270096Strasz * loop; it turns on prompting if the shell is interactive.
196270096Strasz */
197270096Strasz
198270096Straszvoid
199270096Straszcmdloop(int top)
200270096Strasz{
201272512Strasz	union node *n;
202270207Strasz	struct stackmark smark;
203270207Strasz	int inter;
204270207Strasz	int numeof = 0;
205270207Strasz
206272512Strasz	TRACE(("cmdloop(%d) called\n", top));
207270207Strasz	setstackmark(&smark);
208270207Strasz	for (;;) {
209270207Strasz		if (pendingsigs)
210270096Strasz			dotrap();
211270096Strasz		inter = 0;
212270096Strasz		if (iflag && top) {
213270096Strasz			inter++;
214270096Strasz			showjobs(1, SHOWJOBS_DEFAULT);
215270096Strasz			chkmail(0);
216270096Strasz			flushout(&output);
217270096Strasz		}
218270096Strasz		n = parsecmd(inter);
219270096Strasz		/* showtree(n); DEBUG */
220270096Strasz		if (n == NEOF) {
221270096Strasz			if (!top || numeof >= 50)
222270096Strasz				break;
223270096Strasz			if (!stoppedjobs()) {
224270096Strasz				if (!Iflag)
225270096Strasz					break;
226270096Strasz				out2fmt_flush("\nUse \"exit\" to leave shell.\n");
227270096Strasz			}
228270096Strasz			numeof++;
229270207Strasz		} else if (n != NULL && nflag == 0) {
230270207Strasz			job_warning = (job_warning == 2) ? 1 : 0;
231270207Strasz			numeof = 0;
232270207Strasz			evaltree(n, 0);
233270207Strasz		}
234270096Strasz		popstackmark(&smark);
235270207Strasz		setstackmark(&smark);
236272512Strasz		if (evalskip == SKIPFILE) {
237270096Strasz			evalskip = 0;
238270207Strasz			break;
239270096Strasz		}
240270207Strasz	}
241270096Strasz	popstackmark(&smark);
242270096Strasz}
243270096Strasz
244270096Strasz
245270096Strasz
246270096Strasz/*
247270096Strasz * Read /etc/profile or .profile.  Return on error.
248270096Strasz */
249270096Strasz
250270096StraszSTATIC void
251270096Straszread_profile(const char *name)
252270096Strasz{
253270096Strasz	int fd;
254270096Strasz
255270096Strasz	INTOFF;
256270096Strasz	if ((fd = open(name, O_RDONLY)) >= 0)
257270096Strasz		setinputfd(fd, 1);
258270096Strasz	INTON;
259270096Strasz	if (fd < 0)
260270096Strasz		return;
261270096Strasz	cmdloop(0);
262270096Strasz	popfile();
263270096Strasz}
264270096Strasz
265270096Strasz
266270096Strasz
267270096Strasz/*
268270096Strasz * Read a file containing shell functions.
269270096Strasz */
270270096Strasz
271270096Straszvoid
272270096Straszreadcmdfile(const char *name)
273270096Strasz{
274270096Strasz	int fd;
275270096Strasz
276270096Strasz	INTOFF;
277270096Strasz	if ((fd = open(name, O_RDONLY)) >= 0)
278270096Strasz		setinputfd(fd, 1);
279270096Strasz	else
280272470Strasz		error("Can't open %s: %s", name, strerror(errno));
281270096Strasz	INTON;
282270096Strasz	cmdloop(0);
283270096Strasz	popfile();
284272470Strasz}
285270096Strasz
286270096Strasz
287270096Strasz
288272470Strasz/*
289270096Strasz * Take commands from a file.  To be compatible we should do a path
290270096Strasz * search for the file, which is necessary to find sub-commands.
291270096Strasz */
292270096Strasz
293270096Strasz
294270096StraszSTATIC char *
295272470Straszfind_dot_file(char *basename)
296270096Strasz{
297272512Strasz	static char localname[FILENAME_MAX+1];
298270096Strasz	char *fullname;
299270096Strasz	const char *path = pathval();
300270096Strasz	struct stat statb;
301270096Strasz
302270096Strasz	/* don't try this for absolute or relative paths */
303270096Strasz	if( strchr(basename, '/'))
304270096Strasz		return basename;
305270096Strasz
306270096Strasz	while ((fullname = padvance(&path, basename)) != NULL) {
307270096Strasz		strcpy(localname, fullname);
308270096Strasz		stunalloc(fullname);
309270096Strasz		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
310270096Strasz			return localname;
311270096Strasz	}
312270096Strasz	return basename;
313270096Strasz}
314270096Strasz
315270096Straszint
316270096Straszdotcmd(int argc, char **argv)
317270096Strasz{
318270096Strasz	char *fullname;
319270096Strasz
320270096Strasz	if (argc < 2)
321270096Strasz		error("missing filename");
322270096Strasz
323270096Strasz	exitstatus = 0;
324270096Strasz
325270096Strasz	fullname = find_dot_file(argv[1]);
326270096Strasz	setinputfile(fullname, 1);
327270096Strasz	commandname = fullname;
328272470Strasz	cmdloop(0);
329270096Strasz	popfile();
330270096Strasz	return exitstatus;
331270096Strasz}
332272470Strasz
333270096Strasz
334270096Straszint
335272470Straszexitcmd(int argc, char **argv)
336270096Strasz{
337272512Strasz	if (stoppedjobs())
338270096Strasz		return 0;
339270096Strasz	if (argc > 1)
340270096Strasz		exitstatus = number(argv[1]);
341270096Strasz	else
342270096Strasz		exitstatus = oexitstatus;
343270096Strasz	exitshell(exitstatus);
344270096Strasz	/*NOTREACHED*/
345270096Strasz	return 0;
346270096Strasz}
347270096Strasz