1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#) Copyright (c) 1991, 1993 The Regents of the University of California. All rights reserved. 33 * @(#)main.c 8.6 (Berkeley) 5/28/95 34 * $FreeBSD: head/bin/sh/main.c 247206 2013-02-23 22:50:57Z jilles $ 35 */ 36 37 #include <stdio.h> 38 #include <signal.h> 39 #include <sys/stat.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 #include <locale.h> 43 #include <errno.h> 44 45 #include "shell.h" 46 #include "main.h" 47 #include "mail.h" 48 #include "options.h" 49 #include "output.h" 50 #include "parser.h" 51 #include "nodes.h" 52 #include "expand.h" 53 #include "eval.h" 54 #include "jobs.h" 55 #include "input.h" 56 #include "trap.h" 57 #include "var.h" 58 #include "show.h" 59 #include "memalloc.h" 60 #include "error.h" 61 #include "init.h" 62 #include "mystring.h" 63 #include "exec.h" 64 #include "cd.h" 65 #include "builtins.h" 66 67 int rootpid; 68 int rootshell; 69 struct jmploc main_handler; 70 int localeisutf8, initial_localeisutf8; 71 72 static void cmdloop(int); 73 static void read_profile(const char *); 74 static const char *find_dot_file(const char *); 75 76 /* 77 * Main routine. We initialize things, parse the arguments, execute 78 * profiles if we're a login shell, and then call cmdloop to execute 79 * commands. The setjmp call sets up the location to jump to when an 80 * exception occurs. When an exception occurs the variable "state" 81 * is used to figure out how far we had gotten. 82 */ 83 84 int 85 main(int argc, char *argv[]) 86 { 87 struct stackmark smark, smark2; 88 volatile int state; 89 char *shinit; 90 91 setlocale(LC_ALL, ""); 92 initcharset(); 93 state = 0; 94 if (setjmp(main_handler.loc)) { 95 switch (exception) { 96 case EXEXEC: 97 exitstatus = exerrno; 98 break; 99 100 case EXERROR: 101 exitstatus = 2; 102 break; 103 104 default: 105 break; 106 } 107 108 if (state == 0 || iflag == 0 || ! rootshell || 109 exception == EXEXIT) 110 exitshell(exitstatus); 111 reset(); 112 if (exception == EXINT) 113 out2fmt_flush("\n"); 114 popstackmark(&smark); 115 FORCEINTON; /* enable interrupts */ 116 if (state == 1) 117 goto state1; 118 else if (state == 2) 119 goto state2; 120 else if (state == 3) 121 goto state3; 122 else 123 goto state4; 124 } 125 handler = &main_handler; 126 #ifdef DEBUG 127 opentrace(); 128 trputs("Shell args: "); trargs(argv); 129 #endif 130 rootpid = getpid(); 131 rootshell = 1; 132 initvar(); 133 setstackmark(&smark); 134 setstackmark(&smark2); 135 procargs(argc, argv); 136 pwd_init(iflag); 137 if (iflag) 138 chkmail(1); 139 if (argv[0] && argv[0][0] == '-') { 140 state = 1; 141 read_profile("/etc/profile"); 142 state1: 143 state = 2; 144 if (privileged == 0) 145 read_profile("${HOME-}/.profile"); 146 else 147 read_profile("/etc/suid_profile"); 148 } 149 state2: 150 state = 3; 151 if (!privileged && iflag) { 152 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 153 state = 3; 154 read_profile(shinit); 155 } 156 } 157 state3: 158 state = 4; 159 popstackmark(&smark2); 160 if (minusc) { 161 evalstring(minusc, sflag ? 0 : EV_EXIT); 162 } 163 if (sflag || minusc == NULL) { 164 state4: /* XXX ??? - why isn't this before the "if" statement */ 165 cmdloop(1); 166 } 167 exitshell(exitstatus); 168 /*NOTREACHED*/ 169 return 0; 170 } 171 172 173 /* 174 * Read and execute commands. "Top" is nonzero for the top level command 175 * loop; it turns on prompting if the shell is interactive. 176 */ 177 178 static void 179 cmdloop(int top) 180 { 181 union node *n; 182 struct stackmark smark; 183 int inter; 184 int numeof = 0; 185 186 TRACE(("cmdloop(%d) called\n", top)); 187 setstackmark(&smark); 188 for (;;) { 189 if (pendingsig) 190 dotrap(); 191 inter = 0; 192 if (iflag && top) { 193 inter++; 194 showjobs(1, SHOWJOBS_DEFAULT); 195 chkmail(0); 196 flushout(&output); 197 } 198 n = parsecmd(inter); 199 /* showtree(n); DEBUG */ 200 if (n == NEOF) { 201 if (!top || numeof >= 50) 202 break; 203 if (!stoppedjobs()) { 204 if (!Iflag) 205 break; 206 out2fmt_flush("\nUse \"exit\" to leave shell.\n"); 207 } 208 numeof++; 209 } else if (n != NULL && nflag == 0) { 210 job_warning = (job_warning == 2) ? 1 : 0; 211 numeof = 0; 212 evaltree(n, 0); 213 } 214 popstackmark(&smark); 215 setstackmark(&smark); 216 if (evalskip != 0) { 217 if (evalskip == SKIPFILE) 218 evalskip = 0; 219 break; 220 } 221 } 222 popstackmark(&smark); 223 } 224 225 226 227 /* 228 * Read /etc/profile or .profile. Return on error. 229 */ 230 231 static void 232 read_profile(const char *name) 233 { 234 int fd; 235 const char *expandedname; 236 237 expandedname = expandstr(__DECONST(char *, name)); 238 if (expandedname == NULL) 239 return; 240 INTOFF; 241 if ((fd = open(expandedname, O_RDONLY)) >= 0) 242 setinputfd(fd, 1); 243 INTON; 244 if (fd < 0) 245 return; 246 cmdloop(0); 247 popfile(); 248 } 249 250 251 252 /* 253 * Read a file containing shell functions. 254 */ 255 256 void 257 readcmdfile(const char *name) 258 { 259 setinputfile(name, 1); 260 cmdloop(0); 261 popfile(); 262 } 263 264 265 266 /* 267 * Take commands from a file. To be compatible we should do a path 268 * search for the file, which is necessary to find sub-commands. 269 */ 270 271 272 static const char * 273 find_dot_file(const char *basename) 274 { 275 char *fullname; 276 const char *path = pathval(); 277 struct stat statb; 278 279 /* don't try this for absolute or relative paths */ 280 if( strchr(basename, '/')) 281 return basename; 282 283 while ((fullname = padvance(&path, basename)) != NULL) { 284 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { 285 /* 286 * Don't bother freeing here, since it will 287 * be freed by the caller. 288 */ 289 return fullname; 290 } 291 stunalloc(fullname); 292 } 293 return basename; 294 } 295 296 int 297 dotcmd(int argc, char **argv) 298 { 299 const char *fullname; 300 char *filename; 301 302 if (argc < 2) 303 error("missing filename"); 304 305 exitstatus = 0; 306 307 /* 308 * Because we have historically not supported any options, 309 * only treat "--" specially. 310 */ 311 filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1]; 312 313 fullname = find_dot_file(filename); 314 setinputfile(fullname, 1); 315 commandname = fullname; 316 cmdloop(0); 317 popfile(); 318 return exitstatus; 319 } 320 321 322 int 323 exitcmd(int argc, char **argv) 324 { 325 if (stoppedjobs()) 326 return 0; 327 if (argc > 1) 328 exitshell(number(argv[1])); 329 else 330 exitshell_savedstatus(); 331 } 332