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