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