main.c revision 104261
10SN/A/*- 215774Sclanger * Copyright (c) 1991, 1993 30SN/A * The Regents of the University of California. All rights reserved. 40SN/A * 50SN/A * This code is derived from software contributed to Berkeley by 60SN/A * Kenneth Almquist. 72362SN/A * 80SN/A * Redistribution and use in source and binary forms, with or without 92362SN/A * modification, are permitted provided that the following conditions 100SN/A * are met: 110SN/A * 1. Redistributions of source code must retain the above copyright 120SN/A * notice, this list of conditions and the following disclaimer. 130SN/A * 2. Redistributions in binary form must reproduce the above copyright 140SN/A * notice, this list of conditions and the following disclaimer in the 150SN/A * documentation and/or other materials provided with the distribution. 160SN/A * 3. All advertising materials mentioning features or use of this software 170SN/A * must display the following acknowledgement: 180SN/A * This product includes software developed by the University of 190SN/A * California, Berkeley and its contributors. 200SN/A * 4. Neither the name of the University nor the names of its contributors 212362SN/A * may be used to endorse or promote products derived from this software 222362SN/A * without specific prior written permission. 232362SN/A * 240SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2515918Sclanger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2615918Sclanger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 270SN/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 280SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 290SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 300SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 310SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 320SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 330SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 340SN/A * SUCH DAMAGE. 350SN/A */ 360SN/A 370SN/A#ifndef lint 380SN/Astatic char const copyright[] = 390SN/A"@(#) Copyright (c) 1991, 1993\n\ 400SN/A The Regents of the University of California. All rights reserved.\n"; 410SN/A#endif /* not lint */ 420SN/A 430SN/A#ifndef lint 440SN/A#if 0 450SN/Astatic char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95"; 460SN/A#endif 470SN/A#endif /* not lint */ 480SN/A#include <sys/cdefs.h> 490SN/A__FBSDID("$FreeBSD: head/bin/sh/main.c 104261 2002-10-01 01:30:33Z tjr $"); 500SN/A 510SN/A#include <stdio.h> 520SN/A#include <signal.h> 530SN/A#include <sys/stat.h> 540SN/A#include <unistd.h> 550SN/A#include <fcntl.h> 560SN/A#include <locale.h> 570SN/A#include <errno.h> 580SN/A 590SN/A#include "shell.h" 600SN/A#include "main.h" 610SN/A#include "mail.h" 620SN/A#include "options.h" 630SN/A#include "output.h" 640SN/A#include "parser.h" 650SN/A#include "nodes.h" 660SN/A#include "expand.h" 670SN/A#include "eval.h" 680SN/A#include "jobs.h" 690SN/A#include "input.h" 700SN/A#include "trap.h" 710SN/A#include "var.h" 720SN/A#include "show.h" 730SN/A#include "memalloc.h" 740SN/A#include "error.h" 750SN/A#include "init.h" 760SN/A#include "mystring.h" 770SN/A#include "exec.h" 780SN/A#include "cd.h" 790SN/A 800SN/Aint rootpid; 810SN/Aint rootshell; 820SN/A 830SN/ASTATIC void read_profile(char *); 840SN/ASTATIC char *find_dot_file(char *); 850SN/A 860SN/A/* 870SN/A * Main routine. We initialize things, parse the arguments, execute 880SN/A * profiles if we're a login shell, and then call cmdloop to execute 890SN/A * commands. The setjmp call sets up the location to jump to when an 900SN/A * exception occurs. When an exception occurs the variable "state" 910SN/A * is used to figure out how far we had gotten. 920SN/A */ 930SN/A 940SN/Aint 950SN/Amain(int argc, char *argv[]) 960SN/A{ 970SN/A struct jmploc jmploc; 980SN/A struct stackmark smark; 990SN/A volatile int state; 1000SN/A char *shinit; 1010SN/A 1020SN/A (void) setlocale(LC_ALL, ""); 1030SN/A state = 0; 1040SN/A if (setjmp(jmploc.loc)) { 1050SN/A /* 1060SN/A * When a shell procedure is executed, we raise the 1070SN/A * exception EXSHELLPROC to clean up before executing 1080SN/A * the shell procedure. 1090SN/A */ 1107041SN/A switch (exception) { 1117041SN/A case EXSHELLPROC: 1127041SN/A rootpid = getpid(); 1133877SN/A rootshell = 1; 1140SN/A minusc = NULL; 1157041SN/A state = 3; 1167041SN/A break; 1177041SN/A 1187041SN/A case EXEXEC: 1197041SN/A exitstatus = exerrno; 1207041SN/A break; 1217041SN/A 1223877SN/A case EXERROR: 1230SN/A exitstatus = 2; 1240SN/A break; 1250SN/A 1267041SN/A default: 1270SN/A break; 1280SN/A } 1290SN/A 1307041SN/A if (exception != EXSHELLPROC) { 1317041SN/A if (state == 0 || iflag == 0 || ! rootshell) 1327041SN/A exitshell(exitstatus); 1337041SN/A } 1347041SN/A reset(); 1357041SN/A if (exception == EXINT) { 1363877SN/A out2c('\n'); 1370SN/A flushout(&errout); 1380SN/A } 1390SN/A popstackmark(&smark); 1400SN/A FORCEINTON; /* enable interrupts */ 1417041SN/A if (state == 1) 1427041SN/A goto state1; 1437041SN/A else if (state == 2) 1447041SN/A goto state2; 1457041SN/A else if (state == 3) 1467041SN/A goto state3; 1477041SN/A else 1480SN/A goto state4; 1490SN/A } 1500SN/A handler = &jmploc; 1510SN/A#ifdef DEBUG 1520SN/A opentrace(); 1530SN/A trputs("Shell args: "); trargs(argv); 1540SN/A#endif 1550SN/A rootpid = getpid(); 1560SN/A rootshell = 1; 1570SN/A init(); 1580SN/A setstackmark(&smark); 1590SN/A procargs(argc, argv); 1600SN/A if (getpwd() == NULL && iflag) 1610SN/A out2str("sh: cannot determine working directory\n"); 1620SN/A if (argv[0] && argv[0][0] == '-') { 1630SN/A state = 1; 1640SN/A read_profile("/etc/profile"); 1650SN/Astate1: 1660SN/A state = 2; 1673877SN/A if (privileged == 0) 1680SN/A read_profile(".profile"); 1690SN/A else 1700SN/A read_profile("/etc/suid_profile"); 1710SN/A } 1720SN/Astate2: 1730SN/A state = 3; 1740SN/A if (!privileged && iflag) { 1750SN/A if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 1766234SN/A state = 3; 1770SN/A read_profile(shinit); 1780SN/A } 1790SN/A } 1800SN/Astate3: 1810SN/A state = 4; 1820SN/A if (minusc) { 1837041SN/A evalstring(minusc); 1847041SN/A } 1857041SN/A if (sflag || minusc == NULL) { 1867041SN/Astate4: /* XXX ??? - why isn't this before the "if" statement */ 1877041SN/A cmdloop(1); 1883877SN/A } 1890SN/A exitshell(exitstatus); 1907041SN/A /*NOTREACHED*/ 1917041SN/A return 0; 1927041SN/A} 1937041SN/A 1947041SN/A 1957041SN/A/* 1967041SN/A * Read and execute commands. "Top" is nonzero for the top level command 1973877SN/A * loop; it turns on prompting if the shell is interactive. 1980SN/A */ 1990SN/A 2000SN/Avoid 2017041SN/Acmdloop(int top) 2020SN/A{ 2030SN/A union node *n; 2040SN/A struct stackmark smark; 2050SN/A int inter; 2060SN/A int numeof = 0; 2070SN/A 2080SN/A TRACE(("cmdloop(%d) called\n", top)); 2090SN/A setstackmark(&smark); 2100SN/A for (;;) { 2110SN/A if (pendingsigs) 2120SN/A dotrap(); 2130SN/A inter = 0; 2140SN/A if (iflag && top) { 2150SN/A inter++; 2160SN/A showjobs(1, 0, 0); 2170SN/A chkmail(0); 2180SN/A flushout(&output); 2190SN/A } 2200SN/A n = parsecmd(inter); 2210SN/A /* showtree(n); DEBUG */ 2220SN/A if (n == NEOF) { 2230SN/A if (!top || numeof >= 50) 2240SN/A break; 2250SN/A if (!stoppedjobs()) { 2265319SN/A if (!Iflag) 2270SN/A break; 2280SN/A out2str("\nUse \"exit\" to leave shell.\n"); 2290SN/A } 2305319SN/A numeof++; 2310SN/A } else if (n != NULL && nflag == 0) { 2320SN/A job_warning = (job_warning == 2) ? 1 : 0; 2330SN/A numeof = 0; 2345319SN/A evaltree(n, 0); 2350SN/A } 2360SN/A popstackmark(&smark); 2370SN/A setstackmark(&smark); 2380SN/A if (evalskip == SKIPFILE) { 2390SN/A evalskip = 0; 2400SN/A break; 2410SN/A } 2425319SN/A } 2430SN/A popstackmark(&smark); 2440SN/A} 2450SN/A 2460SN/A 2475319SN/A 2480SN/A/* 2490SN/A * Read /etc/profile or .profile. Return on error. 2500SN/A */ 2515319SN/A 2525319SN/ASTATIC void 2535319SN/Aread_profile(char *name) 2545319SN/A{ 2555319SN/A int fd; 2560SN/A 2570SN/A INTOFF; 2580SN/A if ((fd = open(name, O_RDONLY)) >= 0) 2595319SN/A setinputfd(fd, 1); 2600SN/A INTON; 2610SN/A if (fd < 0) 2620SN/A return; 2630SN/A cmdloop(0); 2640SN/A popfile(); 2650SN/A} 2660SN/A 2670SN/A 2686234SN/A 2696234SN/A/* 2706234SN/A * Read a file containing shell functions. 2716234SN/A */ 2726234SN/A 2736234SN/Avoid 2746234SN/Areadcmdfile(char *name) 2756234SN/A{ 2766234SN/A int fd; 2776234SN/A 2780SN/A INTOFF; 2790SN/A if ((fd = open(name, O_RDONLY)) >= 0) 2800SN/A setinputfd(fd, 1); 2810SN/A else 2820SN/A error("Can't open %s: %s", name, strerror(errno)); 2830SN/A INTON; 2840SN/A cmdloop(0); 2850SN/A popfile(); 2860SN/A} 2875618SN/A 2880SN/A 2890SN/A 2900SN/A/* 2910SN/A * Take commands from a file. To be compatible we should do a path 2920SN/A * search for the file, which is necessary to find sub-commands. 2930SN/A */ 2940SN/A 2950SN/A 2960SN/ASTATIC char * 2970SN/Afind_dot_file(char *basename) 2980SN/A{ 2996234SN/A static char localname[FILENAME_MAX+1]; 3006234SN/A char *fullname; 3016234SN/A char *path = pathval(); 3026234SN/A struct stat statb; 3036234SN/A 3046234SN/A /* don't try this for absolute or relative paths */ 3056234SN/A if( strchr(basename, '/')) 3066234SN/A return basename; 3076234SN/A 3086234SN/A while ((fullname = padvance(&path, basename)) != NULL) { 3096234SN/A strcpy(localname, fullname); 3106234SN/A stunalloc(fullname); 3116234SN/A if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) 3126234SN/A return localname; 3136234SN/A } 3146234SN/A return basename; 3156234SN/A} 3166234SN/A 3176234SN/Aint 3186234SN/Adotcmd(int argc, char **argv) 3196234SN/A{ 3206234SN/A struct strlist *sp; 3216234SN/A exitstatus = 0; 3220SN/A 3230SN/A for (sp = cmdenviron; sp ; sp = sp->next) 3243877SN/A setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); 3250SN/A 3260SN/A if (argc >= 2) { /* That's what SVR2 does */ 3270SN/A char *fullname = find_dot_file(argv[1]); 3280SN/A 3290SN/A setinputfile(fullname, 1); 3300SN/A commandname = fullname; 3310SN/A cmdloop(0); 3320SN/A popfile(); 3330SN/A } 3340SN/A return exitstatus; 3350SN/A} 3360SN/A 3370SN/A 3380SN/Aint 3390SN/Aexitcmd(int argc, char **argv) 3400SN/A{ 3410SN/A extern int oexitstatus; 3420SN/A 3430SN/A if (stoppedjobs()) 3440SN/A return 0; 3450SN/A if (argc > 1) 3460SN/A exitstatus = number(argv[1]); 3470SN/A else 3480SN/A exitstatus = oexitstatus; 3490SN/A exitshell(exitstatus); 3500SN/A /*NOTREACHED*/ 3510SN/A return 0; 3520SN/A} 3530SN/A