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