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