1/*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1997-2005 5 * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <zircon/process.h> 36#include <zircon/processargs.h> 37#include <zircon/syscalls.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <signal.h> 41#include <sys/stat.h> 42#include <unistd.h> 43#include <fcntl.h> 44 45#include "shell.h" 46#include "main.h" 47#include "options.h" 48#include "output.h" 49#include "parser.h" 50#include "nodes.h" 51#include "expand.h" 52#include "eval.h" 53#include "jobs.h" 54#include "input.h" 55#include "trap.h" 56#include "var.h" 57#include "show.h" 58#include "memalloc.h" 59#include "error.h" 60#include "init.h" 61#include "mystring.h" 62#include "exec.h" 63#include "cd.h" 64 65#define PROFILE 0 66 67int rootpid; 68int shlvl; 69#ifdef __GLIBC__ 70int *dash_errno; 71#endif 72#if PROFILE 73short profile_buf[16384]; 74extern int etext(); 75#endif 76 77STATIC void read_profile(const char *); 78STATIC char *find_dot_file(char *); 79STATIC void evalifsubshell(zx_handle_t); 80static int cmdloop(int); 81int main(int, char **); 82 83/* 84 * Main routine. We initialize things, parse the arguments, execute 85 * profiles if we're a login shell, and then call cmdloop to execute 86 * commands. The setjmp call sets up the location to jump to when an 87 * exception occurs. When an exception occurs the variable "state" 88 * is used to figure out how far we had gotten. 89 */ 90 91int 92main(int argc, char **argv) 93{ 94 char *shinit; 95 volatile int state; 96 struct jmploc jmploc; 97 struct stackmark smark; 98 int login; 99 100#ifdef __GLIBC__ 101 dash_errno = __errno_location(); 102#endif 103 104#if PROFILE 105 monitor(4, etext, profile_buf, sizeof profile_buf, 50); 106#endif 107 state = 0; 108 if (unlikely(setjmp(jmploc.loc))) { 109 int e; 110 int s; 111 112 reset(); 113 114 e = exception; 115 116 s = state; 117 if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) 118 exitshell(); 119 120 if (e == EXINT 121#if ATTY 122 && (! attyset() || equal(termval(), "emacs")) 123#endif 124 ) { 125 out2c('\n'); 126#ifdef FLUSHERR 127 flushout(out2); 128#endif 129 } 130 popstackmark(&smark); 131 FORCEINTON; /* enable interrupts */ 132 if (s == 1) 133 goto state1; 134 else if (s == 2) 135 goto state2; 136 else if (s == 3) 137 goto state3; 138 else 139 goto state4; 140 } 141 handler = &jmploc; 142#ifdef DEBUG 143 opentrace(); 144 trputs("Shell args: "); trargs(argv); 145#endif 146 rootpid = getpid(); 147 init(); 148 setstackmark(&smark); 149 150 zx_handle_t ast_vmo = zx_take_startup_handle(PA_HND(PA_USER0, 0)); 151 152 login = procargs(argc, argv, ast_vmo != ZX_HANDLE_INVALID); 153 154 // Fuchsia: recognize if we have been invoked for the purpose of evaluating 155 // an expression (i.e., node) and exiting immediately. 156 evalifsubshell(ast_vmo); 157 158 if (login) { 159 state = 1; 160 read_profile("/etc/profile"); 161state1: 162 state = 2; 163 read_profile("$HOME/.profile"); 164 } 165 settitle("sh"); 166state2: 167 state = 3; 168 if ( 169#ifndef linux 170 getuid() == geteuid() && getgid() == getegid() && 171#endif 172 iflag 173 ) { 174 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 175 read_profile(shinit); 176 } 177 } 178 popstackmark(&smark); 179state3: 180 state = 4; 181 if (minusc) 182 evalstring(minusc, sflag ? 0 : EV_EXIT); 183 184 if (sflag || minusc == NULL) { 185state4: /* XXX ??? - why isn't this before the "if" statement */ 186 cmdloop(1); 187 } 188#if PROFILE 189 monitor(0); 190#endif 191#if GPROF 192 { 193 extern void _mcleanup(void); 194 _mcleanup(); 195 } 196#endif 197 exitshell(); 198 /* NOTREACHED */ 199} 200 201STATIC void 202evalifsubshell(zx_handle_t ast_vmo) 203{ 204 if (ast_vmo == ZX_HANDLE_INVALID) 205 return; 206 207 uint64_t size; 208 zx_status_t status = zx_vmo_get_size(ast_vmo, &size); 209 if (status != ZX_OK) 210 exit(status); 211 212 char buffer[size]; 213 214 status = zx_vmo_read(ast_vmo, buffer, 0, size); 215 if (status < 0) 216 exit(status); 217 218 struct nodelist *nlist = codec_decode(buffer, size); 219 if (nlist == NULL) { 220 return; 221 } 222 while (nlist->next) { 223 evaltree(nlist->n, 0); 224 nlist = nlist->next; 225 } 226 evaltree(nlist->n, EV_EXIT); 227 /* NOTREACHED */ 228} 229 230 231/* 232 * Read and execute commands. "Top" is nonzero for the top level command 233 * loop; it turns on prompting if the shell is interactive. 234 */ 235 236static int 237cmdloop(int top) 238{ 239 union node *n; 240 struct stackmark smark; 241 int inter; 242 int status = 0; 243 int numeof = 0; 244 245 TRACE(("cmdloop(%d) called\n", top)); 246 for (;;) { 247 int skip; 248 249 setstackmark(&smark); 250 if (jobctl) 251 showjobs(out2, SHOW_CHANGED); 252 inter = 0; 253 if (iflag && top) 254 inter++; 255 n = parsecmd(inter); 256 /* showtree(n); DEBUG */ 257 if (n == NEOF) { 258 if (!top || numeof >= 50) 259 break; 260 if (!stoppedjobs()) { 261 if (!Iflag) 262 break; 263 out2str("\nUse \"exit\" to leave shell.\n"); 264 } 265 numeof++; 266 } else if (nflag == 0) { 267 int i; 268 269 job_warning = (job_warning == 2) ? 1 : 0; 270 numeof = 0; 271 i = evaltree(n, 0); 272 if (n) 273 status = i; 274 } 275 popstackmark(&smark); 276 277 skip = evalskip; 278 if (skip) { 279 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); 280 break; 281 } 282 } 283 284 return status; 285} 286 287 288 289/* 290 * Read /etc/profile or .profile. Return on error. 291 */ 292 293STATIC void 294read_profile(const char *name) 295{ 296 name = expandstr(name); 297 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) 298 return; 299 300 cmdloop(0); 301 popfile(); 302} 303 304 305 306/* 307 * Read a file containing shell functions. 308 */ 309 310void 311readcmdfile(char *name) 312{ 313 setinputfile(name, INPUT_PUSH_FILE); 314 cmdloop(0); 315 popfile(); 316} 317 318 319 320/* 321 * Take commands from a file. To be compatible we should do a path 322 * search for the file, which is necessary to find sub-commands. 323 */ 324 325 326STATIC char * 327find_dot_file(char *basename) 328{ 329 char *fullname; 330 const char *path = pathval(); 331 struct stat statb; 332 333 /* don't try this for absolute or relative paths */ 334 if (strchr(basename, '/')) 335 return basename; 336 337 while ((fullname = padvance(&path, basename)) != NULL) { 338 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { 339 /* 340 * Don't bother freeing here, since it will 341 * be freed by the caller. 342 */ 343 return fullname; 344 } 345 stunalloc(fullname); 346 } 347 348 /* not found in the PATH */ 349 sh_error("%s: not found", basename); 350 /* NOTREACHED */ 351} 352 353int 354dotcmd(int argc, char **argv) 355{ 356 int status = 0; 357 358 nextopt(nullstr); 359 argv = argptr; 360 361 if (*argv) { 362 char *fullname; 363 364 fullname = find_dot_file(*argv); 365 setinputfile(fullname, INPUT_PUSH_FILE); 366 commandname = fullname; 367 status = cmdloop(0); 368 popfile(); 369 } 370 371 return status; 372} 373 374 375int 376exitcmd(int argc, char **argv) 377{ 378 if (stoppedjobs()) 379 return 0; 380 381 if (argc > 1) { 382 int status = number(argv[1]); 383 384 exitstatus = status; 385 if (savestatus >= 0) 386 savestatus = status; 387 } 388 389 exraise(EXEXIT); 390 /* NOTREACHED */ 391} 392