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