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