1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley Software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include <locale.h> 18 #include "sh.h" 19 /* #include <sys/ioctl.h> */ 20 #include <fcntl.h> 21 #include <sys/filio.h> 22 #include "sh.tconst.h" 23 #include <pwd.h> 24 #include <stdlib.h> 25 #include "sh_policy.h" /* for pfcsh */ 26 #ifdef TRACE 27 #include <stdio.h> 28 #endif 29 30 /* 31 * We use these csh(1) private versions of the select macros, (see select(3C)) 32 * so as not to be limited by the size of struct fd_set (ie 1024). 33 */ 34 #define CSH_FD_SET(n, p) ((*((p) + ((n)/NFDBITS))) |= (1 << ((n) % NFDBITS))) 35 #define CSH_FD_CLR(n, p) ((*((p) + ((n)/NFDBITS))) &= ~(1 << ((n) % NFDBITS))) 36 #define CSH_FD_ISSET(n, p) ((*((p) + ((n)/NFDBITS))) & (1 << ((n) % NFDBITS))) 37 #define CSH_FD_ZERO(p, n) memset((void *)(p), 0, (n)) 38 39 tchar *pathlist[] = { S_usrbin /* "/usr/bin" */, S_DOT /* "." */, 0 }; 40 tchar *dumphist[] = { S_history /* "history" */, S_h /* "-h" */, 0, 0 }; 41 tchar *loadhist[] = { S_source /* "source" */, S_h /* "-h" */, S_NDOThistory /* "~/.history" */, 0 }; 42 tchar HIST = '!'; 43 tchar HISTSUB = '^'; 44 int nofile; 45 bool reenter; 46 bool nverbose; 47 bool nexececho; 48 bool quitit; 49 bool fast; 50 bool batch; 51 bool prompt = 1; 52 bool enterhist = 0; 53 54 extern gid_t getegid(), getgid(); 55 extern uid_t geteuid(), getuid(); 56 extern tchar **strblktotsblk(/* char **, int */); 57 58 void importpath(tchar *); 59 void srccat(tchar *, tchar *); 60 void srccat_inlogin(tchar *, tchar *); 61 void srcunit(int, bool, bool); 62 void rechist(void); 63 void goodbye(void); 64 void pintr1(bool); 65 void process(bool); 66 void dosource(tchar **); 67 void mailchk(void); 68 void printprompt(void); 69 void sigwaiting(void); 70 void siglwp(void); 71 void initdesc(int, char *[]); 72 void initdesc_x(int, char *[], int); 73 void closem(void); 74 void unsetfd(int); 75 void secpolicy_print(int, const char *); 76 void phup(void); 77 78 #ifdef TRACE 79 FILE *trace; 80 /* 81 * Trace routines 82 */ 83 #define TRACEFILE "/tmp/trace.XXXXXX" 84 85 /* 86 * Initialize trace file. 87 * Called from main. 88 */ 89 void 90 trace_init(void) 91 { 92 char name[128]; 93 char *p; 94 95 strcpy(name, TRACEFILE); 96 p = mktemp(name); 97 trace = fopen(p, "w"); 98 } 99 100 /* 101 * write message to trace file 102 */ 103 /*VARARGS1*/ 104 void 105 tprintf(fmt, a, b, c, d, e, f, g, h, i, j) 106 char *fmt; 107 { 108 if (trace) { 109 fprintf(trace, fmt, a, b, c, d, e, f, g, h, i, j); 110 fflush(trace); 111 } 112 } 113 #endif 114 115 int 116 main(int c, char **av) 117 { 118 tchar **v, *cp, *p, *q, *r; 119 int f; 120 struct sigvec osv; 121 struct sigaction sa; 122 tchar s_prompt[MAXHOSTNAMELEN+3]; 123 124 pfcshflag = 0; 125 126 /* 127 * set up the error exit, if there is an error before 128 * this is done, it will core dump, and we don't 129 * tolerate core dumps 130 */ 131 haderr = 0; 132 setexit(); 133 if (haderr) { 134 /* 135 * if were here, there was an error in the csh 136 * startup so just punt 137 */ 138 printf("csh startup error, csh exiting...\n"); 139 flush(); 140 exitstat(); 141 } 142 143 144 (void) setlocale(LC_ALL, ""); 145 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 146 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 147 #endif 148 (void) textdomain(TEXT_DOMAIN); 149 150 /* 151 * This is a profile shell if the simple name of argv[0] is 152 * pfcsh or -pfcsh 153 */ 154 p = strtots(NOSTR, "pfcsh"); 155 r = strtots(NOSTR, "-pfcsh"); 156 if ((p != NOSTR) && (r != NOSTR) && 157 ((q = strtots(NOSTR, *av)) != NOSTR)) { 158 if (c > 0 && (eq(p, simple(q)) || eq(r, simple(q)))) { 159 pfcshflag = 1; 160 } 161 xfree(q); 162 } 163 164 if (p != NOSTR) 165 xfree(p); 166 if (r != NOSTR) 167 xfree(r); 168 169 if (pfcshflag == 1) { 170 secpolicy_init(); 171 } 172 173 /* Copy arguments */ 174 v = strblktotsblk(av, c); 175 176 /* 177 * Initialize paraml list 178 */ 179 paraml.next = paraml.prev = ¶ml; 180 181 settimes(); /* Immed. estab. timing base */ 182 183 if (eq(v[0], S_aout /* "a.out" */)) /* A.out's are quittable */ 184 quitit = 1; 185 uid = getuid(); 186 loginsh = **v == '-'; 187 if (loginsh) 188 (void) time(&chktim); 189 190 /* 191 * Move the descriptors to safe places. 192 * The variable didfds is 0 while we have only FSH* to work with. 193 * When didfds is true, we have 0,1,2 and prefer to use these. 194 * 195 * Also, setup data for csh internal file descriptor book keeping. 196 */ 197 initdesc(c, av); 198 199 /* 200 * Initialize the shell variables. 201 * ARGV and PROMPT are initialized later. 202 * STATUS is also munged in several places. 203 * CHILD is munged when forking/waiting 204 */ 205 206 /* don't do globbing here, just set exact copies */ 207 setNS(S_noglob); 208 209 set(S_status /* "status" */, S_0 /* "0" */); 210 dinit(cp = getenvs_("HOME")); /* dinit thinks that HOME==cwd in a */ 211 /* login shell */ 212 if (cp == NOSTR) 213 fast++; /* No home -> can't read scripts */ 214 else { 215 if (strlen_(cp) >= BUFSIZ - 10) { 216 cp = NOSTR; 217 fast++; 218 printf("%s\n", gettext("Pathname too long")); 219 set(S_home /* "home" */, savestr(cp)); 220 local_setenv(S_HOME, savestr(cp)); 221 } 222 set(S_home /* "home" */, savestr(cp)); 223 } 224 /* 225 * Grab other useful things from the environment. 226 * Should we grab everything?? 227 */ 228 if ((cp = getenvs_("USER")) != NOSTR) 229 set(S_user /* "user" */, savestr(cp)); 230 else { 231 /* 232 * If USER is not defined, set it here. 233 */ 234 struct passwd *pw; 235 pw = getpwuid(getuid()); 236 237 if (pw != NULL) { 238 set(S_user, strtots((tchar *)0, pw->pw_name)); 239 local_setenv(S_USER, strtots((tchar *)0, pw->pw_name)); 240 } 241 else if (loginsh) { /* Give up setting USER variable. */ 242 printf("Warning: USER environment variable could not be set.\n"); 243 } 244 } 245 if ((cp = getenvs_("TERM")) != NOSTR) 246 set(S_term /* "term" */, savestr(cp)); 247 /* 248 * Re-initialize path if set in environment 249 */ 250 if ((cp = getenvs_("PATH")) == NOSTR) 251 set1(S_path /* "path" */, saveblk(pathlist), &shvhed); 252 else 253 importpath(cp); 254 set(S_shell /* "shell" */, S_SHELLPATH); 255 256 doldol = putn(getpid()); /* For $$ */ 257 258 /* restore globbing until the user says otherwise */ 259 unsetv(S_noglob); 260 261 /* 262 * Record the interrupt states from the parent process. 263 * If the parent is non-interruptible our hand must be forced 264 * or we (and our children) won't be either. 265 * Our children inherit termination from our parent. 266 * We catch it only if we are the login shell. 267 */ 268 /* parents interruptibility */ 269 (void) sigvec(SIGINT, (struct sigvec *)0, &osv); 270 parintr = osv.sv_handler; 271 /* parents terminability */ 272 (void) sigvec(SIGTERM, (struct sigvec *)0, &osv); 273 parterm = osv.sv_handler; 274 275 _signal(SIGLWP, siglwp); 276 _signal(SIGWAITING, sigwaiting); 277 if (loginsh) { 278 (void) signal(SIGHUP, phup); /* exit processing on HUP */ 279 (void) signal(SIGXCPU, phup); /* ...and on XCPU */ 280 (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ 281 } 282 283 /* 284 * Process the arguments. 285 * 286 * Note that processing of -v/-x is actually delayed till after 287 * script processing. 288 */ 289 c--, v++; 290 while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) { 291 do switch (*cp++) { 292 293 case 'b': /* -b Next arg is input file */ 294 batch++; 295 break; 296 297 case 'c': /* -c Command input from arg */ 298 if (c == 1) 299 exit(0); 300 c--, v++; 301 arginp = v[0]; 302 prompt = 0; 303 nofile++; 304 cflg++; 305 break; 306 307 case 'e': /* -e Exit on any error */ 308 exiterr++; 309 break; 310 311 case 'f': /* -f Fast start */ 312 fast++; 313 break; 314 315 case 'i': /* -i Interactive, even if !intty */ 316 intact++; 317 nofile++; 318 break; 319 320 case 'n': /* -n Don't execute */ 321 noexec++; 322 break; 323 324 case 'q': /* -q (Undoc'd) ... die on quit */ 325 quitit = 1; 326 break; 327 328 case 's': /* -s Read from std input */ 329 nofile++; 330 break; 331 332 case 't': /* -t Read one line from input */ 333 onelflg = 2; 334 prompt = 0; 335 nofile++; 336 break; 337 #ifdef TRACE 338 case 'T': /* -T trace switch on */ 339 trace_init(); 340 break; 341 #endif 342 343 case 'v': /* -v Echo hist expanded input */ 344 nverbose = 1; /* ... later */ 345 break; 346 347 case 'x': /* -x Echo just before execution */ 348 nexececho = 1; /* ... later */ 349 break; 350 351 case 'V': /* -V Echo hist expanded input */ 352 setNS(S_verbose /* "verbose" */); /* NOW! */ 353 break; 354 355 case 'X': /* -X Echo just before execution */ 356 setNS(S_echo /* "echo" */); /* NOW! */ 357 break; 358 359 } while (*cp); 360 v++, c--; 361 } 362 363 if (quitit) /* With all due haste, for debugging */ 364 (void) signal(SIGQUIT, SIG_DFL); 365 366 /* 367 * Unless prevented by -c, -i, -s, or -t, if there 368 * are remaining arguments the first of them is the name 369 * of a shell file from which to read commands. 370 */ 371 if (!batch && (uid != geteuid() || getgid() != getegid())) { 372 errno = EACCES; 373 child++; /* So this ... */ 374 Perror(S_csh /* "csh" */); /* ... doesn't return */ 375 } 376 377 if (nofile == 0 && c > 0) { 378 nofile = open_(v[0], 0); 379 if (nofile < 0) { 380 child++; /* So this ... */ 381 Perror(v[0]); /* ... doesn't return */ 382 } 383 file = v[0]; 384 SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ 385 (void) fcntl(SHIN, F_SETFD, 1); 386 prompt = 0; 387 c--, v++; 388 } 389 390 /* 391 * Consider input a tty if it really is or we are interactive. 392 */ 393 intty = intact || isatty(SHIN); 394 395 /* 396 * Decide whether we should play with signals or not. 397 * If we are explicitly told (via -i, or -) or we are a login 398 * shell (arg0 starts with -) or the input and output are both 399 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") 400 * Note that in only the login shell is it likely that parent 401 * may have set signals to be ignored 402 */ 403 if (loginsh || intact || intty && isatty(SHOUT)) 404 setintr = 1; 405 #ifdef TELL 406 settell(); 407 #endif 408 /* 409 * Save the remaining arguments in argv. 410 */ 411 setq(S_argv /* "argv" */, v, &shvhed); 412 413 /* 414 * Set up the prompt. 415 */ 416 if (prompt) { 417 gethostname_(s_prompt, MAXHOSTNAMELEN); 418 strcat_(s_prompt, uid == 0 ? S_SHARPSP /* "# " */ : S_PERSENTSP /* "% " */); 419 set(S_prompt /* "prompt" */, s_prompt); 420 } 421 422 /* 423 * If we are an interactive shell, then start fiddling 424 * with the signals; this is a tricky game. 425 */ 426 shpgrp = getpgid(0); 427 opgrp = tpgrp = -1; 428 if (setintr) { 429 **av = '-'; 430 if (!quitit) /* Wary! */ 431 (void) signal(SIGQUIT, SIG_IGN); 432 (void) signal(SIGINT, pintr); 433 (void) sigblock(sigmask(SIGINT)); 434 (void) signal(SIGTERM, SIG_IGN); 435 if (quitit == 0 && arginp == 0) { 436 (void) signal(SIGTSTP, SIG_IGN); 437 (void) signal(SIGTTIN, SIG_IGN); 438 (void) signal(SIGTTOU, SIG_IGN); 439 /* 440 * Wait till in foreground, in case someone 441 * stupidly runs 442 * csh & 443 * dont want to try to grab away the tty. 444 */ 445 if (isatty(FSHDIAG)) 446 f = FSHDIAG; 447 else if (isatty(FSHOUT)) 448 f = FSHOUT; 449 else if (isatty(OLDSTD)) 450 f = OLDSTD; 451 else 452 f = -1; 453 retry: 454 if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 && 455 tpgrp != -1) { 456 if (tpgrp != shpgrp) { 457 void (*old)() = (void (*)())signal(SIGTTIN, SIG_DFL); 458 (void) kill(0, SIGTTIN); 459 (void) signal(SIGTTIN, old); 460 goto retry; 461 } 462 opgrp = shpgrp; 463 shpgrp = getpid(); 464 tpgrp = shpgrp; 465 (void) setpgid(0, shpgrp); 466 (void) ioctl(f, TIOCSPGRP, (char *)&shpgrp); 467 (void) fcntl(dcopy(f, FSHTTY), F_SETFD, 1); 468 } else { 469 notty: 470 printf("Warning: no access to tty; thus no job control in this shell...\n"); 471 tpgrp = -1; 472 } 473 } 474 } 475 if (setintr == 0 && parintr == SIG_DFL) 476 setintr++; 477 478 /* 479 * Set SIGCHLD handler, making sure that reads restart after it runs. 480 */ 481 sigemptyset(&sa.sa_mask); 482 sa.sa_handler = pchild; 483 sa.sa_flags = SA_RESTART; 484 (void) sigaction(SIGCHLD, &sa, (struct sigaction *)NULL); 485 486 /* 487 * Set an exit here in case of an interrupt or error reading 488 * the shell start-up scripts. 489 */ 490 setexit(); 491 haderr = 0; /* In case second time through */ 492 if (!fast && reenter == 0) { 493 reenter++; 494 495 /* 496 * If this is a login csh, and /etc/.login exists, 497 * source /etc/.login first. 498 */ 499 if (loginsh) { 500 tchar tmp_etc[4+1]; /* strlen("/etc")+1 */ 501 tchar tmp_login[7+1]; /* strlen("/.login")+1 */ 502 503 strtots(tmp_etc, "/etc"); 504 strtots(tmp_login, "/.login"); 505 srccat_inlogin(tmp_etc, tmp_login); 506 } 507 508 /* Will have value("home") here because set fast if don't */ 509 srccat(value(S_home /* "home" */), S_SLADOTcshrc /* "/.cshrc" */); 510 511 /* Hash path */ 512 if (!fast && !arginp && !onelflg && !havhash) 513 dohash(xhash); 514 515 516 /* 517 * Reconstruct the history list now, so that it's 518 * available from within .login. 519 */ 520 dosource(loadhist); 521 if (loginsh) { 522 srccat_inlogin(value(S_home /* "home" */), S_SLADOTlogin /* "/.login" */); 523 } 524 525 /* 526 * To get cdpath hashing $cdpath must have a 527 * value, not $CDPATH. So if after reading 528 * the startup files ( .cshrc ), and 529 * user has specified a value for cdpath, then 530 * cache $cdpath paths. xhash2 is global array 531 * for $cdpath caching. 532 */ 533 if (!fast && !arginp && !onelflg && !havhash2) 534 dohash(xhash2); 535 } 536 537 /* 538 * Now are ready for the -v and -x flags 539 */ 540 if (nverbose) 541 setNS(S_verbose /* "verbose" */); 542 if (nexececho) 543 setNS(S_echo /* "echo" */); 544 545 /* 546 * All the rest of the world is inside this call. 547 * The argument to process indicates whether it should 548 * catch "error unwinds". Thus if we are a interactive shell 549 * our call here will never return by being blown past on an error. 550 */ 551 process(setintr); 552 553 /* 554 * Mop-up. 555 */ 556 if (loginsh) { 557 printf("logout\n"); 558 (void) close(SHIN); /* No need for unsetfd(). */ 559 child++; 560 goodbye(); 561 } 562 rechist(); 563 exitstat(); 564 } 565 566 void 567 untty(void) 568 { 569 570 if (tpgrp > 0) { 571 (void) setpgid(0, opgrp); 572 (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 573 } 574 } 575 576 void 577 importpath(tchar *cp) 578 { 579 int i = 0; 580 tchar *dp; 581 tchar **pv; 582 int c; 583 static tchar dot[2] = {'.', 0}; 584 585 for (dp = cp; *dp; dp++) 586 if (*dp == ':') 587 i++; 588 /* 589 * i+2 where i is the number of colons in the path. 590 * There are i+1 directories in the path plus we need 591 * room for a zero terminator. 592 */ 593 pv = (tchar **)xcalloc((unsigned)(i + 2), sizeof (tchar **)); 594 dp = cp; 595 i = 0; 596 if (*dp) 597 for (;;) { 598 if ((c = *dp) == ':' || c == 0) { 599 *dp = 0; 600 pv[i++] = savestr(*cp ? cp : dot); 601 if (c) { 602 cp = dp + 1; 603 *dp = ':'; 604 } else 605 break; 606 } 607 dp++; 608 } 609 pv[i] = 0; 610 set1(S_path /* "path" */, pv, &shvhed); 611 } 612 613 /* 614 * Source to the file which is the catenation of the argument names. 615 */ 616 void 617 srccat(tchar *cp, tchar *dp) 618 { 619 tchar *ep = strspl(cp, dp); 620 int unit = dmove(open_(ep, 0), -1); 621 622 (void) fcntl(unit, F_SETFD, 1); 623 xfree(ep); 624 #ifdef INGRES 625 srcunit(unit, 0, 0); 626 #else 627 srcunit(unit, 1, 0); 628 #endif 629 } 630 631 /* 632 * Source to the file which is the catenation of the argument names. 633 * This one does not check the ownership. 634 */ 635 void 636 srccat_inlogin(tchar *cp, tchar *dp) 637 { 638 tchar *ep = strspl(cp, dp); 639 int unit = dmove(open_(ep, 0), -1); 640 641 (void) fcntl(unit, F_SETFD, 1); 642 xfree(ep); 643 srcunit(unit, 0, 0); 644 } 645 646 /* 647 * Source to a unit. If onlyown it must be our file or our group or 648 * we don't chance it. This occurs on ".cshrc"s and the like. 649 */ 650 void 651 srcunit(int unit, bool onlyown, bool hflg) 652 { 653 /* We have to push down a lot of state here */ 654 /* All this could go into a structure */ 655 int oSHIN = -1, oldintty = intty; 656 struct whyle *oldwhyl = whyles; 657 tchar *ogointr = gointr, *oarginp = arginp; 658 tchar *oevalp = evalp, **oevalvec = evalvec; 659 int oonelflg = onelflg; 660 bool oenterhist = enterhist; 661 tchar OHIST = HIST; 662 #ifdef TELL 663 bool otell = cantell; 664 #endif 665 struct Bin saveB; 666 667 /* The (few) real local variables */ 668 jmp_buf oldexit; 669 int reenter, omask; 670 671 if (unit < 0) 672 return; 673 if (didfds) 674 donefds(); 675 if (onlyown) { 676 struct stat stb; 677 678 if (fstat(unit, &stb) < 0 || 679 (stb.st_uid != uid && stb.st_gid != getgid())) { 680 (void) close(unit); 681 unsetfd(unit); 682 return; 683 } 684 } 685 686 /* 687 * There is a critical section here while we are pushing down the 688 * input stream since we have stuff in different structures. 689 * If we weren't careful an interrupt could corrupt SHIN's Bin 690 * structure and kill the shell. 691 * 692 * We could avoid the critical region by grouping all the stuff 693 * in a single structure and pointing at it to move it all at 694 * once. This is less efficient globally on many variable references 695 * however. 696 */ 697 getexit(oldexit); 698 reenter = 0; 699 if (setintr) 700 omask = sigblock(sigmask(SIGINT)); 701 setexit(); 702 reenter++; 703 if (reenter == 1) { 704 /* Setup the new values of the state stuff saved above */ 705 copy((char *)&saveB, (char *)&B, sizeof saveB); 706 fbuf = (tchar **) 0; 707 fseekp = feobp = fblocks = 0; 708 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 709 intty = isatty(SHIN), whyles = 0, gointr = 0; 710 evalvec = 0; evalp = 0; 711 enterhist = hflg; 712 if (enterhist) 713 HIST = '\0'; 714 /* 715 * Now if we are allowing commands to be interrupted, 716 * we let ourselves be interrupted. 717 */ 718 if (setintr) 719 (void) sigsetmask(omask); 720 #ifdef TELL 721 settell(); 722 #endif 723 process(0); /* 0 -> blow away on errors */ 724 } 725 if (setintr) 726 (void) sigsetmask(omask); 727 if (oSHIN >= 0) { 728 int i; 729 730 /* We made it to the new state... free up its storage */ 731 /* This code could get run twice but xfree doesn't care */ 732 for (i = 0; i < fblocks; i++) 733 xfree(fbuf[i]); 734 xfree((char *)fbuf); 735 736 /* Reset input arena */ 737 copy((char *)&B, (char *)&saveB, sizeof B); 738 739 (void) close(SHIN), SHIN = oSHIN; 740 unsetfd(SHIN); 741 arginp = oarginp, onelflg = oonelflg; 742 evalp = oevalp, evalvec = oevalvec; 743 intty = oldintty, whyles = oldwhyl, gointr = ogointr; 744 if (enterhist) 745 HIST = OHIST; 746 enterhist = oenterhist; 747 #ifdef TELL 748 cantell = otell; 749 #endif 750 } 751 752 resexit(oldexit); 753 /* 754 * If process reset() (effectively an unwind) then 755 * we must also unwind. 756 */ 757 if (reenter >= 2) 758 error(NULL); 759 } 760 761 void 762 rechist(void) 763 { 764 tchar buf[BUFSIZ]; 765 int fp, ftmp, oldidfds; 766 767 if (!fast) { 768 if (value(S_savehist /* "savehist" */)[0] == '\0') 769 return; 770 (void) strcpy_(buf, value(S_home /* "home" */)); 771 (void) strcat_(buf, S_SLADOThistory /* "/.history" */); 772 fp = creat_(buf, 0666); 773 if (fp == -1) 774 return; 775 oldidfds = didfds; 776 didfds = 0; 777 ftmp = SHOUT; 778 SHOUT = fp; 779 (void) strcpy_(buf, value(S_savehist /* "savehist" */)); 780 dumphist[2] = buf; 781 dohist(dumphist); 782 (void) close(fp); 783 unsetfd(fp); 784 SHOUT = ftmp; 785 didfds = oldidfds; 786 } 787 } 788 789 void 790 goodbye(void) 791 { 792 if (loginsh) { 793 (void) signal(SIGQUIT, SIG_IGN); 794 (void) signal(SIGINT, SIG_IGN); 795 (void) signal(SIGTERM, SIG_IGN); 796 setintr = 0; /* No interrupts after "logout" */ 797 if (adrof(S_home /* "home" */)) 798 srccat(value(S_home /* "home" */), S_SLADOTlogout /* "/.logout" */); 799 } 800 rechist(); 801 exitstat(); 802 } 803 804 void 805 exitstat(void) 806 { 807 808 #ifdef PROF 809 monitor(0); 810 #endif 811 /* 812 * Note that if STATUS is corrupted (i.e. getn bombs) 813 * then error will exit directly because we poke child here. 814 * Otherwise we might continue unwarrantedly (sic). 815 */ 816 child++; 817 untty(); 818 exit(getn(value(S_status /* "status" */))); 819 } 820 821 /* 822 * in the event of a HUP we want to save the history 823 */ 824 void 825 phup(void) 826 { 827 rechist(); 828 exit(1); 829 } 830 831 tchar *jobargv[2] = { S_jobs /* "jobs" */, 0 }; 832 /* 833 * Catch an interrupt, e.g. during lexical input. 834 * If we are an interactive shell, we reset the interrupt catch 835 * immediately. In any case we drain the shell output, 836 * and finally go through the normal error mechanism, which 837 * gets a chance to make the shell go away. 838 */ 839 void 840 pintr(void) 841 { 842 pintr1(1); 843 } 844 845 void 846 pintr1(bool wantnl) 847 { 848 tchar **v; 849 int omask; 850 851 omask = sigblock(0); 852 if (setintr) { 853 (void) sigsetmask(omask & ~sigmask(SIGINT)); 854 if (pjobs) { 855 pjobs = 0; 856 printf("\n"); 857 dojobs(jobargv); 858 bferr("Interrupted"); 859 } 860 } 861 (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 862 draino(); 863 864 /* 865 * If we have an active "onintr" then we search for the label. 866 * Note that if one does "onintr -" then we shan't be interruptible 867 * so we needn't worry about that here. 868 */ 869 if (gointr) { 870 search(ZGOTO, 0, gointr); 871 timflg = 0; 872 if (v = pargv) 873 pargv = 0, blkfree(v); 874 if (v = gargv) 875 gargv = 0, blkfree(v); 876 reset(); 877 } else if (intty && wantnl) 878 printf("\n"); /* Some like this, others don't */ 879 error(NULL); 880 } 881 882 /* 883 * Process is the main driving routine for the shell. 884 * It runs all command processing, except for those within { ... } 885 * in expressions (which is run by a routine evalav in sh.exp.c which 886 * is a stripped down process), and `...` evaluation which is run 887 * also by a subset of this code in sh.glob.c in the routine backeval. 888 * 889 * The code here is a little strange because part of it is interruptible 890 * and hence freeing of structures appears to occur when none is necessary 891 * if this is ignored. 892 * 893 * Note that if catch is not set then we will unwind on any error. 894 * If an end-of-file occurs, we return. 895 */ 896 void 897 process(bool catch) 898 { 899 jmp_buf osetexit; 900 struct command *t; 901 902 getexit(osetexit); 903 for (;;) { 904 pendjob(); 905 freelex(¶ml); 906 paraml.next = paraml.prev = ¶ml; 907 paraml.word = S_ /* "" */; 908 t = 0; 909 setexit(); 910 justpr = enterhist; /* execute if not entering history */ 911 912 /* 913 * Interruptible during interactive reads 914 */ 915 if (setintr) 916 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 917 918 /* 919 * For the sake of reset() 920 */ 921 freelex(¶ml), freesyn(t), t = 0; 922 923 if (haderr) { 924 if (!catch) { 925 /* unwind */ 926 doneinp = 0; 927 resexit(osetexit); 928 reset(); 929 } 930 haderr = 0; 931 /* 932 * Every error is eventually caught here or 933 * the shell dies. It is at this 934 * point that we clean up any left-over open 935 * files, by closing all but a fixed number 936 * of pre-defined files. Thus routines don't 937 * have to worry about leaving files open due 938 * to deeper errors... they will get closed here. 939 */ 940 closem(); 941 continue; 942 } 943 if (doneinp) { 944 doneinp = 0; 945 break; 946 } 947 if (chkstop) 948 chkstop--; 949 if (neednote) 950 pnote(); 951 if (intty && prompt && evalvec == 0) { 952 mailchk(); 953 /* 954 * If we are at the end of the input buffer 955 * then we are going to read fresh stuff. 956 * Otherwise, we are rereading input and don't 957 * need or want to prompt. 958 */ 959 if (fseekp == feobp) 960 printprompt(); 961 } 962 err = 0; 963 964 /* 965 * Echo not only on VERBOSE, but also with history expansion. 966 */ 967 if (lex(¶ml) && intty || 968 adrof(S_verbose /* "verbose" */)) { 969 haderr = 1; 970 prlex(¶ml); 971 haderr = 0; 972 } 973 974 /* 975 * The parser may lose space if interrupted. 976 */ 977 if (setintr) 978 (void) sigblock(sigmask(SIGINT)); 979 980 /* 981 * Save input text on the history list if 982 * reading in old history, or it 983 * is from the terminal at the top level and not 984 * in a loop. 985 */ 986 if (enterhist || catch && intty && !whyles) 987 savehist(¶ml); 988 989 /* 990 * Print lexical error messages, except when sourcing 991 * history lists. 992 */ 993 if (!enterhist && err) 994 error("%s", gettext(err)); 995 996 /* 997 * If had a history command :p modifier then 998 * this is as far as we should go 999 */ 1000 if (justpr) 1001 reset(); 1002 1003 alias(¶ml); 1004 1005 /* 1006 * Parse the words of the input into a parse tree. 1007 */ 1008 t = syntax(paraml.next, ¶ml, 0); 1009 if (err) 1010 error("%s", gettext(err)); 1011 1012 /* 1013 * Execute the parse tree 1014 */ 1015 { 1016 /* 1017 * POSIX requires SIGCHLD to be held 1018 * until all processes have joined the 1019 * process group in order to avoid race 1020 * condition. 1021 */ 1022 int omask; 1023 1024 omask = sigblock(sigmask(SIGCHLD)); 1025 execute(t, tpgrp); 1026 (void) sigsetmask(omask &~ sigmask(SIGCHLD)); 1027 } 1028 1029 if (err) 1030 error("%s", gettext(err)); 1031 /* 1032 * Made it! 1033 */ 1034 freelex(¶ml), freesyn(t); 1035 } 1036 resexit(osetexit); 1037 } 1038 1039 void 1040 dosource(tchar **t) 1041 { 1042 tchar *f; 1043 int u; 1044 bool hflg = 0; 1045 tchar buf[BUFSIZ]; 1046 1047 t++; 1048 if (*t && eq(*t, S_h /* "-h" */)) { 1049 if (*++t == NOSTR) 1050 bferr("Too few arguments."); 1051 hflg++; 1052 } 1053 (void) strcpy_(buf, *t); 1054 f = globone(buf); 1055 u = dmove(open_(f, 0), -1); 1056 xfree(f); 1057 freelex(¶ml); 1058 if (u < 0 && !hflg) 1059 Perror(f); 1060 (void) fcntl(u, F_SETFD, 1); 1061 srcunit(u, 0, hflg); 1062 } 1063 1064 /* 1065 * Check for mail. 1066 * If we are a login shell, then we don't want to tell 1067 * about any mail file unless its been modified 1068 * after the time we started. 1069 * This prevents us from telling the user things he already 1070 * knows, since the login program insists on saying 1071 * "You have mail." 1072 */ 1073 void 1074 mailchk(void) 1075 { 1076 struct varent *v; 1077 tchar **vp; 1078 time_t t; 1079 int intvl, cnt; 1080 struct stat stb; 1081 bool new; 1082 1083 v = adrof(S_mail /* "mail" */); 1084 if (v == 0) 1085 return; 1086 (void) time(&t); 1087 vp = v->vec; 1088 cnt = blklen(vp); 1089 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 1090 if (intvl < 1) 1091 intvl = 1; 1092 if (chktim + intvl > t) 1093 return; 1094 for (; *vp; vp++) { 1095 if (stat_(*vp, &stb) < 0) 1096 continue; 1097 new = stb.st_mtime > time0.tv_sec; 1098 if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || 1099 (stb.st_atime <= chktim && stb.st_mtime <= chktim) || 1100 loginsh && !new) 1101 continue; 1102 if (cnt == 1) 1103 printf("You have %smail.\n", new ? "new " : ""); 1104 else 1105 printf("%s in %t.\n", new ? "New mail" : "Mail", *vp); 1106 } 1107 chktim = t; 1108 } 1109 1110 /* 1111 * Extract a home directory from the password file 1112 * The argument points to a buffer where the name of the 1113 * user whose home directory is sought is currently. 1114 * We write the home directory of the user back there. 1115 */ 1116 int 1117 gethdir(tchar *home) 1118 { 1119 /* getpwname will not be modified, so we need temp. buffer */ 1120 char home_str[BUFSIZ]; 1121 tchar home_ts[BUFSIZ]; 1122 struct passwd *pp /* = getpwnam(home) */; 1123 1124 pp = getpwnam(tstostr(home_str, home)); 1125 if (pp == 0) 1126 return (1); 1127 (void) strcpy_(home, strtots(home_ts, pp->pw_dir)); 1128 return (0); 1129 } 1130 1131 1132 #if 0 1133 void 1134 #ifdef PROF 1135 done(int i) 1136 #else 1137 exit(int i) 1138 #endif 1139 { 1140 1141 untty(); 1142 _exit(i); 1143 } 1144 #endif 1145 1146 void 1147 printprompt(void) 1148 { 1149 tchar *cp; 1150 1151 if (!whyles) { 1152 /* 1153 * Print the prompt string 1154 */ 1155 for (cp = value(S_prompt /* "prompt" */); *cp; cp++) 1156 if (*cp == HIST) 1157 printf("%d", eventno + 1); 1158 else { 1159 if (*cp == '\\' && cp[1] == HIST) 1160 cp++; 1161 Putchar(*cp | QUOTE); 1162 } 1163 } else 1164 /* 1165 * Prompt for forward reading loop 1166 * body content. 1167 */ 1168 printf("? "); 1169 flush(); 1170 } 1171 1172 /* 1173 * Save char * block. 1174 */ 1175 tchar ** 1176 strblktotsblk(char **v, int num) 1177 { 1178 tchar **newv = 1179 (tchar **)xcalloc((unsigned)(num+ 1), sizeof (tchar **)); 1180 tchar **onewv = newv; 1181 1182 while (*v && num--) 1183 *newv++ = strtots(NOSTR, *v++); 1184 *newv = 0; 1185 return (onewv); 1186 } 1187 1188 void 1189 sigwaiting(void) 1190 { 1191 _signal(SIGWAITING, sigwaiting); 1192 } 1193 1194 void 1195 siglwp(void) 1196 { 1197 _signal(SIGLWP, siglwp); 1198 } 1199 1200 1201 /* 1202 * Following functions and data are used for csh to do its 1203 * file descriptors book keeping. 1204 */ 1205 1206 static int *fdinuse = NULL; /* The list of files opened by csh */ 1207 static int nbytesused = 0; /* no of bytes allocated to fdinuse */ 1208 static int max_fd = 0; /* The maximum descriptor in fdinuse */ 1209 static int my_pid; /* The process id set in initdesc() */ 1210 static int NoFile = NOFILE; /* The number of files I can use. */ 1211 1212 /* 1213 * Get the number of files this csh can use. 1214 * 1215 * Move the initial descriptors to their eventual 1216 * resting places, closing all other units. 1217 * 1218 * Also, reserve 0/1/2, so NIS+ routines do not get 1219 * hold of them. And initialize fdinuse list and set 1220 * the current process id. 1221 * 1222 * If this csh was invoked from setuid'ed script file, 1223 * do not close the third argument passed. The file 1224 * must be one of /dev/fd/0,1,2,,, 1225 * (execv() always passes three arguments when it execs a script 1226 * file in a form of #! /bin/csh -b.) 1227 * 1228 * If is_reinit is set in initdesc_x(), then we only close the file 1229 * descriptors that we actually opened (as recorded in fdinuse). 1230 */ 1231 void 1232 initdesc(int argc, char *argv[]) 1233 { 1234 initdesc_x(argc, argv, 0); 1235 } 1236 1237 void 1238 reinitdesc(int argc, char *argv[]) 1239 { 1240 initdesc_x(argc, argv, 1); 1241 } 1242 1243 /* 1244 * Callback functions for closing all file descriptors. 1245 */ 1246 static int 1247 close_except(void *cd, int fd) 1248 { 1249 int script_fd = *(int *)cd; 1250 1251 if (fd >= 3 && fd < NoFile && fd != script_fd) 1252 (void) close(fd); 1253 return (0); 1254 } 1255 1256 static int 1257 close_inuse(void *cd, int fd) 1258 { 1259 int script_fd = *(int *)cd; 1260 1261 if (fd >= 3 && fd < NoFile && fd != script_fd && 1262 CSH_FD_ISSET(fd, fdinuse)) { 1263 (void) close(fd); 1264 unsetfd(fd); 1265 } 1266 return (0); 1267 } 1268 1269 void 1270 initdesc_x(int argc, char *argv[], int is_reinit) 1271 { 1272 1273 int script_fd = -1; 1274 struct stat buf; 1275 struct rlimit rlp; 1276 1277 /* 1278 * Get pid of this shell 1279 */ 1280 my_pid = getpid(); 1281 1282 /* 1283 * Get the hard limit numbers of descriptors 1284 * this csh can use. 1285 */ 1286 if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) 1287 NoFile = rlp.rlim_cur; 1288 1289 /* 1290 * If this csh was invoked for executing setuid script file, 1291 * the third argument passed is the special file name 1292 * which should not be closed. This special file name is 1293 * in the form /dev/fd/X. 1294 */ 1295 if (argc >= 3) 1296 if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1) 1297 script_fd = -1; 1298 else 1299 fcntl(script_fd, F_SETFD, 1); /* Make sure to close 1300 * this file on exec. 1301 */ 1302 1303 if (fdinuse == NULL) { 1304 nbytesused = sizeof (int) * howmany(NoFile, sizeof (int) * NBBY); 1305 fdinuse = (int *)xalloc(nbytesused); 1306 } 1307 1308 /* 1309 * Close all files except 0/1/2 to get a clean 1310 * file descritor space. 1311 */ 1312 if (!is_reinit) 1313 (void) fdwalk(close_except, &script_fd); 1314 else 1315 (void) fdwalk(close_inuse, &script_fd); 1316 1317 didfds = 0; /* 0, 1, 2 aren't set up */ 1318 1319 if (fstat(0, &buf) < 0) 1320 open("/dev/null", 0); 1321 1322 (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, 1); 1323 (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, 1); 1324 (void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD, 1); 1325 (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, 1); 1326 1327 /* 1328 * Open 0/1/2 to avoid Nis+ functions to pick them up. 1329 * Now, 0/1/2 are saved, close them and open them. 1330 */ 1331 close(0); close(1); close(2); 1332 open("/dev/null", 0); 1333 dup(0); 1334 dup(0); 1335 1336 /* 1337 * Clear fd_set mask 1338 */ 1339 if (!is_reinit) 1340 CSH_FD_ZERO(fdinuse, nbytesused); 1341 } 1342 1343 /* 1344 * This routine is called after an error to close up 1345 * any units which may have been left open accidentally. 1346 * 1347 * You only need to remove files in fdinuse list. 1348 * After you have removed the files, you can clear the 1349 * list and max_fd. 1350 */ 1351 void 1352 closem(void) 1353 { 1354 int f; 1355 1356 for (f = 3; f <= max_fd; f++) { 1357 if (CSH_FD_ISSET(f, fdinuse) && 1358 f != SHIN && f != SHOUT && f != SHDIAG && 1359 f != OLDSTD && f != FSHTTY) 1360 close(f); 1361 } 1362 CSH_FD_ZERO(fdinuse, nbytesused); 1363 max_fd = 0; 1364 } 1365 1366 /* 1367 * Reset my_pid when a new process is created. Only call this 1368 * if you want the process to affect fdinuse (e.g., fork, but 1369 * not vfork). 1370 */ 1371 void 1372 new_process(void) 1373 { 1374 my_pid = getpid(); 1375 } 1376 1377 1378 /* 1379 * Whenever Csh open/create/dup/pipe a file or files, 1380 * Csh keeps track of its open files. The open files 1381 * are kept in "fdinuse, Fd In Use" list. 1382 * 1383 * When a file descriptor is newly allocated, setfd() is 1384 * used to mark the fact in "fdinuse" list. 1385 * For example, 1386 * fd = open("newfile", 0); 1387 * setfd(fd); 1388 * 1389 * When a file is freed by close() function, unsetfd() is 1390 * used to remove the fd from "fdinuse" list. 1391 * For example, 1392 * close(fd); 1393 * unsetfd(fd); 1394 */ 1395 void 1396 setfd(int fd) 1397 { 1398 /* 1399 * Because you want to avoid 1400 * conflict due to vfork(). 1401 */ 1402 if (my_pid != getpid()) 1403 return; 1404 1405 if (fd >= NoFile || fd < 0) 1406 return; 1407 1408 if (fd > max_fd) 1409 max_fd = fd; 1410 CSH_FD_SET(fd, fdinuse); 1411 } 1412 1413 void 1414 unsetfd(int fd) 1415 { 1416 int i; 1417 1418 /* 1419 * Because you want to avoid 1420 * conflict due to vfork(). 1421 */ 1422 if (my_pid != getpid()) 1423 return; 1424 1425 if (fd >= NoFile || fd < 0) 1426 return; 1427 1428 CSH_FD_CLR(fd, fdinuse); 1429 if (fd == max_fd) { 1430 for (i = max_fd-1; i >= 3; i--) 1431 if (CSH_FD_ISSET(i, fdinuse)) { 1432 max_fd = i; 1433 return; 1434 } 1435 max_fd = 0; 1436 } 1437 } 1438 1439 /* 1440 * A generic call back routine to output error messages from the 1441 * policy backing functions called by pfcsh. 1442 */ 1443 void 1444 secpolicy_print(int level, const char *msg) 1445 { 1446 switch (level) { 1447 case SECPOLICY_WARN: 1448 default: 1449 haderr = 1; 1450 printf("%s: ", msg); /* printf() does gettext() */ 1451 break; 1452 case SECPOLICY_ERROR: 1453 bferr((char *)msg); /* bferr() does gettext() */ 1454 break; 1455 } 1456 } 1457