xref: /freebsd/bin/sh/main.c (revision 65f5dd42f11cdcb7716e6cdd09fac2314c144c1f)
14b88c807SRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
44b88c807SRodney W. Grimes  * Copyright (c) 1991, 1993
54b88c807SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
64b88c807SRodney W. Grimes  *
74b88c807SRodney W. Grimes  * This code is derived from software contributed to Berkeley by
84b88c807SRodney W. Grimes  * Kenneth Almquist.
94b88c807SRodney W. Grimes  *
104b88c807SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
114b88c807SRodney W. Grimes  * modification, are permitted provided that the following conditions
124b88c807SRodney W. Grimes  * are met:
134b88c807SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
144b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
154b88c807SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
164b88c807SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
174b88c807SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
18fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
194b88c807SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
204b88c807SRodney W. Grimes  *    without specific prior written permission.
214b88c807SRodney W. Grimes  *
224b88c807SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
234b88c807SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
244b88c807SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254b88c807SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
264b88c807SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
274b88c807SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
284b88c807SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
294b88c807SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
304b88c807SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
314b88c807SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
324b88c807SRodney W. Grimes  * SUCH DAMAGE.
334b88c807SRodney W. Grimes  */
344b88c807SRodney W. Grimes 
35aa9caaf6SPeter Wemm #include <stdio.h>
364b88c807SRodney W. Grimes #include <signal.h>
37aa9caaf6SPeter Wemm #include <sys/stat.h>
38aa9caaf6SPeter Wemm #include <unistd.h>
394b88c807SRodney W. Grimes #include <fcntl.h>
40ba726b8aSAndrey A. Chernov #include <locale.h>
41fba0ed11SWarner Losh #include <errno.h>
42aa9caaf6SPeter Wemm 
434b88c807SRodney W. Grimes #include "shell.h"
444b88c807SRodney W. Grimes #include "main.h"
454b88c807SRodney W. Grimes #include "mail.h"
464b88c807SRodney W. Grimes #include "options.h"
474b88c807SRodney W. Grimes #include "output.h"
484b88c807SRodney W. Grimes #include "parser.h"
494b88c807SRodney W. Grimes #include "nodes.h"
50aa9caaf6SPeter Wemm #include "expand.h"
514b88c807SRodney W. Grimes #include "eval.h"
524b88c807SRodney W. Grimes #include "jobs.h"
534b88c807SRodney W. Grimes #include "input.h"
544b88c807SRodney W. Grimes #include "trap.h"
554b88c807SRodney W. Grimes #include "var.h"
56aa9caaf6SPeter Wemm #include "show.h"
574b88c807SRodney W. Grimes #include "memalloc.h"
584b88c807SRodney W. Grimes #include "error.h"
594b88c807SRodney W. Grimes #include "mystring.h"
60aa9caaf6SPeter Wemm #include "exec.h"
61ab0a2172SSteve Price #include "cd.h"
62338b821bSJilles Tjoelker #include "redir.h"
63454a02b3SJilles Tjoelker #include "builtins.h"
64988b1bb0SBaptiste Daroussin #ifndef NO_HISTORY
65988b1bb0SBaptiste Daroussin #include "myhistedit.h"
66988b1bb0SBaptiste Daroussin #endif
674b88c807SRodney W. Grimes 
684b88c807SRodney W. Grimes int rootpid;
694b88c807SRodney W. Grimes int rootshell;
7029d401c2SJilles Tjoelker struct jmploc main_handler;
7107eb7033SJilles Tjoelker int localeisutf8, initial_localeisutf8;
724b88c807SRodney W. Grimes 
73338b821bSJilles Tjoelker static void reset(void);
74260fc3f4SJilles Tjoelker static void cmdloop(int);
7546c6b52dSJilles Tjoelker static void read_profile(const char *);
7688328642SDavid E. O'Brien static char *find_dot_file(char *);
774b88c807SRodney W. Grimes 
784b88c807SRodney W. Grimes /*
794b88c807SRodney W. Grimes  * Main routine.  We initialize things, parse the arguments, execute
804b88c807SRodney W. Grimes  * profiles if we're a login shell, and then call cmdloop to execute
814b88c807SRodney W. Grimes  * commands.  The setjmp call sets up the location to jump to when an
824b88c807SRodney W. Grimes  * exception occurs.  When an exception occurs the variable "state"
834b88c807SRodney W. Grimes  * is used to figure out how far we had gotten.
844b88c807SRodney W. Grimes  */
854b88c807SRodney W. Grimes 
86aa9caaf6SPeter Wemm int
main(int argc,char * argv[])875134c3f7SWarner Losh main(int argc, char *argv[])
88aa9caaf6SPeter Wemm {
892178e8c2SDaniel Kolesa 	/*
902178e8c2SDaniel Kolesa 	 * As smark is accessed after a longjmp, it cannot be a local in main().
912178e8c2SDaniel Kolesa 	 * The C standard specifies that the values of non-volatile local
922178e8c2SDaniel Kolesa 	 * variables are unspecified after a jump if modified between the
932178e8c2SDaniel Kolesa 	 * setjmp and longjmp.
942178e8c2SDaniel Kolesa 	 */
952178e8c2SDaniel Kolesa 	static struct stackmark smark, smark2;
964b88c807SRodney W. Grimes 	volatile int state;
974b88c807SRodney W. Grimes 	char *shinit;
98*65f5dd42SBaptiste Daroussin 	int login;
994b88c807SRodney W. Grimes 
100ba726b8aSAndrey A. Chernov 	(void) setlocale(LC_ALL, "");
10107eb7033SJilles Tjoelker 	initcharset();
1024b88c807SRodney W. Grimes 	state = 0;
10329d401c2SJilles Tjoelker 	if (setjmp(main_handler.loc)) {
10445496405SJilles Tjoelker 		if (state == 0 || iflag == 0 || ! rootshell ||
10545496405SJilles Tjoelker 		    exception == EXEXIT)
106ab0a2172SSteve Price 			exitshell(exitstatus);
1074b88c807SRodney W. Grimes 		reset();
108aeb5d065SJilles Tjoelker 		if (exception == EXINT)
109aeb5d065SJilles Tjoelker 			out2fmt_flush("\n");
1104b88c807SRodney W. Grimes 		popstackmark(&smark);
1114b88c807SRodney W. Grimes 		FORCEINTON;				/* enable interrupts */
1124b88c807SRodney W. Grimes 		if (state == 1)
1134b88c807SRodney W. Grimes 			goto state1;
1144b88c807SRodney W. Grimes 		else if (state == 2)
1154b88c807SRodney W. Grimes 			goto state2;
1164b88c807SRodney W. Grimes 		else if (state == 3)
1174b88c807SRodney W. Grimes 			goto state3;
1184b88c807SRodney W. Grimes 		else
1194b88c807SRodney W. Grimes 			goto state4;
1204b88c807SRodney W. Grimes 	}
12129d401c2SJilles Tjoelker 	handler = &main_handler;
1224b88c807SRodney W. Grimes #ifdef DEBUG
1234b88c807SRodney W. Grimes 	opentrace();
1244b88c807SRodney W. Grimes 	trputs("Shell args:  ");  trargs(argv);
1254b88c807SRodney W. Grimes #endif
1264b88c807SRodney W. Grimes 	rootpid = getpid();
1274b88c807SRodney W. Grimes 	rootshell = 1;
128bc7f6652SJilles Tjoelker 	INTOFF;
12959e0cc8eSJilles Tjoelker 	initvar();
1304b88c807SRodney W. Grimes 	setstackmark(&smark);
1311a62d884SJilles Tjoelker 	setstackmark(&smark2);
132*65f5dd42SBaptiste Daroussin 	login = procargs(argc, argv);
1331cffe8b8SJilles Tjoelker 	trap_init();
1348eac1f94SJilles Tjoelker 	pwd_init(iflag);
135bc7f6652SJilles Tjoelker 	INTON;
13657063576SJilles Tjoelker 	if (iflag)
13757063576SJilles Tjoelker 		chkmail(1);
138*65f5dd42SBaptiste Daroussin 	if (login) {
1394b88c807SRodney W. Grimes 		state = 1;
1404b88c807SRodney W. Grimes 		read_profile("/etc/profile");
1414b88c807SRodney W. Grimes state1:
1424b88c807SRodney W. Grimes 		state = 2;
143621a31c6SSteve Price 		if (privileged == 0)
144c5f4fe06SJilles Tjoelker 			read_profile("${HOME-}/.profile");
145621a31c6SSteve Price 		else
146621a31c6SSteve Price 			read_profile("/etc/suid_profile");
1474b88c807SRodney W. Grimes 	}
1484b88c807SRodney W. Grimes state2:
1494b88c807SRodney W. Grimes 	state = 3;
1502afa86e7SSteve Price 	if (!privileged && iflag) {
151aa9caaf6SPeter Wemm 		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
1524b88c807SRodney W. Grimes 			state = 3;
1534b88c807SRodney W. Grimes 			read_profile(shinit);
1544b88c807SRodney W. Grimes 		}
155aa9caaf6SPeter Wemm 	}
156988b1bb0SBaptiste Daroussin #ifndef NO_HISTORY
157988b1bb0SBaptiste Daroussin 	if (iflag)
158988b1bb0SBaptiste Daroussin 		histload();
159988b1bb0SBaptiste Daroussin #endif
1604b88c807SRodney W. Grimes state3:
1614b88c807SRodney W. Grimes 	state = 4;
1621a62d884SJilles Tjoelker 	popstackmark(&smark2);
1634b88c807SRodney W. Grimes 	if (minusc) {
164960da934SJilles Tjoelker 		evalstring(minusc, sflag ? 0 : EV_EXIT);
1654b88c807SRodney W. Grimes 	}
166fd7d6d8aSJilles Tjoelker state4:
1674b88c807SRodney W. Grimes 	if (sflag || minusc == NULL) {
1684b88c807SRodney W. Grimes 		cmdloop(1);
1694b88c807SRodney W. Grimes 	}
1704b88c807SRodney W. Grimes 	exitshell(exitstatus);
171aa9caaf6SPeter Wemm 	/*NOTREACHED*/
172aa9caaf6SPeter Wemm 	return 0;
1734b88c807SRodney W. Grimes }
1744b88c807SRodney W. Grimes 
175338b821bSJilles Tjoelker static void
reset(void)176338b821bSJilles Tjoelker reset(void)
177338b821bSJilles Tjoelker {
178338b821bSJilles Tjoelker 	reseteval();
179338b821bSJilles Tjoelker 	resetinput();
180338b821bSJilles Tjoelker }
1814b88c807SRodney W. Grimes 
1824b88c807SRodney W. Grimes /*
1834b88c807SRodney W. Grimes  * Read and execute commands.  "Top" is nonzero for the top level command
1844b88c807SRodney W. Grimes  * loop; it turns on prompting if the shell is interactive.
1854b88c807SRodney W. Grimes  */
1864b88c807SRodney W. Grimes 
187260fc3f4SJilles Tjoelker static void
cmdloop(int top)1885134c3f7SWarner Losh cmdloop(int top)
189aa9caaf6SPeter Wemm {
1904b88c807SRodney W. Grimes 	union node *n;
1914b88c807SRodney W. Grimes 	struct stackmark smark;
1924b88c807SRodney W. Grimes 	int inter;
1934b88c807SRodney W. Grimes 	int numeof = 0;
1944b88c807SRodney W. Grimes 
1954b88c807SRodney W. Grimes 	TRACE(("cmdloop(%d) called\n", top));
1964b88c807SRodney W. Grimes 	setstackmark(&smark);
1974b88c807SRodney W. Grimes 	for (;;) {
19825e0f0f5SJilles Tjoelker 		if (pendingsig)
1994b88c807SRodney W. Grimes 			dotrap();
2004b88c807SRodney W. Grimes 		inter = 0;
2014b88c807SRodney W. Grimes 		if (iflag && top) {
2024b88c807SRodney W. Grimes 			inter++;
203de37e41cSStefan Farfeleder 			showjobs(1, SHOWJOBS_DEFAULT);
2044b88c807SRodney W. Grimes 			chkmail(0);
2054b88c807SRodney W. Grimes 			flushout(&output);
2064b88c807SRodney W. Grimes 		}
2074b88c807SRodney W. Grimes 		n = parsecmd(inter);
2084b88c807SRodney W. Grimes 		/* showtree(n); DEBUG */
2094b88c807SRodney W. Grimes 		if (n == NEOF) {
2104b88c807SRodney W. Grimes 			if (!top || numeof >= 50)
2114b88c807SRodney W. Grimes 				break;
2124b88c807SRodney W. Grimes 			if (!stoppedjobs()) {
2134b88c807SRodney W. Grimes 				if (!Iflag)
2144b88c807SRodney W. Grimes 					break;
215c6204d4aSJilles Tjoelker 				out2fmt_flush("\nUse \"exit\" to leave shell.\n");
2164b88c807SRodney W. Grimes 			}
2174b88c807SRodney W. Grimes 			numeof++;
2184b88c807SRodney W. Grimes 		} else if (n != NULL && nflag == 0) {
2194b88c807SRodney W. Grimes 			job_warning = (job_warning == 2) ? 1 : 0;
2204b88c807SRodney W. Grimes 			numeof = 0;
2214b88c807SRodney W. Grimes 			evaltree(n, 0);
2224b88c807SRodney W. Grimes 		}
2234b88c807SRodney W. Grimes 		popstackmark(&smark);
22484c3800cSMartin Cracauer 		setstackmark(&smark);
225ae7c0700SJilles Tjoelker 		if (evalskip != 0) {
2262935c4ccSJilles Tjoelker 			if (evalskip == SKIPRETURN)
227ab0a2172SSteve Price 				evalskip = 0;
228ab0a2172SSteve Price 			break;
229ab0a2172SSteve Price 		}
2304b88c807SRodney W. Grimes 	}
23184c3800cSMartin Cracauer 	popstackmark(&smark);
2329b2a9780SPiotr Pawel Stefaniak 	if (top && iflag) {
2339b2a9780SPiotr Pawel Stefaniak 		out2c('\n');
2349b2a9780SPiotr Pawel Stefaniak 		flushout(out2);
2359b2a9780SPiotr Pawel Stefaniak 	}
2364b88c807SRodney W. Grimes }
2374b88c807SRodney W. Grimes 
2384b88c807SRodney W. Grimes 
2394b88c807SRodney W. Grimes 
2404b88c807SRodney W. Grimes /*
2414b88c807SRodney W. Grimes  * Read /etc/profile or .profile.  Return on error.
2424b88c807SRodney W. Grimes  */
2434b88c807SRodney W. Grimes 
24488328642SDavid E. O'Brien static void
read_profile(const char * name)24546c6b52dSJilles Tjoelker read_profile(const char *name)
2464b88c807SRodney W. Grimes {
2474b88c807SRodney W. Grimes 	int fd;
2481a62d884SJilles Tjoelker 	const char *expandedname;
249d2c23317SStephane Rochoy 	int oflags = O_RDONLY | O_CLOEXEC;
250d2c23317SStephane Rochoy 
251d2c23317SStephane Rochoy 	if (verifyflag)
252d2c23317SStephane Rochoy 		oflags |= O_VERIFY;
2534b88c807SRodney W. Grimes 
2541a62d884SJilles Tjoelker 	expandedname = expandstr(name);
2551a62d884SJilles Tjoelker 	if (expandedname == NULL)
2561a62d884SJilles Tjoelker 		return;
2574b88c807SRodney W. Grimes 	INTOFF;
258d2c23317SStephane Rochoy 	if ((fd = open(expandedname, oflags)) >= 0)
2594b88c807SRodney W. Grimes 		setinputfd(fd, 1);
2604b88c807SRodney W. Grimes 	INTON;
2614b88c807SRodney W. Grimes 	if (fd < 0)
2624b88c807SRodney W. Grimes 		return;
2634b88c807SRodney W. Grimes 	cmdloop(0);
2644b88c807SRodney W. Grimes 	popfile();
2654b88c807SRodney W. Grimes }
2664b88c807SRodney W. Grimes 
2674b88c807SRodney W. Grimes 
2684b88c807SRodney W. Grimes 
2694b88c807SRodney W. Grimes /*
2704b88c807SRodney W. Grimes  * Read a file containing shell functions.
2714b88c807SRodney W. Grimes  */
2724b88c807SRodney W. Grimes 
2734b88c807SRodney W. Grimes void
readcmdfile(const char * name,int verify)274d2c23317SStephane Rochoy readcmdfile(const char *name, int verify)
2754b88c807SRodney W. Grimes {
276d2c23317SStephane Rochoy 	setinputfile(name, 1, verify);
2774b88c807SRodney W. Grimes 	cmdloop(0);
2784b88c807SRodney W. Grimes 	popfile();
2794b88c807SRodney W. Grimes }
2804b88c807SRodney W. Grimes 
2814b88c807SRodney W. Grimes 
2824b88c807SRodney W. Grimes 
2834b88c807SRodney W. Grimes /*
28446be34b9SKris Kennaway  * Take commands from a file.  To be compatible we should do a path
285aa9caaf6SPeter Wemm  * search for the file, which is necessary to find sub-commands.
2864b88c807SRodney W. Grimes  */
2874b88c807SRodney W. Grimes 
288aa9caaf6SPeter Wemm 
28988328642SDavid E. O'Brien static char *
find_dot_file(char * basename)2905134c3f7SWarner Losh find_dot_file(char *basename)
291aa9caaf6SPeter Wemm {
292aa9caaf6SPeter Wemm 	char *fullname;
2934600b569SJilles Tjoelker 	const char *opt;
2942cac6e36SJilles Tjoelker 	const char *path = pathval();
295aa9caaf6SPeter Wemm 	struct stat statb;
296aa9caaf6SPeter Wemm 
297aa9caaf6SPeter Wemm 	/* don't try this for absolute or relative paths */
298aa9caaf6SPeter Wemm 	if( strchr(basename, '/'))
299aa9caaf6SPeter Wemm 		return basename;
300aa9caaf6SPeter Wemm 
3014600b569SJilles Tjoelker 	while ((fullname = padvance(&path, &opt, basename)) != NULL) {
302c9e93e67SJilles Tjoelker 		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
303c9e93e67SJilles Tjoelker 			/*
304c9e93e67SJilles Tjoelker 			 * Don't bother freeing here, since it will
305c9e93e67SJilles Tjoelker 			 * be freed by the caller.
306c9e93e67SJilles Tjoelker 			 */
307c9e93e67SJilles Tjoelker 			return fullname;
308c9e93e67SJilles Tjoelker 		}
309aa9caaf6SPeter Wemm 		stunalloc(fullname);
310aa9caaf6SPeter Wemm 	}
311aa9caaf6SPeter Wemm 	return basename;
312aa9caaf6SPeter Wemm }
313aa9caaf6SPeter Wemm 
314aa9caaf6SPeter Wemm int
dotcmd(int argc,char ** argv)3155134c3f7SWarner Losh dotcmd(int argc, char **argv)
316aa9caaf6SPeter Wemm {
317c1564db0SJilles Tjoelker 	char *filename, *fullname;
318e5f1cf08SStefan Farfeleder 
319e5f1cf08SStefan Farfeleder 	if (argc < 2)
320e5f1cf08SStefan Farfeleder 		error("missing filename");
321e5f1cf08SStefan Farfeleder 
3224b88c807SRodney W. Grimes 	exitstatus = 0;
323aa9caaf6SPeter Wemm 
324c1564db0SJilles Tjoelker 	/*
325c1564db0SJilles Tjoelker 	 * Because we have historically not supported any options,
326c1564db0SJilles Tjoelker 	 * only treat "--" specially.
327c1564db0SJilles Tjoelker 	 */
328c1564db0SJilles Tjoelker 	filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1];
329c1564db0SJilles Tjoelker 
330c1564db0SJilles Tjoelker 	fullname = find_dot_file(filename);
331d2c23317SStephane Rochoy 	setinputfile(fullname, 1, -1 /* verify */);
332aa9caaf6SPeter Wemm 	commandname = fullname;
3334b88c807SRodney W. Grimes 	cmdloop(0);
3344b88c807SRodney W. Grimes 	popfile();
3354b88c807SRodney W. Grimes 	return exitstatus;
3364b88c807SRodney W. Grimes }
3374b88c807SRodney W. Grimes 
3384b88c807SRodney W. Grimes 
339aa9caaf6SPeter Wemm int
exitcmd(int argc,char ** argv)3405134c3f7SWarner Losh exitcmd(int argc, char **argv)
341aa9caaf6SPeter Wemm {
3424b88c807SRodney W. Grimes 	if (stoppedjobs())
343aa9caaf6SPeter Wemm 		return 0;
344ab0a2172SSteve Price 	if (argc > 1)
34570df11eaSJilles Tjoelker 		exitshell(number(argv[1]));
346ab0a2172SSteve Price 	else
34770df11eaSJilles Tjoelker 		exitshell_savedstatus();
3484b88c807SRodney W. Grimes }
349