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