main.c revision 99110
1189251Ssam/*- 2189251Ssam * Copyright (c) 1991, 1993 3189251Ssam * The Regents of the University of California. All rights reserved. 4189251Ssam * 5189251Ssam * This code is derived from software contributed to Berkeley by 6189251Ssam * Kenneth Almquist. 7189251Ssam * 8189251Ssam * Redistribution and use in source and binary forms, with or without 9189251Ssam * modification, are permitted provided that the following conditions 10189251Ssam * are met: 11189251Ssam * 1. Redistributions of source code must retain the above copyright 12189251Ssam * notice, this list of conditions and the following disclaimer. 13189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 14189251Ssam * notice, this list of conditions and the following disclaimer in the 15189251Ssam * documentation and/or other materials provided with the distribution. 16189251Ssam * 3. All advertising materials mentioning features or use of this software 17189251Ssam * must display the following acknowledgement: 18189251Ssam * This product includes software developed by the University of 19189251Ssam * California, Berkeley and its contributors. 20189251Ssam * 4. Neither the name of the University nor the names of its contributors 21189251Ssam * may be used to endorse or promote products derived from this software 22189251Ssam * without specific prior written permission. 23189251Ssam * 24189251Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27189251Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34189251Ssam * SUCH DAMAGE. 35189251Ssam */ 36189251Ssam 37189251Ssam#ifndef lint 38189251Ssamstatic char const copyright[] = 39189251Ssam"@(#) Copyright (c) 1991, 1993\n\ 40189251Ssam The Regents of the University of California. All rights reserved.\n"; 41189251Ssam#endif /* not lint */ 42189251Ssam 43189251Ssam#ifndef lint 44189251Ssam#if 0 45189251Ssamstatic char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95"; 46189251Ssam#endif 47189251Ssam#endif /* not lint */ 48189251Ssam#include <sys/cdefs.h> 49189251Ssam__FBSDID("$FreeBSD: head/bin/sh/main.c 99110 2002-06-30 05:15:05Z obrien $"); 50189251Ssam 51189251Ssam#include <stdio.h> 52189251Ssam#include <signal.h> 53189251Ssam#include <sys/stat.h> 54189251Ssam#include <unistd.h> 55189251Ssam#include <fcntl.h> 56189251Ssam#include <locale.h> 57189251Ssam#include <errno.h> 58189251Ssam 59189251Ssam#include "shell.h" 60189251Ssam#include "main.h" 61189251Ssam#include "mail.h" 62189251Ssam#include "options.h" 63189251Ssam#include "output.h" 64189251Ssam#include "parser.h" 65189251Ssam#include "nodes.h" 66189251Ssam#include "expand.h" 67189251Ssam#include "eval.h" 68189251Ssam#include "jobs.h" 69189251Ssam#include "input.h" 70189251Ssam#include "trap.h" 71189251Ssam#include "var.h" 72189251Ssam#include "show.h" 73189251Ssam#include "memalloc.h" 74189251Ssam#include "error.h" 75189251Ssam#include "init.h" 76189251Ssam#include "mystring.h" 77189251Ssam#include "exec.h" 78189251Ssam#include "cd.h" 79189251Ssam 80189251Ssam#define PROFILE 0 81189251Ssam 82189251Ssamint rootpid; 83189251Ssamint rootshell; 84189251Ssam#if PROFILE 85189251Ssamshort profile_buf[16384]; 86189251Ssamextern int etext(); 87189251Ssam#endif 88189251Ssam 89189251SsamSTATIC void read_profile(char *); 90189251SsamSTATIC char *find_dot_file(char *); 91189251Ssam 92189251Ssam/* 93189251Ssam * Main routine. We initialize things, parse the arguments, execute 94189251Ssam * profiles if we're a login shell, and then call cmdloop to execute 95189251Ssam * commands. The setjmp call sets up the location to jump to when an 96189251Ssam * exception occurs. When an exception occurs the variable "state" 97189251Ssam * is used to figure out how far we had gotten. 98189251Ssam */ 99189251Ssam 100189251Ssamint 101189251Ssammain(int argc, char *argv[]) 102189251Ssam{ 103189251Ssam struct jmploc jmploc; 104189251Ssam struct stackmark smark; 105189251Ssam volatile int state; 106189251Ssam char *shinit; 107189251Ssam 108189251Ssam#if PROFILE 109189251Ssam monitor(4, etext, profile_buf, sizeof profile_buf, 50); 110189251Ssam#endif 111189251Ssam (void) setlocale(LC_ALL, ""); 112189251Ssam state = 0; 113189251Ssam if (setjmp(jmploc.loc)) { 114189251Ssam /* 115189251Ssam * When a shell procedure is executed, we raise the 116189251Ssam * exception EXSHELLPROC to clean up before executing 117189251Ssam * the shell procedure. 118189251Ssam */ 119189251Ssam switch (exception) { 120189251Ssam case EXSHELLPROC: 121189251Ssam rootpid = getpid(); 122189251Ssam rootshell = 1; 123189251Ssam minusc = NULL; 124189251Ssam state = 3; 125189251Ssam break; 126189251Ssam 127189251Ssam case EXEXEC: 128189251Ssam exitstatus = exerrno; 129189251Ssam break; 130189251Ssam 131189251Ssam case EXERROR: 132189251Ssam exitstatus = 2; 133189251Ssam break; 134189251Ssam 135189251Ssam default: 136189251Ssam break; 137189251Ssam } 138189251Ssam 139189251Ssam if (exception != EXSHELLPROC) { 140189251Ssam if (state == 0 || iflag == 0 || ! rootshell) 141189251Ssam exitshell(exitstatus); 142189251Ssam } 143189251Ssam reset(); 144189251Ssam if (exception == EXINT 145189251Ssam#if ATTY 146189251Ssam && (! attyset() || equal(termval(), "emacs")) 147189251Ssam#endif 148189251Ssam ) { 149189251Ssam out2c('\n'); 150189251Ssam flushout(&errout); 151189251Ssam } 152189251Ssam popstackmark(&smark); 153189251Ssam FORCEINTON; /* enable interrupts */ 154189251Ssam if (state == 1) 155189251Ssam goto state1; 156189251Ssam else if (state == 2) 157189251Ssam goto state2; 158189251Ssam else if (state == 3) 159189251Ssam goto state3; 160189251Ssam else 161189251Ssam goto state4; 162189251Ssam } 163189251Ssam handler = &jmploc; 164189251Ssam#ifdef DEBUG 165189251Ssam opentrace(); 166189251Ssam trputs("Shell args: "); trargs(argv); 167189251Ssam#endif 168189251Ssam rootpid = getpid(); 169189251Ssam rootshell = 1; 170189251Ssam init(); 171189251Ssam setstackmark(&smark); 172189251Ssam procargs(argc, argv); 173189251Ssam if (getpwd() == NULL && iflag) 174189251Ssam out2str("sh: cannot determine working directory\n"); 175189251Ssam if (argv[0] && argv[0][0] == '-') { 176189251Ssam state = 1; 177189251Ssam read_profile("/etc/profile"); 178189251Ssamstate1: 179189251Ssam state = 2; 180189251Ssam if (privileged == 0) 181189251Ssam read_profile(".profile"); 182189251Ssam else 183189251Ssam read_profile("/etc/suid_profile"); 184189251Ssam } 185189251Ssamstate2: 186189251Ssam state = 3; 187189251Ssam if (!privileged && iflag) { 188189251Ssam if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 189189251Ssam state = 3; 190189251Ssam read_profile(shinit); 191189251Ssam } 192189251Ssam } 193189251Ssamstate3: 194189251Ssam state = 4; 195189251Ssam if (minusc) { 196189251Ssam evalstring(minusc); 197189251Ssam } 198189251Ssam if (sflag || minusc == NULL) { 199189251Ssamstate4: /* XXX ??? - why isn't this before the "if" statement */ 200189251Ssam cmdloop(1); 201189251Ssam } 202189251Ssam#if PROFILE 203189251Ssam monitor(0); 204189251Ssam#endif 205189251Ssam exitshell(exitstatus); 206189251Ssam /*NOTREACHED*/ 207189251Ssam return 0; 208189251Ssam} 209189251Ssam 210189251Ssam 211189251Ssam/* 212189251Ssam * Read and execute commands. "Top" is nonzero for the top level command 213189251Ssam * loop; it turns on prompting if the shell is interactive. 214189251Ssam */ 215189251Ssam 216189251Ssamvoid 217189251Ssamcmdloop(int top) 218189251Ssam{ 219189251Ssam union node *n; 220189251Ssam struct stackmark smark; 221189251Ssam int inter; 222189251Ssam int numeof = 0; 223189251Ssam 224189251Ssam TRACE(("cmdloop(%d) called\n", top)); 225189251Ssam setstackmark(&smark); 226189251Ssam for (;;) { 227189251Ssam if (pendingsigs) 228189251Ssam dotrap(); 229189251Ssam inter = 0; 230189251Ssam if (iflag && top) { 231189251Ssam inter++; 232189251Ssam showjobs(1, 0, 0); 233189251Ssam chkmail(0); 234189251Ssam flushout(&output); 235189251Ssam } 236189251Ssam n = parsecmd(inter); 237189251Ssam /* showtree(n); DEBUG */ 238189251Ssam if (n == NEOF) { 239189251Ssam if (!top || numeof >= 50) 240189251Ssam break; 241189251Ssam if (!stoppedjobs()) { 242189251Ssam if (!Iflag) 243189251Ssam break; 244189251Ssam out2str("\nUse \"exit\" to leave shell.\n"); 245189251Ssam } 246189251Ssam numeof++; 247189251Ssam } else if (n != NULL && nflag == 0) { 248189251Ssam job_warning = (job_warning == 2) ? 1 : 0; 249189251Ssam numeof = 0; 250189251Ssam evaltree(n, 0); 251189251Ssam } 252189251Ssam popstackmark(&smark); 253189251Ssam setstackmark(&smark); 254189251Ssam if (evalskip == SKIPFILE) { 255189251Ssam evalskip = 0; 256189251Ssam break; 257189251Ssam } 258189251Ssam } 259189251Ssam popstackmark(&smark); 260189251Ssam} 261189251Ssam 262189251Ssam 263189251Ssam 264189251Ssam/* 265189251Ssam * Read /etc/profile or .profile. Return on error. 266189251Ssam */ 267189251Ssam 268189251SsamSTATIC void 269189251Ssamread_profile(char *name) 270189251Ssam{ 271189251Ssam int fd; 272189251Ssam 273189251Ssam INTOFF; 274189251Ssam if ((fd = open(name, O_RDONLY)) >= 0) 275189251Ssam setinputfd(fd, 1); 276189251Ssam INTON; 277189251Ssam if (fd < 0) 278189251Ssam return; 279189251Ssam cmdloop(0); 280189251Ssam popfile(); 281189251Ssam} 282189251Ssam 283189251Ssam 284189251Ssam 285189251Ssam/* 286189251Ssam * Read a file containing shell functions. 287189251Ssam */ 288189251Ssam 289189251Ssamvoid 290189251Ssamreadcmdfile(char *name) 291189251Ssam{ 292189251Ssam int fd; 293189251Ssam 294189251Ssam INTOFF; 295189251Ssam if ((fd = open(name, O_RDONLY)) >= 0) 296189251Ssam setinputfd(fd, 1); 297189251Ssam else 298189251Ssam error("Can't open %s: %s", name, strerror(errno)); 299189251Ssam INTON; 300189251Ssam cmdloop(0); 301189251Ssam popfile(); 302189251Ssam} 303189251Ssam 304189251Ssam 305189251Ssam 306189251Ssam/* 307189251Ssam * Take commands from a file. To be compatible we should do a path 308189251Ssam * search for the file, which is necessary to find sub-commands. 309189251Ssam */ 310189251Ssam 311189251Ssam 312189251SsamSTATIC char * 313189251Ssamfind_dot_file(char *basename) 314189251Ssam{ 315189251Ssam static char localname[FILENAME_MAX+1]; 316189251Ssam char *fullname; 317189251Ssam char *path = pathval(); 318189251Ssam struct stat statb; 319189251Ssam 320189251Ssam /* don't try this for absolute or relative paths */ 321189251Ssam if( strchr(basename, '/')) 322189251Ssam return basename; 323189251Ssam 324189251Ssam while ((fullname = padvance(&path, basename)) != NULL) { 325189251Ssam strcpy(localname, fullname); 326189251Ssam stunalloc(fullname); 327189251Ssam if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) 328189251Ssam return localname; 329189251Ssam } 330189251Ssam return basename; 331189251Ssam} 332189251Ssam 333189251Ssamint 334189251Ssamdotcmd(int argc, char **argv) 335189251Ssam{ 336189251Ssam struct strlist *sp; 337189251Ssam exitstatus = 0; 338189251Ssam 339189251Ssam for (sp = cmdenviron; sp ; sp = sp->next) 340189251Ssam setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); 341189251Ssam 342189251Ssam if (argc >= 2) { /* That's what SVR2 does */ 343189251Ssam char *fullname = find_dot_file(argv[1]); 344189251Ssam 345189251Ssam setinputfile(fullname, 1); 346189251Ssam commandname = fullname; 347189251Ssam cmdloop(0); 348189251Ssam popfile(); 349189251Ssam } 350189251Ssam return exitstatus; 351189251Ssam} 352189251Ssam 353189251Ssam 354189251Ssamint 355189251Ssamexitcmd(int argc, char **argv) 356189251Ssam{ 357189251Ssam extern int oexitstatus; 358189251Ssam 359189251Ssam if (stoppedjobs()) 360189251Ssam return 0; 361189251Ssam if (argc > 1) 362189251Ssam exitstatus = number(argv[1]); 363189251Ssam else 364189251Ssam exitstatus = oexitstatus; 365189251Ssam exitshell(exitstatus); 366189251Ssam /*NOTREACHED*/ 367189251Ssam return 0; 368189251Ssam} 369189251Ssam