1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. 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 <stdio.h> 36 #include <signal.h> 37 #include <sys/stat.h> 38 #include <unistd.h> 39 #include <fcntl.h> 40 #include <locale.h> 41 #include <errno.h> 42 #include <termios.h> 43 #include <paths.h> 44 #include <unistd.h> 45 46 #include "shell.h" 47 #include "main.h" 48 #include "mail.h" 49 #include "options.h" 50 #include "output.h" 51 #include "parser.h" 52 #include "nodes.h" 53 #include "expand.h" 54 #include "eval.h" 55 #include "jobs.h" 56 #include "input.h" 57 #include "trap.h" 58 #include "var.h" 59 #include "show.h" 60 #include "memalloc.h" 61 #include "error.h" 62 #include "mystring.h" 63 #include "exec.h" 64 #include "cd.h" 65 #include "redir.h" 66 #include "builtins.h" 67 #ifndef NO_HISTORY 68 #include "myhistedit.h" 69 #endif 70 71 int rootpid; 72 int rootshell; 73 struct jmploc main_handler; 74 int localeisutf8, initial_localeisutf8; 75 76 static void reset(void); 77 static void cmdloop(int); 78 static void read_profile(const char *); 79 static char *find_dot_file(char *); 80 81 /* 82 * Main routine. We initialize things, parse the arguments, execute 83 * profiles if we're a login shell, and then call cmdloop to execute 84 * commands. The setjmp call sets up the location to jump to when an 85 * exception occurs. When an exception occurs the variable "state" 86 * is used to figure out how far we had gotten. 87 */ 88 89 int 90 main(int argc, char *argv[]) 91 { 92 /* 93 * As smark is accessed after a longjmp, it cannot be a local in main(). 94 * The C standard specifies that the values of non-volatile local 95 * variables are unspecified after a jump if modified between the 96 * setjmp and longjmp. 97 */ 98 static struct stackmark smark, smark2; 99 volatile int state; 100 char *shinit; 101 int login; 102 103 (void) setlocale(LC_ALL, ""); 104 initcharset(); 105 state = 0; 106 if (setjmp(main_handler.loc)) { 107 if (state == 0 || iflag == 0 || ! rootshell || 108 exception == EXEXIT) 109 exitshell(exitstatus); 110 reset(); 111 if (exception == EXINT) 112 out2fmt_flush("\n"); 113 popstackmark(&smark); 114 FORCEINTON; /* enable interrupts */ 115 if (state == 1) 116 goto state1; 117 else if (state == 2) 118 goto state2; 119 else if (state == 3) 120 goto state3; 121 else 122 goto state4; 123 } 124 handler = &main_handler; 125 #ifdef DEBUG 126 opentrace(); 127 trputs("Shell args: "); trargs(argv); 128 #endif 129 rootpid = getpid(); 130 if (rootpid == 1) { 131 /* 132 * Make sh usable for invocation as interactive init 133 * substitute with init_path=/bin/sh, by opening 134 * file descriptors 0, 1, and 2 on /dev/console. 135 */ 136 if (fcntl(STDIN_FILENO, F_GETFL, NULL) == -1 && errno == EBADF) { 137 (void)open(_PATH_CONSOLE, O_RDWR); 138 (void)setsid(); 139 (void)tcsetsid(STDIN_FILENO, rootpid); 140 } 141 if (fcntl(STDOUT_FILENO, F_GETFL, NULL) == -1 && errno == EBADF) 142 (void)dup2(STDIN_FILENO, STDOUT_FILENO); 143 if (fcntl(STDERR_FILENO, F_GETFL, NULL) == -1 && errno == EBADF) 144 (void)dup2(STDIN_FILENO, STDERR_FILENO); 145 } 146 rootshell = 1; 147 INTOFF; 148 initvar(); 149 setstackmark(&smark); 150 setstackmark(&smark2); 151 login = procargs(argc, argv); 152 trap_init(); 153 pwd_init(iflag); 154 INTON; 155 if (iflag) 156 chkmail(1); 157 if (login) { 158 state = 1; 159 read_profile("/etc/profile"); 160 state1: 161 state = 2; 162 if (privileged == 0) 163 read_profile("${HOME-}/.profile"); 164 else 165 read_profile("/etc/suid_profile"); 166 } 167 state2: 168 state = 3; 169 if (!privileged && iflag) { 170 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 171 state = 3; 172 read_profile(shinit); 173 } 174 } 175 #ifndef NO_HISTORY 176 if (iflag) 177 histload(); 178 #endif 179 state3: 180 state = 4; 181 popstackmark(&smark2); 182 if (minusc) { 183 evalstring(minusc, sflag ? 0 : EV_EXIT); 184 } 185 state4: 186 if (sflag || minusc == NULL) { 187 cmdloop(1); 188 } 189 exitshell(exitstatus); 190 /*NOTREACHED*/ 191 return 0; 192 } 193 194 static void 195 reset(void) 196 { 197 reseteval(); 198 resetinput(); 199 } 200 201 /* 202 * Read and execute commands. "Top" is nonzero for the top level command 203 * loop; it turns on prompting if the shell is interactive. 204 */ 205 206 static void 207 cmdloop(int top) 208 { 209 union node *n; 210 struct stackmark smark; 211 int inter; 212 int numeof = 0; 213 214 TRACE(("cmdloop(%d) called\n", top)); 215 setstackmark(&smark); 216 for (;;) { 217 if (pendingsig) 218 dotrap(); 219 inter = 0; 220 if (iflag && top) { 221 inter++; 222 showjobs(1, SHOWJOBS_DEFAULT); 223 chkmail(0); 224 flushout(&output); 225 } 226 n = parsecmd(inter); 227 /* showtree(n); DEBUG */ 228 if (n == NEOF) { 229 if (!top || numeof >= 50) 230 break; 231 if (!stoppedjobs()) { 232 if (!Iflag) 233 break; 234 out2fmt_flush("\nUse \"exit\" to leave shell.\n"); 235 } 236 numeof++; 237 } else if (n != NULL && nflag == 0) { 238 job_warning = (job_warning == 2) ? 1 : 0; 239 numeof = 0; 240 evaltree(n, 0); 241 } 242 popstackmark(&smark); 243 setstackmark(&smark); 244 if (evalskip != 0) { 245 if (evalskip == SKIPRETURN) 246 evalskip = 0; 247 break; 248 } 249 } 250 popstackmark(&smark); 251 if (top && iflag) { 252 out2c('\n'); 253 flushout(out2); 254 } 255 } 256 257 258 259 /* 260 * Read /etc/profile or .profile. Return on error. 261 */ 262 263 static void 264 read_profile(const char *name) 265 { 266 int fd; 267 const char *expandedname; 268 int oflags = O_RDONLY | O_CLOEXEC; 269 270 if (verifyflag) 271 oflags |= O_VERIFY; 272 273 expandedname = expandstr(name); 274 if (expandedname == NULL) 275 return; 276 INTOFF; 277 if ((fd = open(expandedname, oflags)) >= 0) 278 setinputfd(fd, 1); 279 INTON; 280 if (fd < 0) 281 return; 282 cmdloop(0); 283 popfile(); 284 } 285 286 287 288 /* 289 * Read a file containing shell functions. 290 */ 291 292 void 293 readcmdfile(const char *name, int verify) 294 { 295 setinputfile(name, 1, verify); 296 cmdloop(0); 297 popfile(); 298 } 299 300 301 302 /* 303 * Take commands from a file. To be compatible we should do a path 304 * search for the file, which is necessary to find sub-commands. 305 */ 306 307 308 static char * 309 find_dot_file(char *basename) 310 { 311 char *fullname; 312 const char *opt; 313 const char *path = pathval(); 314 struct stat statb; 315 316 /* don't try this for absolute or relative paths */ 317 if( strchr(basename, '/')) 318 return basename; 319 320 while ((fullname = padvance(&path, &opt, basename)) != NULL) { 321 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { 322 /* 323 * Don't bother freeing here, since it will 324 * be freed by the caller. 325 */ 326 return fullname; 327 } 328 stunalloc(fullname); 329 } 330 return basename; 331 } 332 333 int 334 dotcmd(int argc, char **argv) 335 { 336 char *filename, *fullname; 337 338 if (argc < 2) 339 error("missing filename"); 340 341 exitstatus = 0; 342 343 /* 344 * Because we have historically not supported any options, 345 * only treat "--" specially. 346 */ 347 filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1]; 348 349 fullname = find_dot_file(filename); 350 setinputfile(fullname, 1, -1 /* verify */); 351 commandname = fullname; 352 cmdloop(0); 353 popfile(); 354 return exitstatus; 355 } 356 357 358 int 359 exitcmd(int argc, char **argv) 360 { 361 if (stoppedjobs()) 362 return 0; 363 if (argc > 1) 364 exitshell(number(argv[1])); 365 else 366 exitshell_savedstatus(); 367 } 368