1 1.90 kre /* $NetBSD: main.c,v 1.90 2023/04/07 10:34:13 kre Exp $ */ 2 1.17 cgd 3 1.1 cgd /*- 4 1.9 jtc * Copyright (c) 1991, 1993 5 1.9 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Kenneth Almquist. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.47 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.26 christos #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.51 lukem __COPYRIGHT("@(#) Copyright (c) 1991, 1993\ 38 1.51 lukem The Regents of the University of California. All rights reserved."); 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #ifndef lint 42 1.17 cgd #if 0 43 1.21 christos static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; 44 1.17 cgd #else 45 1.90 kre __RCSID("$NetBSD: main.c,v 1.90 2023/04/07 10:34:13 kre Exp $"); 46 1.17 cgd #endif 47 1.1 cgd #endif /* not lint */ 48 1.1 cgd 49 1.36 simonb #include <errno.h> 50 1.11 jtc #include <stdio.h> 51 1.1 cgd #include <signal.h> 52 1.11 jtc #include <sys/stat.h> 53 1.12 jtc #include <unistd.h> 54 1.87 kre #include <stdbool.h> 55 1.55 christos #include <stdlib.h> 56 1.46 christos #include <locale.h> 57 1.1 cgd #include <fcntl.h> 58 1.18 christos 59 1.18 christos 60 1.1 cgd #include "shell.h" 61 1.1 cgd #include "main.h" 62 1.1 cgd #include "mail.h" 63 1.1 cgd #include "options.h" 64 1.57 christos #include "builtins.h" 65 1.1 cgd #include "output.h" 66 1.1 cgd #include "parser.h" 67 1.1 cgd #include "nodes.h" 68 1.20 christos #include "expand.h" 69 1.1 cgd #include "eval.h" 70 1.1 cgd #include "jobs.h" 71 1.1 cgd #include "input.h" 72 1.1 cgd #include "trap.h" 73 1.1 cgd #include "var.h" 74 1.18 christos #include "show.h" 75 1.1 cgd #include "memalloc.h" 76 1.1 cgd #include "error.h" 77 1.1 cgd #include "init.h" 78 1.1 cgd #include "mystring.h" 79 1.11 jtc #include "exec.h" 80 1.23 christos #include "cd.h" 81 1.68 kre #include "redir.h" 82 1.1 cgd 83 1.1 cgd #define PROFILE 0 84 1.1 cgd 85 1.1 cgd int rootpid; 86 1.1 cgd int rootshell; 87 1.77 kre struct jmploc main_handler; 88 1.68 kre int max_user_fd; 89 1.86 kre long user_fd_limit; 90 1.87 kre bool privileged; 91 1.1 cgd #if PROFILE 92 1.1 cgd short profile_buf[16384]; 93 1.1 cgd extern int etext(); 94 1.1 cgd #endif 95 1.1 cgd 96 1.45 christos STATIC void read_profile(const char *); 97 1.1 cgd 98 1.1 cgd /* 99 1.1 cgd * Main routine. We initialize things, parse the arguments, execute 100 1.1 cgd * profiles if we're a login shell, and then call cmdloop to execute 101 1.1 cgd * commands. The setjmp call sets up the location to jump to when an 102 1.1 cgd * exception occurs. When an exception occurs the variable "state" 103 1.1 cgd * is used to figure out how far we had gotten. 104 1.1 cgd */ 105 1.1 cgd 106 1.15 cgd int 107 1.45 christos main(int argc, char **argv) 108 1.15 cgd { 109 1.1 cgd struct stackmark smark; 110 1.1 cgd volatile int state; 111 1.1 cgd char *shinit; 112 1.59 christos uid_t uid; 113 1.59 christos gid_t gid; 114 1.85 fox sigset_t mask; 115 1.87 kre bool waspriv; 116 1.59 christos 117 1.83 kre /* 118 1.83 kre * If we happen to be invoked with SIGCHLD ignored, we cannot 119 1.83 kre * successfully do almost anything. Perhaps we should remember 120 1.83 kre * its state and pass it on ignored to children if it was ignored 121 1.83 kre * on entry, but that seems like just leaving the shit on the 122 1.83 kre * footpath for someone else to fall into... 123 1.83 kre */ 124 1.83 kre (void)signal(SIGCHLD, SIG_DFL); 125 1.84 kre /* 126 1.84 kre * Similarly, SIGCHLD must not be blocked 127 1.84 kre */ 128 1.85 fox sigemptyset(&mask); 129 1.85 fox sigaddset(&mask, SIGCHLD); 130 1.85 fox sigprocmask(SIG_UNBLOCK, &mask, NULL); 131 1.83 kre 132 1.59 christos uid = getuid(); 133 1.59 christos gid = getgid(); 134 1.46 christos 135 1.87 kre waspriv = privileged = (uid != geteuid()) || (gid != getegid()); 136 1.87 kre 137 1.68 kre max_user_fd = fcntl(0, F_MAXFD); 138 1.68 kre if (max_user_fd < 2) 139 1.68 kre max_user_fd = 2; 140 1.86 kre user_fd_limit = sysconf(_SC_OPEN_MAX); 141 1.68 kre 142 1.46 christos setlocale(LC_ALL, ""); 143 1.1 cgd 144 1.55 christos posix = getenv("POSIXLY_CORRECT") != NULL; 145 1.1 cgd #if PROFILE 146 1.1 cgd monitor(4, etext, profile_buf, sizeof profile_buf, 50); 147 1.1 cgd #endif 148 1.1 cgd state = 0; 149 1.77 kre if (setjmp(main_handler.loc)) { 150 1.1 cgd /* 151 1.1 cgd * When a shell procedure is executed, we raise the 152 1.1 cgd * exception EXSHELLPROC to clean up before executing 153 1.1 cgd * the shell procedure. 154 1.1 cgd */ 155 1.24 christos switch (exception) { 156 1.24 christos case EXSHELLPROC: 157 1.1 cgd rootpid = getpid(); 158 1.1 cgd rootshell = 1; 159 1.1 cgd minusc = NULL; 160 1.1 cgd state = 3; 161 1.24 christos break; 162 1.24 christos 163 1.24 christos case EXEXEC: 164 1.24 christos exitstatus = exerrno; 165 1.24 christos break; 166 1.24 christos 167 1.24 christos case EXERROR: 168 1.24 christos exitstatus = 2; 169 1.24 christos break; 170 1.24 christos 171 1.24 christos default: 172 1.24 christos break; 173 1.24 christos } 174 1.24 christos 175 1.24 christos if (exception != EXSHELLPROC) { 176 1.75 kre if (state == 0 || iflag == 0 || ! rootshell || 177 1.75 kre exception == EXEXIT) 178 1.45 christos exitshell(exitstatus); 179 1.24 christos } 180 1.1 cgd reset(); 181 1.63 christos if (exception == EXINT) { 182 1.1 cgd out2c('\n'); 183 1.1 cgd flushout(&errout); 184 1.1 cgd } 185 1.1 cgd popstackmark(&smark); 186 1.1 cgd FORCEINTON; /* enable interrupts */ 187 1.1 cgd if (state == 1) 188 1.1 cgd goto state1; 189 1.1 cgd else if (state == 2) 190 1.1 cgd goto state2; 191 1.9 jtc else if (state == 3) 192 1.9 jtc goto state3; 193 1.1 cgd else 194 1.9 jtc goto state4; 195 1.1 cgd } 196 1.77 kre handler = &main_handler; 197 1.1 cgd #ifdef DEBUG 198 1.70 kre #if DEBUG >= 2 199 1.69 kre debug = 1; /* this may be reset by procargs() later */ 200 1.48 jmmv #endif 201 1.1 cgd opentrace(); 202 1.87 kre if (privileged) 203 1.87 kre trputs("Privileged "); 204 1.1 cgd trputs("Shell args: "); trargs(argv); 205 1.70 kre #if DEBUG >= 3 206 1.71 kre set_debug(((DEBUG)==3 ? "_@" : "++"), 1); 207 1.70 kre #endif 208 1.1 cgd #endif 209 1.1 cgd rootpid = getpid(); 210 1.1 cgd rootshell = 1; 211 1.1 cgd init(); 212 1.50 christos initpwd(); 213 1.1 cgd setstackmark(&smark); 214 1.1 cgd procargs(argc, argv); 215 1.89 kre setvar_invocation(argc, argv); 216 1.59 christos 217 1.87 kre #if 0 /* This now happens (indirectly) in the procargs() just above */ 218 1.59 christos /* 219 1.59 christos * Limit bogus system(3) or popen(3) calls in setuid binaries, 220 1.59 christos * by requiring the -p flag 221 1.59 christos */ 222 1.59 christos if (!pflag && (uid != geteuid() || gid != getegid())) { 223 1.59 christos setuid(uid); 224 1.59 christos setgid(gid); 225 1.59 christos /* PS1 might need to be changed accordingly. */ 226 1.59 christos choose_ps1(); 227 1.59 christos } 228 1.87 kre #else /* except for this one little bit */ 229 1.87 kre if (waspriv && !privileged) 230 1.87 kre choose_ps1(); 231 1.87 kre #endif 232 1.59 christos 233 1.89 kre if (loginsh) { 234 1.1 cgd state = 1; 235 1.1 cgd read_profile("/etc/profile"); 236 1.79 kre state1: 237 1.1 cgd state = 2; 238 1.87 kre if (!privileged) { 239 1.87 kre char *profile; 240 1.87 kre const char *home; 241 1.87 kre 242 1.87 kre home = lookupvar("HOME"); 243 1.87 kre if (home == NULL) 244 1.87 kre home = nullstr; 245 1.88 kre profile = ststrcat(NULL, home, "/.profile", STSTRC_END); 246 1.87 kre read_profile(profile); 247 1.87 kre stunalloc(profile); 248 1.87 kre } 249 1.87 kre #if 0 /* FreeBSD does (effectively) ...*/ 250 1.87 kre else 251 1.87 kre read_profile("/etc/suid_profile"); 252 1.87 kre #endif 253 1.24 christos } 254 1.79 kre state2: 255 1.1 cgd state = 3; 256 1.87 kre if ((iflag || !posix) && !privileged) { 257 1.79 kre struct stackmark env_smark; 258 1.79 kre 259 1.79 kre setstackmark(&env_smark); 260 1.14 jtc if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { 261 1.14 jtc state = 3; 262 1.79 kre read_profile(expandenv(shinit)); 263 1.14 jtc } 264 1.79 kre popstackmark(&env_smark); 265 1.9 jtc } 266 1.79 kre state3: 267 1.9 jtc state = 4; 268 1.71 kre line_number = 1; /* undo anything from profile files */ 269 1.71 kre 270 1.33 christos if (sflag == 0 || minusc) { 271 1.32 christos static int sigs[] = { 272 1.90 kre SIGINT, SIGQUIT, SIGHUP, 273 1.31 christos #ifdef SIGTSTP 274 1.32 christos SIGTSTP, 275 1.31 christos #endif 276 1.32 christos SIGPIPE 277 1.31 christos }; 278 1.32 christos #define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) 279 1.53 lukem size_t i; 280 1.31 christos 281 1.31 christos for (i = 0; i < SIGSSIZE; i++) 282 1.42 christos setsignal(sigs[i], 0); 283 1.33 christos } 284 1.33 christos 285 1.87 kre rststackmark(&smark); /* this one is never popped */ 286 1.87 kre 287 1.33 christos if (minusc) 288 1.75 kre evalstring(minusc, sflag ? 0 : EV_EXIT); 289 1.33 christos 290 1.1 cgd if (sflag || minusc == NULL) { 291 1.79 kre state4: /* XXX ??? - why isn't this before the "if" statement */ 292 1.1 cgd cmdloop(1); 293 1.82 kre if (iflag) { 294 1.82 kre out2str("\n"); 295 1.82 kre flushout(&errout); 296 1.82 kre } 297 1.1 cgd } 298 1.1 cgd #if PROFILE 299 1.1 cgd monitor(0); 300 1.1 cgd #endif 301 1.80 kre line_number = plinno; 302 1.1 cgd exitshell(exitstatus); 303 1.28 mycroft /* NOTREACHED */ 304 1.1 cgd } 305 1.1 cgd 306 1.1 cgd 307 1.1 cgd /* 308 1.1 cgd * Read and execute commands. "Top" is nonzero for the top level command 309 1.1 cgd * loop; it turns on prompting if the shell is interactive. 310 1.1 cgd */ 311 1.1 cgd 312 1.1 cgd void 313 1.45 christos cmdloop(int top) 314 1.15 cgd { 315 1.1 cgd union node *n; 316 1.1 cgd struct stackmark smark; 317 1.1 cgd int inter; 318 1.9 jtc int numeof = 0; 319 1.58 christos enum skipstate skip; 320 1.1 cgd 321 1.72 kre CTRACE(DBG_ALWAYS, ("cmdloop(%d) called\n", top)); 322 1.1 cgd setstackmark(&smark); 323 1.1 cgd for (;;) { 324 1.1 cgd if (pendingsigs) 325 1.1 cgd dotrap(); 326 1.1 cgd inter = 0; 327 1.49 christos if (iflag == 1 && top) { 328 1.45 christos inter = 1; 329 1.45 christos showjobs(out2, SHOW_CHANGED); 330 1.1 cgd chkmail(0); 331 1.45 christos flushout(&errout); 332 1.61 christos nflag = 0; 333 1.1 cgd } 334 1.1 cgd n = parsecmd(inter); 335 1.72 kre VXTRACE(DBG_PARSE|DBG_EVAL|DBG_CMDS,("cmdloop: "),showtree(n)); 336 1.1 cgd if (n == NEOF) { 337 1.9 jtc if (!top || numeof >= 50) 338 1.1 cgd break; 339 1.61 christos if (nflag) 340 1.61 christos break; 341 1.9 jtc if (!stoppedjobs()) { 342 1.62 martin if (!iflag || !Iflag) 343 1.9 jtc break; 344 1.9 jtc out2str("\nUse \"exit\" to leave shell.\n"); 345 1.9 jtc } 346 1.1 cgd numeof++; 347 1.1 cgd } else if (n != NULL && nflag == 0) { 348 1.9 jtc job_warning = (job_warning == 2) ? 1 : 0; 349 1.9 jtc numeof = 0; 350 1.74 kre evaltree(n, 0); 351 1.1 cgd } 352 1.76 kre rststackmark(&smark); 353 1.58 christos 354 1.58 christos /* 355 1.58 christos * Any SKIP* can occur here! SKIP(FUNC|BREAK|CONT) occur when 356 1.58 christos * a dotcmd is in a loop or a function body and appropriate 357 1.58 christos * built-ins occurs in file scope in the sourced file. Values 358 1.58 christos * other than SKIPFILE are reset by the appropriate eval*() 359 1.58 christos * that contained the dotcmd() call. 360 1.58 christos */ 361 1.58 christos skip = current_skipstate(); 362 1.58 christos if (skip != SKIPNONE) { 363 1.58 christos if (skip == SKIPFILE) 364 1.58 christos stop_skipping(); 365 1.22 christos break; 366 1.22 christos } 367 1.1 cgd } 368 1.39 christos popstackmark(&smark); 369 1.1 cgd } 370 1.1 cgd 371 1.1 cgd 372 1.1 cgd 373 1.1 cgd /* 374 1.1 cgd * Read /etc/profile or .profile. Return on error. 375 1.1 cgd */ 376 1.1 cgd 377 1.1 cgd STATIC void 378 1.45 christos read_profile(const char *name) 379 1.30 cjs { 380 1.1 cgd int fd; 381 1.30 cjs int xflag_set = 0; 382 1.30 cjs int vflag_set = 0; 383 1.1 cgd 384 1.79 kre if (*name == '\0') 385 1.79 kre return; 386 1.79 kre 387 1.1 cgd INTOFF; 388 1.1 cgd if ((fd = open(name, O_RDONLY)) >= 0) 389 1.1 cgd setinputfd(fd, 1); 390 1.1 cgd INTON; 391 1.1 cgd if (fd < 0) 392 1.1 cgd return; 393 1.30 cjs /* -q turns off -x and -v just when executing init files */ 394 1.30 cjs if (qflag) { 395 1.30 cjs if (xflag) 396 1.30 cjs xflag = 0, xflag_set = 1; 397 1.30 cjs if (vflag) 398 1.30 cjs vflag = 0, vflag_set = 1; 399 1.30 cjs } 400 1.81 kre (void)set_dot_funcnest(1); /* allow profile to "return" */ 401 1.1 cgd cmdloop(0); 402 1.81 kre (void)set_dot_funcnest(0); 403 1.30 cjs if (qflag) { 404 1.30 cjs if (xflag_set) 405 1.30 cjs xflag = 1; 406 1.30 cjs if (vflag_set) 407 1.30 cjs vflag = 1; 408 1.30 cjs } 409 1.1 cgd popfile(); 410 1.1 cgd } 411 1.1 cgd 412 1.1 cgd 413 1.1 cgd 414 1.1 cgd /* 415 1.1 cgd * Read a file containing shell functions. 416 1.1 cgd */ 417 1.1 cgd 418 1.1 cgd void 419 1.45 christos readcmdfile(char *name) 420 1.16 cgd { 421 1.1 cgd int fd; 422 1.1 cgd 423 1.1 cgd INTOFF; 424 1.1 cgd if ((fd = open(name, O_RDONLY)) >= 0) 425 1.1 cgd setinputfd(fd, 1); 426 1.1 cgd else 427 1.1 cgd error("Can't open %s", name); 428 1.1 cgd INTON; 429 1.1 cgd cmdloop(0); 430 1.1 cgd popfile(); 431 1.1 cgd } 432 1.1 cgd 433 1.1 cgd 434 1.1 cgd 435 1.15 cgd int 436 1.45 christos exitcmd(int argc, char **argv) 437 1.15 cgd { 438 1.9 jtc if (stoppedjobs()) 439 1.18 christos return 0; 440 1.1 cgd if (argc > 1) 441 1.78 kre exitshell(number(argv[1])); 442 1.78 kre else 443 1.78 kre exitshell_savedstatus(); 444 1.28 mycroft /* NOTREACHED */ 445 1.1 cgd } 446