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