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