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 notty: 470 printf("Warning: no access to tty; thus no job control in this shell...\n"); 471 tpgrp = -1; 472 } 473 } 474 } 475 if (setintr == 0 && parintr == SIG_DFL) 476 setintr++; 477 478 /* 479 * Set SIGCHLD handler, making sure that reads restart after it runs. 480 */ 481 sigemptyset(&sa.sa_mask); 482 sa.sa_handler = pchild; 483 sa.sa_flags = SA_RESTART; 484 (void) sigaction(SIGCHLD, &sa, (struct sigaction *)NULL); 485 486 /* 487 * Set an exit here in case of an interrupt or error reading 488 * the shell start-up scripts. 489 */ 490 setexit(); 491 haderr = 0; /* In case second time through */ 492 if (!fast && reenter == 0) { 493 reenter++; 494 495 /* 496 * If this is a login csh, and /etc/.login exists, 497 * source /etc/.login first. 498 */ 499 if (loginsh) { 500 tchar tmp_etc[4+1]; /* strlen("/etc")+1 */ 501 tchar tmp_login[7+1]; /* strlen("/.login")+1 */ 502 503 strtots(tmp_etc, "/etc"); 504 strtots(tmp_login, "/.login"); 505 srccat_inlogin(tmp_etc, tmp_login); 506 } 507 508 /* Will have value("home") here because set fast if don't */ 509 srccat(value(S_home /* "home" */), 510 S_SLADOTcshrc /* "/.cshrc" */); 511 512 /* Hash path */ 513 if (!fast && !arginp && !onelflg && !havhash) 514 dohash(xhash); 515 516 517 /* 518 * Reconstruct the history list now, so that it's 519 * available from within .login. 520 */ 521 dosource(loadhist); 522 if (loginsh) { 523 srccat_inlogin(value(S_home /* "home" */), 524 S_SLADOTlogin /* "/.login" */); 525 } 526 527 /* 528 * To get cdpath hashing $cdpath must have a 529 * value, not $CDPATH. So if after reading 530 * the startup files ( .cshrc ), and 531 * user has specified a value for cdpath, then 532 * cache $cdpath paths. xhash2 is global array 533 * for $cdpath caching. 534 */ 535 if (!fast && !arginp && !onelflg && !havhash2) 536 dohash(xhash2); 537 } 538 539 /* 540 * Now are ready for the -v and -x flags 541 */ 542 if (nverbose) 543 setNS(S_verbose /* "verbose" */); 544 if (nexececho) 545 setNS(S_echo /* "echo" */); 546 547 /* 548 * All the rest of the world is inside this call. 549 * The argument to process indicates whether it should 550 * catch "error unwinds". Thus if we are a interactive shell 551 * our call here will never return by being blown past on an error. 552 */ 553 process(setintr); 554 555 /* 556 * Mop-up. 557 */ 558 if (loginsh) { 559 printf("logout\n"); 560 (void) close(SHIN); /* No need for unsetfd(). */ 561 child++; 562 goodbye(); 563 } 564 rechist(); 565 exitstat(); 566 } 567 568 void 569 untty(void) 570 { 571 572 if (tpgrp > 0) { 573 (void) setpgid(0, opgrp); 574 (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp); 575 } 576 } 577 578 void 579 importpath(tchar *cp) 580 { 581 int i = 0; 582 tchar *dp; 583 tchar **pv; 584 int c; 585 static tchar dot[2] = {'.', 0}; 586 587 for (dp = cp; *dp; dp++) 588 if (*dp == ':') 589 i++; 590 /* 591 * i+2 where i is the number of colons in the path. 592 * There are i+1 directories in the path plus we need 593 * room for a zero terminator. 594 */ 595 pv = (tchar **)xcalloc((unsigned)(i + 2), sizeof (tchar **)); 596 dp = cp; 597 i = 0; 598 if (*dp) 599 for (;;) { 600 if ((c = *dp) == ':' || c == 0) { 601 *dp = 0; 602 pv[i++] = savestr(*cp ? cp : dot); 603 if (c) { 604 cp = dp + 1; 605 *dp = ':'; 606 } else 607 break; 608 } 609 dp++; 610 } 611 pv[i] = 0; 612 set1(S_path /* "path" */, pv, &shvhed); 613 } 614 615 /* 616 * Source to the file which is the catenation of the argument names. 617 */ 618 void 619 srccat(tchar *cp, tchar *dp) 620 { 621 tchar *ep = strspl(cp, dp); 622 int unit = dmove(open_(ep, 0), -1); 623 624 (void) fcntl(unit, F_SETFD, 1); 625 xfree(ep); 626 #ifdef INGRES 627 srcunit(unit, 0, 0); 628 #else 629 srcunit(unit, 1, 0); 630 #endif 631 } 632 633 /* 634 * Source to the file which is the catenation of the argument names. 635 * This one does not check the ownership. 636 */ 637 void 638 srccat_inlogin(tchar *cp, tchar *dp) 639 { 640 tchar *ep = strspl(cp, dp); 641 int unit = dmove(open_(ep, 0), -1); 642 643 (void) fcntl(unit, F_SETFD, 1); 644 xfree(ep); 645 srcunit(unit, 0, 0); 646 } 647 648 /* 649 * Source to a unit. If onlyown it must be our file or our group or 650 * we don't chance it. This occurs on ".cshrc"s and the like. 651 */ 652 void 653 srcunit(int unit, bool onlyown, bool hflg) 654 { 655 /* We have to push down a lot of state here */ 656 /* All this could go into a structure */ 657 int oSHIN = -1, oldintty = intty; 658 struct whyle *oldwhyl = whyles; 659 tchar *ogointr = gointr, *oarginp = arginp; 660 tchar *oevalp = evalp, **oevalvec = evalvec; 661 int oonelflg = onelflg; 662 bool oenterhist = enterhist; 663 tchar OHIST = HIST; 664 #ifdef TELL 665 bool otell = cantell; 666 #endif 667 struct Bin saveB; 668 669 /* The (few) real local variables */ 670 jmp_buf oldexit; 671 int reenter, omask; 672 673 if (unit < 0) 674 return; 675 if (didfds) 676 donefds(); 677 if (onlyown) { 678 struct stat stb; 679 680 if (fstat(unit, &stb) < 0 || 681 (stb.st_uid != uid && stb.st_gid != getgid())) { 682 (void) close(unit); 683 unsetfd(unit); 684 return; 685 } 686 } 687 688 /* 689 * There is a critical section here while we are pushing down the 690 * input stream since we have stuff in different structures. 691 * If we weren't careful an interrupt could corrupt SHIN's Bin 692 * structure and kill the shell. 693 * 694 * We could avoid the critical region by grouping all the stuff 695 * in a single structure and pointing at it to move it all at 696 * once. This is less efficient globally on many variable references 697 * however. 698 */ 699 getexit(oldexit); 700 reenter = 0; 701 if (setintr) 702 omask = sigblock(sigmask(SIGINT)); 703 setexit(); 704 reenter++; 705 if (reenter == 1) { 706 /* Setup the new values of the state stuff saved above */ 707 copy((char *)&saveB, (char *)&B, sizeof (saveB)); 708 fbuf = (tchar **) 0; 709 fseekp = feobp = fblocks = 0; 710 oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; 711 intty = isatty(SHIN), whyles = 0, gointr = 0; 712 evalvec = 0; evalp = 0; 713 enterhist = hflg; 714 if (enterhist) 715 HIST = '\0'; 716 /* 717 * Now if we are allowing commands to be interrupted, 718 * we let ourselves be interrupted. 719 */ 720 if (setintr) 721 (void) sigsetmask(omask); 722 #ifdef TELL 723 settell(); 724 #endif 725 process(0); /* 0 -> blow away on errors */ 726 } 727 if (setintr) 728 (void) sigsetmask(omask); 729 if (oSHIN >= 0) { 730 int i; 731 732 /* We made it to the new state... free up its storage */ 733 /* This code could get run twice but xfree doesn't care */ 734 for (i = 0; i < fblocks; i++) 735 xfree(fbuf[i]); 736 xfree((char *)fbuf); 737 738 /* Reset input arena */ 739 copy((char *)&B, (char *)&saveB, sizeof (B)); 740 741 (void) close(SHIN), SHIN = oSHIN; 742 unsetfd(SHIN); 743 arginp = oarginp, onelflg = oonelflg; 744 evalp = oevalp, evalvec = oevalvec; 745 intty = oldintty, whyles = oldwhyl, gointr = ogointr; 746 if (enterhist) 747 HIST = OHIST; 748 enterhist = oenterhist; 749 #ifdef TELL 750 cantell = otell; 751 #endif 752 } 753 754 resexit(oldexit); 755 /* 756 * If process reset() (effectively an unwind) then 757 * we must also unwind. 758 */ 759 if (reenter >= 2) 760 error(NULL); 761 } 762 763 void 764 rechist(void) 765 { 766 tchar buf[BUFSIZ]; 767 int fp, ftmp, oldidfds; 768 769 if (!fast) { 770 if (value(S_savehist /* "savehist" */)[0] == '\0') 771 return; 772 (void) strcpy_(buf, value(S_home /* "home" */)); 773 (void) strcat_(buf, S_SLADOThistory /* "/.history" */); 774 fp = creat_(buf, 0666); 775 if (fp == -1) 776 return; 777 oldidfds = didfds; 778 didfds = 0; 779 ftmp = SHOUT; 780 SHOUT = fp; 781 (void) strcpy_(buf, value(S_savehist /* "savehist" */)); 782 dumphist[2] = buf; 783 dohist(dumphist); 784 (void) close(fp); 785 unsetfd(fp); 786 SHOUT = ftmp; 787 didfds = oldidfds; 788 } 789 } 790 791 void 792 goodbye(void) 793 { 794 if (loginsh) { 795 (void) signal(SIGQUIT, SIG_IGN); 796 (void) signal(SIGINT, SIG_IGN); 797 (void) signal(SIGTERM, SIG_IGN); 798 setintr = 0; /* No interrupts after "logout" */ 799 if (adrof(S_home /* "home" */)) 800 srccat(value(S_home /* "home" */), 801 S_SLADOTlogout /* "/.logout" */); 802 } 803 rechist(); 804 exitstat(); 805 } 806 807 void 808 exitstat(void) 809 { 810 811 #ifdef PROF 812 monitor(0); 813 #endif 814 /* 815 * Note that if STATUS is corrupted (i.e. getn bombs) 816 * then error will exit directly because we poke child here. 817 * Otherwise we might continue unwarrantedly (sic). 818 */ 819 child++; 820 untty(); 821 exit(getn(value(S_status /* "status" */))); 822 } 823 824 /* 825 * in the event of a HUP we want to save the history 826 */ 827 void 828 phup(void) 829 { 830 rechist(); 831 exit(1); 832 } 833 834 void 835 interactive_hup(void) 836 { 837 hupforegnd(); 838 exit(1); 839 } 840 841 void 842 interactive_login_hup(void) 843 { 844 rechist(); 845 hupforegnd(); 846 exit(1); 847 } 848 849 tchar *jobargv[2] = { S_jobs /* "jobs" */, 0 }; 850 /* 851 * Catch an interrupt, e.g. during lexical input. 852 * If we are an interactive shell, we reset the interrupt catch 853 * immediately. In any case we drain the shell output, 854 * and finally go through the normal error mechanism, which 855 * gets a chance to make the shell go away. 856 */ 857 void 858 pintr(void) 859 { 860 pintr1(1); 861 } 862 863 void 864 pintr1(bool wantnl) 865 { 866 tchar **v; 867 int omask; 868 869 omask = sigblock(0); 870 if (setintr) { 871 (void) sigsetmask(omask & ~sigmask(SIGINT)); 872 if (pjobs) { 873 pjobs = 0; 874 printf("\n"); 875 dojobs(jobargv); 876 bferr("Interrupted"); 877 } 878 } 879 (void) sigsetmask(omask & ~sigmask(SIGCHLD)); 880 draino(); 881 882 /* 883 * If we have an active "onintr" then we search for the label. 884 * Note that if one does "onintr -" then we shan't be interruptible 885 * so we needn't worry about that here. 886 */ 887 if (gointr) { 888 search(ZGOTO, 0, gointr); 889 if (v = pargv) 890 pargv = 0, blkfree(v); 891 if (v = gargv) 892 gargv = 0, blkfree(v); 893 reset(); 894 } else if (intty && wantnl) 895 printf("\n"); /* Some like this, others don't */ 896 error(NULL); 897 } 898 899 /* 900 * Process is the main driving routine for the shell. 901 * It runs all command processing, except for those within { ... } 902 * in expressions (which is run by a routine evalav in sh.exp.c which 903 * is a stripped down process), and `...` evaluation which is run 904 * also by a subset of this code in sh.glob.c in the routine backeval. 905 * 906 * The code here is a little strange because part of it is interruptible 907 * and hence freeing of structures appears to occur when none is necessary 908 * if this is ignored. 909 * 910 * Note that if catch is not set then we will unwind on any error. 911 * If an end-of-file occurs, we return. 912 */ 913 void 914 process(bool catch) 915 { 916 jmp_buf osetexit; 917 struct command *t; 918 919 getexit(osetexit); 920 for (;;) { 921 pendjob(); 922 freelex(¶ml); 923 paraml.next = paraml.prev = ¶ml; 924 paraml.word = S_ /* "" */; 925 t = 0; 926 setexit(); 927 justpr = enterhist; /* execute if not entering history */ 928 929 /* 930 * Interruptible during interactive reads 931 */ 932 if (setintr) 933 (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT)); 934 935 /* 936 * For the sake of reset() 937 */ 938 freelex(¶ml), freesyn(t), t = 0; 939 940 if (haderr) { 941 if (!catch) { 942 /* unwind */ 943 doneinp = 0; 944 resexit(osetexit); 945 reset(); 946 } 947 haderr = 0; 948 /* 949 * Every error is eventually caught here or 950 * the shell dies. It is at this 951 * point that we clean up any left-over open 952 * files, by closing all but a fixed number 953 * of pre-defined files. Thus routines don't 954 * have to worry about leaving files open due 955 * to deeper errors... they will get closed here. 956 */ 957 closem(); 958 continue; 959 } 960 if (doneinp) { 961 doneinp = 0; 962 break; 963 } 964 if (chkstop) 965 chkstop--; 966 if (neednote) 967 pnote(); 968 if (intty && prompt && evalvec == 0) { 969 mailchk(); 970 /* 971 * If we are at the end of the input buffer 972 * then we are going to read fresh stuff. 973 * Otherwise, we are rereading input and don't 974 * need or want to prompt. 975 */ 976 if (fseekp == feobp) 977 printprompt(); 978 } 979 err_msg = NULL; 980 981 /* 982 * Echo not only on VERBOSE, but also with history expansion. 983 */ 984 if (lex(¶ml) && intty || 985 adrof(S_verbose /* "verbose" */)) { 986 haderr = 1; 987 prlex(¶ml); 988 haderr = 0; 989 } 990 991 /* 992 * The parser may lose space if interrupted. 993 */ 994 if (setintr) 995 (void) sigblock(sigmask(SIGINT)); 996 997 /* 998 * Save input text on the history list if 999 * reading in old history, or it 1000 * is from the terminal at the top level and not 1001 * in a loop. 1002 */ 1003 if (enterhist || catch && intty && !whyles) 1004 savehist(¶ml); 1005 1006 /* 1007 * Print lexical error messages, except when sourcing 1008 * history lists. 1009 */ 1010 if (!enterhist && err_msg) 1011 error("%s", gettext(err_msg)); 1012 1013 /* 1014 * If had a history command :p modifier then 1015 * this is as far as we should go 1016 */ 1017 if (justpr) 1018 reset(); 1019 1020 alias(¶ml); 1021 1022 /* 1023 * Parse the words of the input into a parse tree. 1024 */ 1025 t = syntax(paraml.next, ¶ml, 0); 1026 if (err_msg) 1027 error("%s", gettext(err_msg)); 1028 1029 /* 1030 * Execute the parse tree 1031 */ 1032 { 1033 /* 1034 * POSIX requires SIGCHLD to be held 1035 * until all processes have joined the 1036 * process group in order to avoid race 1037 * condition. 1038 */ 1039 int omask; 1040 1041 omask = sigblock(sigmask(SIGCHLD)); 1042 execute(t, tpgrp); 1043 (void) sigsetmask(omask &~ sigmask(SIGCHLD)); 1044 } 1045 1046 if (err_msg) 1047 error("%s", gettext(err_msg)); 1048 /* 1049 * Made it! 1050 */ 1051 freelex(¶ml), freesyn(t); 1052 } 1053 resexit(osetexit); 1054 } 1055 1056 void 1057 dosource(tchar **t) 1058 { 1059 tchar *f; 1060 int u; 1061 bool hflg = 0; 1062 tchar buf[BUFSIZ]; 1063 1064 t++; 1065 if (*t && eq(*t, S_h /* "-h" */)) { 1066 if (*++t == NOSTR) 1067 bferr("Too few arguments."); 1068 hflg++; 1069 } 1070 (void) strcpy_(buf, *t); 1071 f = globone(buf); 1072 u = dmove(open_(f, 0), -1); 1073 xfree(f); 1074 freelex(¶ml); 1075 if (u < 0 && !hflg) 1076 Perror(f); 1077 (void) fcntl(u, F_SETFD, 1); 1078 srcunit(u, 0, hflg); 1079 } 1080 1081 /* 1082 * Check for mail. 1083 * If we are a login shell, then we don't want to tell 1084 * about any mail file unless its been modified 1085 * after the time we started. 1086 * This prevents us from telling the user things they already 1087 * know, since the login program insists on saying 1088 * "You have mail." 1089 */ 1090 void 1091 mailchk(void) 1092 { 1093 struct varent *v; 1094 tchar **vp; 1095 time_t t; 1096 int intvl, cnt; 1097 struct stat stb; 1098 bool new; 1099 1100 v = adrof(S_mail /* "mail" */); 1101 if (v == 0) 1102 return; 1103 (void) time(&t); 1104 vp = v->vec; 1105 cnt = blklen(vp); 1106 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; 1107 if (intvl < 1) 1108 intvl = 1; 1109 if (chktim + intvl > t) 1110 return; 1111 for (; *vp; vp++) { 1112 if (stat_(*vp, &stb) < 0) 1113 continue; 1114 new = stb.st_mtime > time0.tv_sec; 1115 if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime || 1116 (stb.st_atime <= chktim && stb.st_mtime <= chktim) || 1117 loginsh && !new) 1118 continue; 1119 if (cnt == 1) 1120 printf("You have %smail.\n", new ? "new " : ""); 1121 else 1122 printf("%s in %t.\n", new ? "New mail" : "Mail", *vp); 1123 } 1124 chktim = t; 1125 } 1126 1127 /* 1128 * Extract a home directory from the password file 1129 * The argument points to a buffer where the name of the 1130 * user whose home directory is sought is currently. 1131 * We write the home directory of the user back there. 1132 */ 1133 int 1134 gethdir(tchar *home) 1135 { 1136 /* getpwname will not be modified, so we need temp. buffer */ 1137 char home_str[BUFSIZ]; 1138 tchar home_ts[BUFSIZ]; 1139 struct passwd *pp /* = getpwnam(home) */; 1140 1141 pp = getpwnam(tstostr(home_str, home)); 1142 if (pp == 0) 1143 return (1); 1144 (void) strcpy_(home, strtots(home_ts, pp->pw_dir)); 1145 return (0); 1146 } 1147 1148 1149 #if 0 1150 void 1151 #ifdef PROF 1152 done(int i) 1153 #else 1154 exit(int i) 1155 #endif 1156 { 1157 1158 untty(); 1159 _exit(i); 1160 } 1161 #endif 1162 1163 void 1164 printprompt(void) 1165 { 1166 tchar *cp; 1167 1168 if (!whyles) { 1169 /* 1170 * Print the prompt string 1171 */ 1172 for (cp = value(S_prompt /* "prompt" */); *cp; cp++) 1173 if (*cp == HIST) 1174 printf("%d", eventno + 1); 1175 else { 1176 if (*cp == '\\' && cp[1] == HIST) 1177 cp++; 1178 Putchar(*cp | QUOTE); 1179 } 1180 } else 1181 /* 1182 * Prompt for forward reading loop 1183 * body content. 1184 */ 1185 printf("? "); 1186 flush(); 1187 } 1188 1189 /* 1190 * Save char * block. 1191 */ 1192 tchar ** 1193 strblktotsblk(char **v, int num) 1194 { 1195 tchar **newv = 1196 (tchar **)xcalloc((unsigned)(num+ 1), sizeof (tchar **)); 1197 tchar **onewv = newv; 1198 1199 while (*v && num--) 1200 *newv++ = strtots(NOSTR, *v++); 1201 *newv = 0; 1202 return (onewv); 1203 } 1204 1205 void 1206 sigwaiting(void) 1207 { 1208 _signal(SIGWAITING, sigwaiting); 1209 } 1210 1211 void 1212 siglwp(void) 1213 { 1214 _signal(SIGLWP, siglwp); 1215 } 1216 1217 1218 /* 1219 * Following functions and data are used for csh to do its 1220 * file descriptors book keeping. 1221 */ 1222 1223 static int *fdinuse = NULL; /* The list of files opened by csh */ 1224 static int nbytesused = 0; /* no of bytes allocated to fdinuse */ 1225 static int max_fd = 0; /* The maximum descriptor in fdinuse */ 1226 static int my_pid; /* The process id set in initdesc() */ 1227 static int NoFile = NOFILE; /* The number of files I can use. */ 1228 1229 /* 1230 * Get the number of files this csh can use. 1231 * 1232 * Move the initial descriptors to their eventual 1233 * resting places, closing all other units. 1234 * 1235 * Also, reserve 0/1/2, so NIS+ routines do not get 1236 * hold of them. And initialize fdinuse list and set 1237 * the current process id. 1238 * 1239 * If this csh was invoked from setuid'ed script file, 1240 * do not close the third argument passed. The file 1241 * must be one of /dev/fd/0,1,2,,, 1242 * (execv() always passes three arguments when it execs a script 1243 * file in a form of #! /bin/csh -b.) 1244 * 1245 * If is_reinit is set in initdesc_x(), then we only close the file 1246 * descriptors that we actually opened (as recorded in fdinuse). 1247 */ 1248 void 1249 initdesc(int argc, char *argv[]) 1250 { 1251 initdesc_x(argc, argv, 0); 1252 } 1253 1254 void 1255 reinitdesc(int argc, char *argv[]) 1256 { 1257 initdesc_x(argc, argv, 1); 1258 } 1259 1260 /* 1261 * Callback functions for closing all file descriptors. 1262 */ 1263 static int 1264 close_except(void *cd, int fd) 1265 { 1266 int script_fd = *(int *)cd; 1267 1268 if (fd >= 3 && fd < NoFile && fd != script_fd) 1269 (void) close(fd); 1270 return (0); 1271 } 1272 1273 static int 1274 close_inuse(void *cd, int fd) 1275 { 1276 int script_fd = *(int *)cd; 1277 1278 if (fd >= 3 && fd < NoFile && fd != script_fd && 1279 CSH_FD_ISSET(fd, fdinuse)) { 1280 (void) close(fd); 1281 unsetfd(fd); 1282 } 1283 return (0); 1284 } 1285 1286 void 1287 initdesc_x(int argc, char *argv[], int is_reinit) 1288 { 1289 1290 int script_fd = -1; 1291 struct stat buf; 1292 struct rlimit rlp; 1293 1294 /* 1295 * Get pid of this shell 1296 */ 1297 my_pid = getpid(); 1298 1299 /* 1300 * Get the hard limit numbers of descriptors 1301 * this csh can use. 1302 */ 1303 if (getrlimit(RLIMIT_NOFILE, &rlp) == 0) 1304 NoFile = rlp.rlim_cur; 1305 1306 /* 1307 * If this csh was invoked for executing setuid script file, 1308 * the third argument passed is the special file name 1309 * which should not be closed. This special file name is 1310 * in the form /dev/fd/X. 1311 */ 1312 if (argc >= 3) 1313 if (sscanf(argv[2], "/dev/fd/%d", &script_fd) != 1) 1314 script_fd = -1; 1315 else 1316 /* Make sure to close this file on exec. */ 1317 fcntl(script_fd, F_SETFD, 1); 1318 1319 if (fdinuse == NULL) { 1320 nbytesused = sizeof (int) * 1321 howmany(NoFile, sizeof (int) * NBBY); 1322 fdinuse = (int *)xalloc(nbytesused); 1323 } 1324 1325 /* 1326 * Close all files except 0/1/2 to get a clean 1327 * file descritor space. 1328 */ 1329 if (!is_reinit) 1330 (void) fdwalk(close_except, &script_fd); 1331 else 1332 (void) fdwalk(close_inuse, &script_fd); 1333 1334 didfds = 0; /* 0, 1, 2 aren't set up */ 1335 1336 if (fstat(0, &buf) < 0) 1337 open("/dev/null", 0); 1338 1339 (void) fcntl(SHIN = dcopy(0, FSHIN), F_SETFD, 1); 1340 (void) fcntl(SHOUT = dcopy(1, FSHOUT), F_SETFD, 1); 1341 (void) fcntl(SHDIAG = dcopy(2, FSHDIAG), F_SETFD, 1); 1342 (void) fcntl(OLDSTD = dcopy(SHIN, FOLDSTD), F_SETFD, 1); 1343 1344 /* 1345 * Open 0/1/2 to avoid Nis+ functions to pick them up. 1346 * Now, 0/1/2 are saved, close them and open them. 1347 */ 1348 close(0); close(1); close(2); 1349 open("/dev/null", 0); 1350 dup(0); 1351 dup(0); 1352 1353 /* 1354 * Clear fd_set mask 1355 */ 1356 if (!is_reinit) 1357 CSH_FD_ZERO(fdinuse, nbytesused); 1358 } 1359 1360 /* 1361 * This routine is called after an error to close up 1362 * any units which may have been left open accidentally. 1363 * 1364 * You only need to remove files in fdinuse list. 1365 * After you have removed the files, you can clear the 1366 * list and max_fd. 1367 */ 1368 void 1369 closem(void) 1370 { 1371 int f; 1372 1373 for (f = 3; f <= max_fd; f++) { 1374 if (CSH_FD_ISSET(f, fdinuse) && 1375 f != SHIN && f != SHOUT && f != SHDIAG && 1376 f != OLDSTD && f != FSHTTY) 1377 close(f); 1378 } 1379 CSH_FD_ZERO(fdinuse, nbytesused); 1380 max_fd = 0; 1381 } 1382 1383 /* 1384 * Reset my_pid when a new process is created. Only call this 1385 * if you want the process to affect fdinuse (e.g., fork, but 1386 * not vfork). 1387 */ 1388 void 1389 new_process(void) 1390 { 1391 my_pid = getpid(); 1392 } 1393 1394 1395 /* 1396 * Whenever Csh open/create/dup/pipe a file or files, 1397 * Csh keeps track of its open files. The open files 1398 * are kept in "fdinuse, Fd In Use" list. 1399 * 1400 * When a file descriptor is newly allocated, setfd() is 1401 * used to mark the fact in "fdinuse" list. 1402 * For example, 1403 * fd = open("newfile", 0); 1404 * setfd(fd); 1405 * 1406 * When a file is freed by close() function, unsetfd() is 1407 * used to remove the fd from "fdinuse" list. 1408 * For example, 1409 * close(fd); 1410 * unsetfd(fd); 1411 */ 1412 void 1413 setfd(int fd) 1414 { 1415 /* 1416 * Because you want to avoid 1417 * conflict due to vfork(). 1418 */ 1419 if (my_pid != getpid()) 1420 return; 1421 1422 if (fd >= NoFile || fd < 0) 1423 return; 1424 1425 if (fd > max_fd) 1426 max_fd = fd; 1427 CSH_FD_SET(fd, fdinuse); 1428 } 1429 1430 void 1431 unsetfd(int fd) 1432 { 1433 int i; 1434 1435 /* 1436 * Because you want to avoid 1437 * conflict due to vfork(). 1438 */ 1439 if (my_pid != getpid()) 1440 return; 1441 1442 if (fd >= NoFile || fd < 0) 1443 return; 1444 1445 CSH_FD_CLR(fd, fdinuse); 1446 if (fd == max_fd) { 1447 for (i = max_fd-1; i >= 3; i--) 1448 if (CSH_FD_ISSET(i, fdinuse)) { 1449 max_fd = i; 1450 return; 1451 } 1452 max_fd = 0; 1453 } 1454 } 1455