csh.c revision 1.19
1/* $OpenBSD: csh.c,v 1.19 2003/01/08 06:54:16 deraadt Exp $ */ 2/* $NetBSD: csh.c,v 1.14 1995/04/29 23:21:28 mycroft Exp $ */ 3 4/*- 5 * Copyright (c) 1980, 1991, 1993 6 * The Regents of the University of California. All rights reserved. 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38static char copyright[] = 39"@(#) Copyright (c) 1980, 1991, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44#if 0 45static char sccsid[] = "@(#)csh.c 8.2 (Berkeley) 10/12/93"; 46#else 47static char rcsid[] = "$OpenBSD: csh.c,v 1.19 2003/01/08 06:54:16 deraadt Exp $"; 48#endif 49#endif /* not lint */ 50 51#include <sys/types.h> 52#include <sys/ioctl.h> 53#include <sys/stat.h> 54#include <sys/param.h> 55#include <fcntl.h> 56#include <errno.h> 57#include <pwd.h> 58#include <stdlib.h> 59#include <string.h> 60#include <locale.h> 61#include <unistd.h> 62#include <vis.h> 63#include <stdarg.h> 64 65#include "csh.h" 66#include "proc.h" 67#include "extern.h" 68#include "pathnames.h" 69 70extern bool MapsAreInited; 71extern bool NLSMapsAreInited; 72 73/* 74 * C Shell 75 * 76 * Bill Joy, UC Berkeley, California, USA 77 * October 1978, May 1980 78 * 79 * Jim Kulp, IIASA, Laxenburg, Austria 80 * April 1980 81 * 82 * Christos Zoulas, Cornell University 83 * June, 1991 84 */ 85 86Char *dumphist[] = {STRhistory, STRmh, 0, 0}; 87Char *loadhist[] = {STRsource, STRmh, STRtildothist, 0}; 88 89int nofile = 0; 90bool reenter = 0; 91bool nverbose = 0; 92bool nexececho = 0; 93bool quitit = 0; 94bool fast = 0; 95bool batch = 0; 96bool mflag = 0; 97bool prompt = 1; 98bool enterhist = 0; 99bool tellwhat = 0; 100 101extern char **environ; 102 103static int readf(void *, char *, int); 104static fpos_t seekf(void *, fpos_t, int); 105static int writef(void *, const char *, int); 106static int closef(void *); 107static int srccat(Char *, Char *); 108static int srcfile(char *, bool, bool); 109static void phup(int); 110static void srcunit(int, bool, bool); 111static void mailchk(void); 112static Char **defaultpath(void); 113 114int 115main(argc, argv) 116 int argc; 117 char **argv; 118{ 119 register Char *cp; 120 register char *tcp; 121 register int f; 122 register char **tempv; 123 struct sigaction oact; 124 sigset_t sigset; 125 126 cshin = stdin; 127 cshout = stdout; 128 csherr = stderr; 129 130 settimes(); /* Immed. estab. timing base */ 131 132 /* 133 * Initialize non constant strings 134 */ 135#ifdef _PATH_BSHELL 136 STR_BSHELL = SAVE(_PATH_BSHELL); 137#endif 138#ifdef _PATH_CSHELL 139 STR_SHELLPATH = SAVE(_PATH_CSHELL); 140#endif 141 STR_environ = blk2short(environ); 142 environ = short2blk(STR_environ); /* So that we can free it */ 143 STR_WORD_CHARS = SAVE(WORD_CHARS); 144 145 HIST = '!'; 146 HISTSUB = '^'; 147 word_chars = STR_WORD_CHARS; 148 149 tempv = argv; 150 if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ 151 quitit = 1; 152 uid = getuid(); 153 gid = getgid(); 154 euid = geteuid(); 155 egid = getegid(); 156 /* 157 * We are a login shell if: 1. we were invoked as -<something> and we had 158 * no arguments 2. or we were invoked only with the -l flag 159 */ 160 loginsh = (**tempv == '-' && argc == 1) || 161 (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && 162 tempv[1][2] == '\0'); 163 164 if (loginsh && **tempv != '-') { 165 /* 166 * Mangle the argv space 167 */ 168 tempv[1][0] = '\0'; 169 tempv[1][1] = '\0'; 170 tempv[1] = NULL; 171 for (tcp = *tempv; *tcp++;) 172 continue; 173 for (tcp--; tcp >= *tempv; tcp--) 174 tcp[1] = tcp[0]; 175 *++tcp = '-'; 176 argc--; 177 } 178 if (loginsh) 179 (void) time(&chktim); 180 181 AsciiOnly = 1; 182#ifdef NLS 183 (void) setlocale(LC_ALL, ""); 184 { 185 int k; 186 187 for (k = 0200; k <= 0377 && !Isprint(k); k++) 188 continue; 189 AsciiOnly = k > 0377; 190 } 191#else 192 AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; 193#endif /* NLS */ 194 195 /* 196 * Move the descriptors to safe places. The variable didfds is 0 while we 197 * have only FSH* to work with. When didfds is true, we have 0,1,2 and 198 * prefer to use these. 199 */ 200 initdesc(); 201 /* 202 * XXX: This is to keep programs that use stdio happy. 203 * what we really want is freunopen() .... 204 * Closing cshin cshout and csherr (which are really stdin stdout 205 * and stderr at this point and then reopening them in the same order 206 * gives us again stdin == cshin stdout == cshout and stderr == csherr. 207 * If that was not the case builtins like printf that use stdio 208 * would break. But in any case we could fix that with memcpy and 209 * a bit of pointer manipulation... 210 * Fortunately this is not needed under the current implementation 211 * of stdio. 212 */ 213 (void) fclose(cshin); 214 (void) fclose(cshout); 215 (void) fclose(csherr); 216 if (!(cshin = funopen((void *) &SHIN, readf, writef, seekf, closef))) 217 exit(1); 218 if (!(cshout = funopen((void *) &SHOUT, readf, writef, seekf, closef))) 219 exit(1); 220 if (!(csherr = funopen((void *) &SHERR, readf, writef, seekf, closef))) 221 exit(1); 222 (void) setvbuf(cshin, NULL, _IOLBF, 0); 223 (void) setvbuf(cshout, NULL, _IOLBF, 0); 224 (void) setvbuf(csherr, NULL, _IOLBF, 0); 225 226 /* 227 * Initialize the shell variables. ARGV and PROMPT are initialized later. 228 * STATUS is also munged in several places. CHILD is munged when 229 * forking/waiting 230 */ 231 set(STRstatus, Strsave(STR0)); 232 233 if ((tcp = getenv("HOME")) != NULL && strlen(tcp) < MAXPATHLEN) 234 cp = SAVE(tcp); 235 else 236 cp = NULL; 237 238 if (cp == NULL) 239 fast = 1; /* No home -> can't read scripts */ 240 else 241 set(STRhome, cp); 242 dinit(cp); /* dinit thinks that HOME == cwd in a login 243 * shell */ 244 /* 245 * Grab other useful things from the environment. Should we grab 246 * everything?? 247 */ 248 if ((tcp = getenv("LOGNAME")) != NULL || 249 (tcp = getenv("USER")) != NULL) 250 set(STRuser, quote(SAVE(tcp))); 251 if ((tcp = getenv("TERM")) != NULL) 252 set(STRterm, quote(SAVE(tcp))); 253 254 /* 255 * Re-initialize path if set in environment 256 */ 257 if ((tcp = getenv("PATH")) == NULL) 258 setq(STRpath, defaultpath(), &shvhed); 259 else 260 importpath(str2short(tcp)); 261 262 set(STRshell, Strsave(STR_SHELLPATH)); 263 264 doldol = putn((int) getpid()); /* For $$ */ 265 266 /* 267 * Record the interrupt states from the parent process. If the parent is 268 * non-interruptible our hand must be forced or we (and our children) won't 269 * be either. Our children inherit termination from our parent. We catch it 270 * only if we are the login shell. 271 */ 272 /* parents interruptibility */ 273 (void) sigaction(SIGINT, NULL, &oact); 274 parintr = oact.sa_handler; 275 (void) sigaction(SIGTERM, NULL, &oact); 276 parterm = oact.sa_handler; 277 278 /* catch these all, login shell or not */ 279 (void) signal(SIGHUP, phup); /* exit processing on HUP */ 280 (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 281 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 282 283 /* 284 * Process the arguments. 285 * 286 * Note that processing of -v/-x is actually delayed till after script 287 * processing. 288 * 289 * We set the first character of our name to be '-' if we are a shell 290 * running interruptible commands. Many programs which examine ps'es 291 * use this to filter such shells out. 292 */ 293 argc--, tempv++; 294 while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { 295 do 296 switch (*tcp++) { 297 298 case 0: /* - Interruptible, no prompt */ 299 prompt = 0; 300 setintr = 1; 301 nofile = 1; 302 break; 303 304 case 'b': /* -b Next arg is input file */ 305 batch = 1; 306 break; 307 308 case 'c': /* -c Command input from arg */ 309 if (argc == 1) 310 xexit(0); 311 argc--, tempv++; 312 arginp = SAVE(tempv[0]); 313 prompt = 0; 314 nofile = 1; 315 break; 316 317 case 'e': /* -e Exit on any error */ 318 exiterr = 1; 319 break; 320 321 case 'f': /* -f Fast start */ 322 fast = 1; 323 break; 324 325 case 'i': /* -i Interactive, even if !intty */ 326 intact = 1; 327 nofile = 1; 328 break; 329 330 case 'm': /* -m read .cshrc (from su) */ 331 mflag = 1; 332 break; 333 334 case 'n': /* -n Don't execute */ 335 noexec = 1; 336 break; 337 338 case 'q': /* -q (Undoc'd) ... die on quit */ 339 quitit = 1; 340 break; 341 342 case 's': /* -s Read from std input */ 343 nofile = 1; 344 break; 345 346 case 't': /* -t Read one line from input */ 347 onelflg = 2; 348 prompt = 0; 349 nofile = 1; 350 break; 351 352 case 'v': /* -v Echo hist expanded input */ 353 nverbose = 1; /* ... later */ 354 break; 355 356 case 'x': /* -x Echo just before execution */ 357 nexececho = 1; /* ... later */ 358 break; 359 360 case 'V': /* -V Echo hist expanded input */ 361 setNS(STRverbose); /* NOW! */ 362 break; 363 364 case 'X': /* -X Echo just before execution */ 365 setNS(STRecho); /* NOW! */ 366 break; 367 368 } while (*tcp); 369 tempv++, argc--; 370 } 371 372 if (quitit) /* With all due haste, for debugging */ 373 (void) signal(SIGQUIT, SIG_DFL); 374 375 /* 376 * Unless prevented by -, -c, -i, -s, or -t, if there are remaining 377 * arguments the first of them is the name of a shell file from which to 378 * read commands. 379 */ 380 if (nofile == 0 && argc > 0) { 381 nofile = open(tempv[0], O_RDONLY); 382 if (nofile < 0) { 383 child = 1; /* So this doesn't return */ 384 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 385 } 386 ffile = SAVE(tempv[0]); 387 /* 388 * Replace FSHIN. Handle /dev/std{in,out,err} specially 389 * since once they are closed we cannot open them again. 390 * In that case we use our own saved descriptors 391 */ 392 if ((SHIN = dmove(nofile, FSHIN)) < 0) 393 switch(nofile) { 394 case 0: 395 SHIN = FSHIN; 396 break; 397 case 1: 398 SHIN = FSHOUT; 399 break; 400 case 2: 401 SHIN = FSHERR; 402 break; 403 default: 404 stderror(ERR_SYSTEM, tempv[0], strerror(errno)); 405 break; 406 } 407 (void) ioctl(SHIN, FIOCLEX, NULL); 408 prompt = 0; 409 /* argc not used any more */ tempv++; 410 } 411 412 intty = isatty(SHIN); 413 intty |= intact; 414 if (intty || (intact && isatty(SHOUT))) { 415 if (!batch && (uid != euid || gid != egid)) { 416 errno = EACCES; 417 child = 1; /* So this doesn't return */ 418 stderror(ERR_SYSTEM, "csh", strerror(errno)); 419 } 420 } 421 /* 422 * Decide whether we should play with signals or not. If we are explicitly 423 * told (via -i, or -) or we are a login shell (arg0 starts with -) or the 424 * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 425 * Note that in only the login shell is it likely that parent may have set 426 * signals to be ignored 427 */ 428 if (loginsh || intact || (intty && isatty(SHOUT))) 429 setintr = 1; 430 settell(); 431 /* 432 * Save the remaining arguments in argv. 433 */ 434 setq(STRargv, blk2short(tempv), &shvhed); 435 436 /* 437 * Set up the prompt. 438 */ 439 if (prompt) { 440 set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); 441 /* that's a meta-questionmark */ 442 set(STRprompt2, Strsave(STRmquestion)); 443 } 444 445 /* 446 * If we are an interactive shell, then start fiddling with the signals; 447 * this is a tricky game. 448 */ 449 shpgrp = getpgrp(); 450 opgrp = tpgrp = -1; 451 if (setintr) { 452 **argv = '-'; 453 if (!quitit) /* Wary! */ 454 (void) signal(SIGQUIT, SIG_IGN); 455 (void) signal(SIGINT, pintr); 456 sigemptyset(&sigset); 457 sigaddset(&sigset, SIGINT); 458 sigprocmask(SIG_BLOCK, &sigset, NULL); 459 (void) signal(SIGTERM, SIG_IGN); 460 if (quitit == 0 && arginp == 0) { 461 (void) signal(SIGTSTP, SIG_IGN); 462 (void) signal(SIGTTIN, SIG_IGN); 463 (void) signal(SIGTTOU, SIG_IGN); 464 /* 465 * Wait till in foreground, in case someone stupidly runs csh & 466 * dont want to try to grab away the tty. 467 */ 468 if (isatty(FSHERR)) 469 f = FSHERR; 470 else if (isatty(FSHOUT)) 471 f = FSHOUT; 472 else if (isatty(OLDSTD)) 473 f = OLDSTD; 474 else 475 f = -1; 476 retry: 477 if ((tpgrp = tcgetpgrp(f)) != -1) { 478 if (tpgrp != shpgrp) { 479 sig_t old = signal(SIGTTIN, SIG_DFL); 480 (void) kill(0, SIGTTIN); 481 (void) signal(SIGTTIN, old); 482 goto retry; 483 } 484 opgrp = shpgrp; 485 shpgrp = getpid(); 486 tpgrp = shpgrp; 487 /* 488 * Setpgid will fail if we are a session leader and 489 * mypid == mypgrp (POSIX 4.3.3) 490 */ 491 if (opgrp != shpgrp) 492 if (setpgid(0, shpgrp) == -1) 493 goto notty; 494 /* 495 * We do that after we set our process group, to make sure 496 * that the process group belongs to a process in the same 497 * session as the tty (our process and our group) (POSIX 7.2.4) 498 */ 499 if (tcsetpgrp(f, shpgrp) == -1) 500 goto notty; 501 (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); 502 } 503 if (tpgrp == -1) { 504notty: 505 (void) fprintf(csherr, "Warning: no access to tty (%s).\n", 506 strerror(errno)); 507 (void) fprintf(csherr, "Thus no job control in this shell.\n"); 508 } 509 } 510 } 511 if ((setintr == 0) && (parintr == SIG_DFL)) 512 setintr = 1; 513 (void) signal(SIGCHLD, pchild); /* while signals not ready */ 514 515 /* 516 * Set an exit here in case of an interrupt or error reading the shell 517 * start-up scripts. 518 */ 519 reenter = setexit(); /* PWP */ 520 exitset++; 521 haderr = 0; /* In case second time through */ 522 if (!fast && reenter == 0) { 523 /* Will have value(STRhome) here because set fast if don't */ 524 { 525 int osetintr = setintr; 526 sig_t oparintr = parintr; 527 sigset_t osigset; 528 529 sigemptyset(&sigset); 530 sigaddset(&sigset, SIGINT); 531 sigprocmask(SIG_BLOCK, &sigset, &osigset); 532 533 setintr = 0; 534 parintr = SIG_IGN; /* Disable onintr */ 535#ifdef _PATH_DOTCSHRC 536 (void) srcfile(_PATH_DOTCSHRC, 0, 0); 537#endif 538 if (!fast && !arginp && !onelflg) 539 dohash(NULL, NULL); 540#ifdef _PATH_DOTLOGIN 541 if (loginsh) 542 (void) srcfile(_PATH_DOTLOGIN, 0, 0); 543#endif 544 sigprocmask(SIG_SETMASK, &osigset, NULL); 545 setintr = osetintr; 546 parintr = oparintr; 547 } 548 (void) srccat(value(STRhome), STRsldotcshrc); 549 550 if (!fast && !arginp && !onelflg && !havhash) 551 dohash(NULL, NULL); 552 /* 553 * Source history before .login so that it is available in .login 554 */ 555 if ((cp = value(STRhistfile)) != STRNULL) 556 loadhist[2] = cp; 557 dosource(loadhist, NULL); 558 if (loginsh) 559 (void) srccat(value(STRhome), STRsldotlogin); 560 } 561 562 /* 563 * Now are ready for the -v and -x flags 564 */ 565 if (nverbose) 566 setNS(STRverbose); 567 if (nexececho) 568 setNS(STRecho); 569 570 /* 571 * All the rest of the world is inside this call. The argument to process 572 * indicates whether it should catch "error unwinds". Thus if we are a 573 * interactive shell our call here will never return by being blown past on 574 * an error. 575 */ 576 process(setintr); 577 578 /* 579 * Mop-up. 580 */ 581 if (intty) { 582 if (loginsh) { 583 (void) fprintf(cshout, "logout\n"); 584 (void) close(SHIN); 585 child = 1; 586 goodbye(); 587 } 588 else { 589 (void) fprintf(cshout, "exit\n"); 590 } 591 } 592 rechist(); 593 exitstat(); 594 return (0); 595} 596 597void 598untty() 599{ 600 if (tpgrp > 0) { 601 (void) setpgid(0, opgrp); 602 (void) tcsetpgrp(FSHTTY, opgrp); 603 } 604} 605 606void 607importpath(cp) 608 Char *cp; 609{ 610 register int i = 0; 611 register Char *dp; 612 register Char **pv; 613 int c; 614 615 for (dp = cp; *dp; dp++) 616 if (*dp == ':') 617 i++; 618 /* 619 * i+2 where i is the number of colons in the path. There are i+1 620 * directories in the path plus we need room for a zero terminator. 621 */ 622 pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); 623 dp = cp; 624 i = 0; 625 if (*dp) 626 for (;;) { 627 if ((c = *dp) == ':' || c == 0) { 628 *dp = 0; 629 pv[i++] = Strsave(*cp ? cp : STRdot); 630 if (c) { 631 cp = dp + 1; 632 *dp = ':'; 633 } 634 else 635 break; 636 } 637 dp++; 638 } 639 pv[i] = 0; 640 setq(STRpath, pv, &shvhed); 641} 642 643/* 644 * Source to the file which is the catenation of the argument names. 645 */ 646static int 647srccat(cp, dp) 648 Char *cp, *dp; 649{ 650 register Char *ep = Strspl(cp, dp); 651 char *ptr = short2str(ep); 652 653 xfree((ptr_t) ep); 654 return srcfile(ptr, mflag ? 0 : 1, 0); 655} 656 657/* 658 * Source to a file putting the file descriptor in a safe place (> 2). 659 */ 660static int 661srcfile(f, onlyown, flag) 662 char *f; 663 bool onlyown, flag; 664{ 665 register int unit; 666 667 if ((unit = open(f, O_RDONLY)) == -1) 668 return 0; 669 unit = dmove(unit, -1); 670 671 (void) ioctl(unit, FIOCLEX, NULL); 672 srcunit(unit, onlyown, flag); 673 return 1; 674} 675 676/* 677 * Source to a unit. If onlyown it must be our file or our group or 678 * we don't chance it. This occurs on ".cshrc"s and the like. 679 */ 680int insource; 681static void 682srcunit(unit, onlyown, hflg) 683 register int unit; 684 bool onlyown, hflg; 685{ 686 /* We have to push down a lot of state here */ 687 /* All this could go into a structure */ 688 int oSHIN = -1, oldintty = intty, oinsource = insource; 689 struct whyle *oldwhyl = whyles; 690 Char *ogointr = gointr, *oarginp = arginp; 691 Char *oevalp = evalp, **oevalvec = evalvec; 692 int oonelflg = onelflg; 693 bool oenterhist = enterhist; 694 char OHIST = HIST; 695 bool otell = cantell; 696 697 struct Bin saveB; 698 sigset_t sigset, osigset; 699 jmp_buf oldexit; 700 701 /* The (few) real local variables */ 702 int my_reenter; 703 704 if (unit < 0) 705 return; 706 if (didfds) 707 donefds(); 708 if (onlyown) { 709 struct stat stb; 710 711 if (fstat(unit, &stb) < 0) { 712 (void) close(unit); 713 return; 714 } 715 } 716 717 /* 718 * There is a critical section here while we are pushing down the input 719 * stream since we have stuff in different structures. If we weren't 720 * careful an interrupt could corrupt SHIN's Bin structure and kill the 721 * shell. 722 * 723 * We could avoid the critical region by grouping all the stuff in a single 724 * structure and pointing at it to move it all at once. This is less 725 * efficient globally on many variable references however. 726 */ 727 insource = 1; 728 getexit(oldexit); 729 730 if (setintr) { 731 sigemptyset(&sigset); 732 sigaddset(&sigset, SIGINT); 733 sigprocmask(SIG_BLOCK, &sigset, &osigset); 734 } 735 /* Setup the new values of the state stuff saved above */ 736 memcpy(&saveB, &B, sizeof(B)); 737 fbuf = NULL; 738 fseekp = feobp = fblocks = 0; 739 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 740 intty = isatty(SHIN), whyles = 0, gointr = 0; 741 evalvec = 0; 742 evalp = 0; 743 enterhist = hflg; 744 if (enterhist) 745 HIST = '\0'; 746 747 /* 748 * Now if we are allowing commands to be interrupted, we let ourselves be 749 * interrupted. 750 */ 751 if (setintr) 752 sigprocmask(SIG_SETMASK, &osigset, NULL); 753 settell(); 754 755 if ((my_reenter = setexit()) == 0) 756 process(0); /* 0 -> blow away on errors */ 757 758 if (setintr) 759 sigprocmask(SIG_SETMASK, &osigset, NULL); 760 if (oSHIN >= 0) { 761 register int i; 762 763 /* We made it to the new state... free up its storage */ 764 /* This code could get run twice but xfree doesn't care */ 765 for (i = 0; i < fblocks; i++) 766 xfree((ptr_t) fbuf[i]); 767 xfree((ptr_t) fbuf); 768 769 /* Reset input arena */ 770 memcpy(&B, &saveB, sizeof(B)); 771 772 (void) close(SHIN), SHIN = oSHIN; 773 arginp = oarginp, onelflg = oonelflg; 774 evalp = oevalp, evalvec = oevalvec; 775 intty = oldintty, whyles = oldwhyl, gointr = ogointr; 776 if (enterhist) 777 HIST = OHIST; 778 enterhist = oenterhist; 779 cantell = otell; 780 } 781 782 resexit(oldexit); 783 /* 784 * If process reset() (effectively an unwind) then we must also unwind. 785 */ 786 if (my_reenter) 787 stderror(ERR_SILENT); 788 insource = oinsource; 789} 790 791void 792rechist() 793{ 794 Char buf[BUFSIZ], hbuf[BUFSIZ], *hfile; 795 int fp, ftmp, oldidfds; 796 struct varent *shist; 797 798 if (!fast) { 799 /* 800 * If $savehist is just set, we use the value of $history 801 * else we use the value in $savehist 802 */ 803 if ((shist = adrof(STRsavehist)) != NULL) { 804 if (shist->vec[0][0] != '\0') 805 (void) Strlcpy(hbuf, shist->vec[0], sizeof hbuf/sizeof(Char)); 806 else if ((shist = adrof(STRhistory)) && shist->vec[0][0] != '\0') 807 (void) Strlcpy(hbuf, shist->vec[0], sizeof hbuf/sizeof(Char)); 808 else 809 return; 810 } 811 else 812 return; 813 814 if ((hfile = value(STRhistfile)) == STRNULL) { 815 Strlcpy(buf, value(STRhome), sizeof buf/sizeof(Char)); 816 hfile = buf; 817 (void) Strlcat(buf, STRsldthist, sizeof buf/sizeof(Char)); 818 } 819 820 if ((fp = open(short2str(hfile), O_WRONLY | O_CREAT | O_TRUNC, 821 0600)) == -1) 822 return; 823 824 oldidfds = didfds; 825 didfds = 0; 826 ftmp = SHOUT; 827 SHOUT = fp; 828 dumphist[2] = hbuf; 829 dohist(dumphist, NULL); 830 SHOUT = ftmp; 831 (void) close(fp); 832 didfds = oldidfds; 833 } 834} 835 836void 837goodbye() 838{ 839 rechist(); 840 841 if (loginsh) { 842 (void) signal(SIGQUIT, SIG_IGN); 843 (void) signal(SIGINT, SIG_IGN); 844 (void) signal(SIGTERM, SIG_IGN); 845 setintr = 0; /* No interrupts after "logout" */ 846 if (!(adrof(STRlogout))) 847 set(STRlogout, STRnormal); 848#ifdef _PATH_DOTLOGOUT 849 (void) srcfile(_PATH_DOTLOGOUT, 0, 0); 850#endif 851 if (adrof(STRhome)) 852 (void) srccat(value(STRhome), STRsldtlogout); 853 } 854 exitstat(); 855} 856 857void 858exitstat() 859{ 860 Char *s; 861#ifdef PROF 862 monitor(0); 863#endif 864 /* 865 * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit 866 * directly because we poke child here. Otherwise we might continue 867 * unwarrantedly (sic). 868 */ 869 child = 1; 870 s = value(STRstatus); 871 xexit(s ? getn(s) : 0); 872} 873 874/* 875 * in the event of a HUP we want to save the history 876 */ 877static void 878phup(sig) 879int sig; 880{ 881 /* XXX sigh, everything after this is a signal race */ 882 883 rechist(); 884 885 /* 886 * We kill the last foreground process group. It then becomes 887 * responsible to propagate the SIGHUP to its progeny. 888 */ 889 { 890 struct process *pp, *np; 891 892 for (pp = proclist.p_next; pp; pp = pp->p_next) { 893 np = pp; 894 /* 895 * Find if this job is in the foreground. It could be that 896 * the process leader has exited and the foreground flag 897 * is cleared for it. 898 */ 899 do 900 /* 901 * If a process is in the foreground; we try to kill 902 * it's process group. If we succeed, then the 903 * whole job is gone. Otherwise we keep going... 904 * But avoid sending HUP to the shell again. 905 */ 906 if ((np->p_flags & PFOREGND) != 0 && np->p_jobid != shpgrp && 907 kill(-np->p_jobid, SIGHUP) != -1) { 908 /* In case the job was suspended... */ 909 (void) kill(-np->p_jobid, SIGCONT); 910 break; 911 } 912 while ((np = np->p_friends) != pp); 913 } 914 } 915 xexit(sig); 916} 917 918Char *jobargv[2] = {STRjobs, 0}; 919 920/* 921 * Catch an interrupt, e.g. during lexical input. 922 * If we are an interactive shell, we reset the interrupt catch 923 * immediately. In any case we drain the shell output, 924 * and finally go through the normal error mechanism, which 925 * gets a chance to make the shell go away. 926 */ 927/* ARGSUSED */ 928void 929pintr(notused) 930 int notused; 931{ 932 int save_errno = errno; 933 934 pintr1(1); 935 errno = save_errno; 936} 937 938void 939pintr1(wantnl) 940 bool wantnl; 941{ 942 Char **v; 943 sigset_t sigset, osigset; 944 945 sigemptyset(&sigset); 946 sigprocmask(SIG_BLOCK, &sigset, &osigset); 947 if (setintr) { 948 sigset = osigset; 949 sigdelset(&sigset, SIGINT); 950 sigprocmask(SIG_SETMASK, &sigset, NULL); 951 if (pjobs) { 952 pjobs = 0; 953 (void) fprintf(cshout, "\n"); 954 dojobs(jobargv, NULL); 955 stderror(ERR_NAME | ERR_INTR); 956 } 957 } 958 sigdelset(&osigset, SIGCHLD); 959 sigprocmask(SIG_SETMASK, &osigset, NULL); 960 (void) fpurge(cshout); 961 (void) endpwent(); 962 963 /* 964 * If we have an active "onintr" then we search for the label. Note that if 965 * one does "onintr -" then we shan't be interruptible so we needn't worry 966 * about that here. 967 */ 968 if (gointr) { 969 gotolab(gointr); 970 timflg = 0; 971 if ((v = pargv) != NULL) 972 pargv = 0, blkfree(v); 973 if ((v = gargv) != NULL) 974 gargv = 0, blkfree(v); 975 reset(); 976 } 977 else if (intty && wantnl) { 978 (void) fputc('\r', cshout); 979 (void) fputc('\n', cshout); 980 } 981 stderror(ERR_SILENT); 982} 983 984/* 985 * Process is the main driving routine for the shell. 986 * It runs all command processing, except for those within { ... } 987 * in expressions (which is run by a routine evalav in sh.exp.c which 988 * is a stripped down process), and `...` evaluation which is run 989 * also by a subset of this code in sh.glob.c in the routine backeval. 990 * 991 * The code here is a little strange because part of it is interruptible 992 * and hence freeing of structures appears to occur when none is necessary 993 * if this is ignored. 994 * 995 * Note that if catch is not set then we will unwind on any error. 996 * If an end-of-file occurs, we return. 997 */ 998static struct command *savet = NULL; 999void 1000process(catch) 1001 bool catch; 1002{ 1003 jmp_buf osetexit; 1004 struct command *t = savet; 1005 sigset_t sigset; 1006 1007 savet = NULL; 1008 getexit(osetexit); 1009 for (;;) { 1010 pendjob(); 1011 paraml.next = paraml.prev = ¶ml; 1012 paraml.word = STRNULL; 1013 (void) setexit(); 1014 justpr = enterhist; /* execute if not entering history */ 1015 1016 /* 1017 * Interruptible during interactive reads 1018 */ 1019 if (setintr) { 1020 sigemptyset(&sigset); 1021 sigaddset(&sigset, SIGINT); 1022 sigprocmask(SIG_UNBLOCK, &sigset, NULL); 1023 } 1024 1025 /* 1026 * For the sake of reset() 1027 */ 1028 freelex(¶ml); 1029 if (savet) 1030 freesyn(savet), savet = NULL; 1031 1032 if (haderr) { 1033 if (!catch) { 1034 /* unwind */ 1035 doneinp = 0; 1036 resexit(osetexit); 1037 savet = t; 1038 reset(); 1039 } 1040 haderr = 0; 1041 /* 1042 * Every error is eventually caught here or the shell dies. It is 1043 * at this point that we clean up any left-over open files, by 1044 * closing all but a fixed number of pre-defined files. Thus 1045 * routines don't have to worry about leaving files open due to 1046 * deeper errors... they will get closed here. 1047 */ 1048 closem(); 1049 continue; 1050 } 1051 if (doneinp) { 1052 doneinp = 0; 1053 break; 1054 } 1055 if (chkstop) 1056 chkstop--; 1057 if (neednote) 1058 pnote(); 1059 if (intty && prompt && evalvec == 0) { 1060 mailchk(); 1061 /* 1062 * If we are at the end of the input buffer then we are going to 1063 * read fresh stuff. Otherwise, we are rereading input and don't 1064 * need or want to prompt. 1065 */ 1066 if (aret == F_SEEK && fseekp == feobp) 1067 printprompt(); 1068 (void) fflush(cshout); 1069 } 1070 if (seterr) { 1071 xfree((ptr_t) seterr); 1072 seterr = NULL; 1073 } 1074 1075 /* 1076 * Echo not only on VERBOSE, but also with history expansion. If there 1077 * is a lexical error then we forego history echo. 1078 */ 1079 if ((lex(¶ml) && !seterr && intty) || adrof(STRverbose)) { 1080 prlex(csherr, ¶ml); 1081 } 1082 1083 /* 1084 * The parser may lose space if interrupted. 1085 */ 1086 if (setintr) 1087 sigprocmask(SIG_BLOCK, &sigset, NULL); 1088 1089 /* 1090 * Save input text on the history list if reading in old history, or it 1091 * is from the terminal at the top level and not in a loop. 1092 * 1093 * PWP: entry of items in the history list while in a while loop is done 1094 * elsewhere... 1095 */ 1096 if (enterhist || (catch && intty && !whyles)) 1097 savehist(¶ml); 1098 1099 /* 1100 * Print lexical error messages, except when sourcing history lists. 1101 */ 1102 if (!enterhist && seterr) 1103 stderror(ERR_OLD); 1104 1105 /* 1106 * If had a history command :p modifier then this is as far as we 1107 * should go 1108 */ 1109 if (justpr) 1110 reset(); 1111 1112 alias(¶ml); 1113 1114 /* 1115 * Parse the words of the input into a parse tree. 1116 */ 1117 savet = syntax(paraml.next, ¶ml, 0); 1118 if (seterr) 1119 stderror(ERR_OLD); 1120 1121 execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); 1122 1123 /* 1124 * Made it! 1125 */ 1126 freelex(¶ml); 1127 freesyn((struct command *) savet), savet = NULL; 1128 } 1129 resexit(osetexit); 1130 savet = t; 1131} 1132 1133void 1134/*ARGSUSED*/ 1135dosource(v, t) 1136 Char **v; 1137 struct command *t; 1138 1139{ 1140 register Char *f; 1141 bool hflg = 0; 1142 Char buf[BUFSIZ]; 1143 char sbuf[BUFSIZ]; 1144 1145 v++; 1146 if (*v && eq(*v, STRmh)) { 1147 if (*++v == NULL) 1148 stderror(ERR_NAME | ERR_HFLAG); 1149 hflg++; 1150 } 1151 (void) Strlcpy(buf, *v, sizeof buf/sizeof(Char)); 1152 f = globone(buf, G_ERROR); 1153 (void) strlcpy(sbuf, short2str(f), sizeof sbuf); 1154 xfree((ptr_t) f); 1155 if (!srcfile(sbuf, 0, hflg) && !hflg) 1156 stderror(ERR_SYSTEM, sbuf, strerror(errno)); 1157} 1158 1159/* 1160 * Check for mail. 1161 * If we are a login shell, then we don't want to tell 1162 * about any mail file unless its been modified 1163 * after the time we started. 1164 * This prevents us from telling the user things he already 1165 * knows, since the login program insists on saying 1166 * "You have mail." 1167 */ 1168static void 1169mailchk() 1170{ 1171 register struct varent *v; 1172 register Char **vp; 1173 time_t t; 1174 int intvl, cnt; 1175 struct stat stb; 1176 bool new; 1177 1178 v = adrof(STRmail); 1179 if (v == 0) 1180 return; 1181 (void) time(&t); 1182 vp = v->vec; 1183 cnt = blklen(vp); 1184 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 1185 if (intvl < 1) 1186 intvl = 1; 1187 if (chktim + intvl > t) 1188 return; 1189 for (; *vp; vp++) { 1190 if (stat(short2str(*vp), &stb) < 0) 1191 continue; 1192 new = stb.st_mtime > time0.tv_sec; 1193 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || 1194 (stb.st_atime < chktim && stb.st_mtime < chktim) || 1195 (loginsh && !new)) 1196 continue; 1197 if (cnt == 1) 1198 (void) fprintf(cshout, "You have %smail.\n", new ? "new " : ""); 1199 else 1200 (void) fprintf(cshout, "%s in %s.\n", new ? "New mail" : "Mail", 1201 vis_str(*vp)); 1202 } 1203 chktim = t; 1204} 1205 1206/* 1207 * Extract a home directory from the password file 1208 * The argument points to a buffer where the name of the 1209 * user whose home directory is sought is currently. 1210 * We write the home directory of the user back there. 1211 */ 1212int 1213gethdir(home, len) 1214 Char *home; 1215 int len; 1216{ 1217 Char *h; 1218 struct passwd *pw; 1219 1220 /* 1221 * Is it us? 1222 */ 1223 if (*home == '\0') { 1224 if ((h = value(STRhome)) != NULL) { 1225 if (Strlcpy(home, h, len) >= len) 1226 return 1; 1227 return 0; 1228 } 1229 else 1230 return 1; 1231 } 1232 1233 if ((pw = getpwnam(short2str(home))) != NULL) { 1234 if (Strlcpy(home, str2short(pw->pw_dir), len) >= len) 1235 return 1; 1236 return 0; 1237 } 1238 else 1239 return 1; 1240} 1241 1242/* 1243 * When didfds is set, we do I/O from 0, 1, 2 otherwise from 15, 16, 17 1244 * We also check if the shell has already changed the descriptor to point to 1245 * 0, 1, 2 when didfds is set. 1246 */ 1247#define DESC(a) (*((int *) (a)) - (didfds && *((int *) a) >= FSHIN ? FSHIN : 0)) 1248 1249static int 1250readf(oreo, buf, siz) 1251 void *oreo; 1252 char *buf; 1253 int siz; 1254{ 1255 return read(DESC(oreo), buf, siz); 1256} 1257 1258 1259static int 1260writef(oreo, buf, siz) 1261 void *oreo; 1262 const char *buf; 1263 int siz; 1264{ 1265 return write(DESC(oreo), buf, siz); 1266} 1267 1268static fpos_t 1269seekf(oreo, off, whence) 1270 void *oreo; 1271 fpos_t off; 1272 int whence; 1273{ 1274 return lseek(DESC(oreo), off, whence); 1275} 1276 1277 1278static int 1279closef(oreo) 1280 void *oreo; 1281{ 1282 return close(DESC(oreo)); 1283} 1284 1285 1286/* 1287 * Print the visible version of a string. 1288 */ 1289int 1290vis_fputc(ch, fp) 1291 int ch; 1292 FILE *fp; 1293{ 1294 char uenc[5]; /* 4 + NUL */ 1295 1296 if (ch & QUOTE) 1297 return fputc(ch & TRIM, fp); 1298 /* 1299 * XXX: When we are in AsciiOnly we want all characters >= 0200 to 1300 * be encoded, but currently there is no way in vis to do that. 1301 */ 1302 (void) vis(uenc, ch & TRIM, VIS_NOSLASH, 0); 1303 return fputs(uenc, fp); 1304} 1305 1306/* 1307 * Move the initial descriptors to their eventual 1308 * resting places, closing all other units. 1309 */ 1310void 1311initdesc() 1312{ 1313 1314 didfds = 0; /* 0, 1, 2 aren't set up */ 1315 (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); 1316 (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); 1317 (void) ioctl(SHERR = dcopy(2, FSHERR), FIOCLEX, NULL); 1318 (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); 1319 closem(); 1320} 1321 1322 1323void 1324#ifdef PROF 1325done(i) 1326#else 1327xexit(i) 1328#endif 1329 int i; 1330{ 1331 untty(); 1332 _exit(i); 1333} 1334 1335static Char ** 1336defaultpath() 1337{ 1338 char *ptr; 1339 Char **blk, **blkp; 1340 struct stat stb; 1341 1342 blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); 1343 1344#define DIRAPPEND(a) \ 1345 if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \ 1346 *blkp++ = SAVE(ptr) 1347 1348 DIRAPPEND(_PATH_BIN); 1349 DIRAPPEND(_PATH_USRBIN); 1350 1351#undef DIRAPPEND 1352 1353#if 0 1354 if (euid != 0 && uid != 0) 1355 *blkp++ = Strsave(STRdot); 1356#endif 1357 1358 *blkp = NULL; 1359 return (blk); 1360} 1361 1362void 1363printprompt() 1364{ 1365 register Char *cp; 1366 1367 if (!whyles) { 1368 for (cp = value(STRprompt); *cp; cp++) 1369 if (*cp == HIST) 1370 (void) fprintf(cshout, "%d", eventno + 1); 1371 else { 1372 if (*cp == '\\' && cp[1] == HIST) 1373 cp++; 1374 (void) vis_fputc(*cp | QUOTE, cshout); 1375 } 1376 } 1377 else 1378 /* 1379 * Prompt for forward reading loop body content. 1380 */ 1381 (void) fprintf(cshout, "? "); 1382 (void) fflush(cshout); 1383} 1384