1 /* 2 3 * Copyright (c) 1984, 1985, 1986 AT&T 4 * All Rights Reserved 5 6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE 7 * CODE OF AT&T. 8 * The copyright notice above does not 9 * evidence any actual or intended 10 * publication of such source code. 11 12 */ 13 /* @(#)main.c 1.1 */ 14 /* 15 * UNIX shell 16 * 17 * S. R. Bourne 18 * Rewritten by David Korn 19 * AT&T Bell Laboratories 20 * 21 */ 22 23 #ifdef BSD 24 # ifdef BSD_4_2 25 # include <sys/param.h> 26 # else 27 # include <sys/types.h> 28 # endif /* BSD_4_2 */ 29 #include <sgtty.h> 30 #else 31 #include <sys/types.h> 32 #endif /* BSD */ 33 #include <sys/stat.h> 34 #include "defs.h" 35 #include "sym.h" 36 #include "flags.h" 37 #include "io.h" 38 #include "history.h" 39 #include "mode.h" 40 #include "name.h" 41 #include "stak.h" 42 #include "timeout.h" 43 #include "brkincr.h" 44 #include "builtins.h" 45 #ifdef pdp11 46 #include <execargs.h> 47 #endif /* pdp11 */ 48 49 #define FC_CHAR '!' 50 51 52 /* These routines are defined by this module */ 53 int main(); 54 void chkpr(); 55 char *getpwd(); 56 57 /* These routines are referenced by this module */ 58 extern void addblok(); 59 extern void assign(); 60 extern FILE *chkopen(); 61 extern TREPTR cmd(); 62 extern void done(); 63 extern void fassign(); 64 extern char *getpwd(); 65 extern void gscan_some(); 66 extern int hist_open(); 67 extern void hist_close(); 68 extern void hist_eof(); 69 extern void hist_flush(); 70 extern void initf(); 71 extern char *itos(); 72 extern char *mactry(); 73 extern char *movstr(); 74 extern void mem_reinit(); 75 extern void meminit(); 76 extern FILE *pathopen(); 77 extern void p_flush(); 78 extern void p_num(); 79 extern void p_str(); 80 extern void p_setout(); 81 extern void rmlocal(); 82 extern char *strrchr(); 83 extern void settemp(); 84 extern char *simple(); 85 extern void stdsigs(); 86 extern void tdystak(); 87 extern char *valup(); 88 89 static void exfile(); 90 static void pr_prompt(); 91 static void chkmail(); 92 93 extern char **environ; 94 95 96 /* The following is defined for fixcmd */ 97 98 static struct stat lastmail; 99 static long mailtime; 100 static BOOL beenhere = 0; 101 static int euserid; 102 static int egroupid; 103 104 main(c, v) 105 int c; 106 register char *v[]; 107 { 108 FILE fd; 109 register char *sim; 110 register int rsflag=1; /* local restricted flag */ 111 #ifdef apollo 112 extern char pm_$unix_env ; 113 extern FILE *_iobmax; 114 pm_$unix_env = -1; 115 _iobmax = _iob + 20; 116 #endif /* apollo */ 117 standout = stdout; 118 p_setout(stderr); 119 standin = &stdfile; 120 userid=getuid(); 121 euserid=geteuid(); 122 egroupid=getegid(); 123 time(&mailtime); 124 stdsigs(); 125 /* initialize storage allocation */ 126 stakbot = 0; 127 addblok((unsigned)0); 128 /* set up memory for namenods */ 129 meminit(); 130 /* set names from userenv 131 * 'rsflag' is zero if SHELL variable is 132 * set in environment and contains an'r' in 133 * the simple file part of the value. 134 */ 135 136 rsflag=genenv(); 137 /* a shell is also restricted if argv(0) has 138 * an 'rsh' for its simple name 139 */ 140 sim = simple(*v); 141 if(*sim=='-') 142 { 143 sim++; 144 login_sh = 2; 145 } 146 /* check for restricted shell */ 147 if(c>0 && gmatch(sim,"*rsh")) 148 rsflag=0; 149 /* look for options */ 150 /* dolc is $# */ 151 dolc=arg_opts(c,v); 152 dolv=v+c-dolc--; 153 dolv[0] = v[0]; 154 if(dolc < 1) 155 on_option(STDFLG); 156 if(is_option(STDFLG|CFLAG)==0) 157 { 158 dolc--; 159 dolv++; 160 } 161 /* set[ug]id scripts run with the -p flag */ 162 if(userid!=euserid || getgid()!=egroupid) 163 { 164 #ifdef BSD_4_2 165 /* careful of #! setuid scripts with name beginning with - */ 166 if(login_sh && strcmp(v[0],v[1])==0) 167 error(prohibited); 168 #endif /* BSD_4_2 */ 169 assign(PATHNOD,defpath); 170 on_option(PRIVM); 171 } 172 if(is_option(RSHFLG)) 173 { 174 rsflag = 0; 175 off_option(RSHFLG); 176 } 177 /* 178 * return here for shell file execution 179 * but not for parenthesis subshells 180 */ 181 setjmp(subshell); 182 cmdadr=dolv[0]; /* cmdadr is $0 */ 183 /* set pidname '$$' */ 184 movstr(itos(ppid=getpid()),pidadr); 185 srand(ppid); 186 ppid = getppid(); 187 settemp(pidadr); 188 if((beenhere++)==0) 189 { 190 int prof = !is_option(PRIVM); 191 /* decide whether shell is interactive */ 192 if(is_option(ONEFLG|CFLAG)==0 && is_option(STDFLG) && isatty(0) && 193 isatty(2)) 194 on_option(INTFLG); 195 if(ppid==1) 196 login_sh++; 197 if(login_sh >= 2) 198 { 199 /* ? profile */ 200 login_sh += 2; 201 #ifdef JOBS 202 # ifdef BSD 203 init_jobs(1); 204 # endif /* BSD */ 205 # ifdef SXT 206 init_jobs(1); 207 # endif /* SXT */ 208 #endif /* JOBS */ 209 /* system profile */ 210 if((input=pathopen(sysprofile)) != NULL) 211 { 212 exfile(TTYFLG); /* file exists */ 213 fclose(input); 214 } 215 if(prof && (input=pathopen(mactry(profile))) != NULL) 216 { 217 exfile(TTYFLG); 218 states &= ~TTYFLG; 219 fclose(input); 220 } 221 } 222 /* make sure PWD is set up correctly */ 223 if(getpwd(1)) 224 attrib(PWDNOD,N_EXPORT); 225 if(prof) 226 { 227 if(sim = valup(ENVNOD)) 228 if(*(sim = mactry(sim)) == 0) 229 sim = 0; 230 } 231 else 232 sim = suid_profile; 233 if(sim && (input = pathopen(sim)) != NULL) 234 { 235 exfile(TTYFLG); 236 states &= ~TTYFLG; 237 fclose(input); 238 } 239 if(rsflag==0) 240 on_option(RSHFLG); 241 /* open input file if specified */ 242 if(comdiv) 243 { 244 estabf(comdiv,&fd); 245 } 246 else 247 { 248 sim = cmdadr; 249 cmdadr = v[0]; 250 if(is_option(STDFLG)) 251 input = stdin; 252 else 253 { 254 #ifdef VFORK 255 char *sp = sim; 256 /* avoid path search if we already have the 257 * path name in the environment 258 */ 259 if(strcmp(sim,simple(*environ))==0) 260 sp = (*environ)+2; 261 #endif /* VFORK */ 262 #ifdef SUID_EXEC 263 /* open stream should have been passed into shell */ 264 if(gmatch(sim,devfdNN)) 265 { 266 struct stat statb; 267 int fd = atoi(sim+8); 268 if(fstat(fd,&statb)<0) 269 failed(cmdadr,badopen); 270 input = fdopen(fd,"r"); 271 sim = cmdadr; 272 off_option(EXECPR|READPR); 273 } 274 else 275 #endif /* SUID_EXEC */ 276 #ifdef VFORK 277 if((input=pathopen(sp))==NULL) 278 #else 279 if((input=pathopen(sim))==NULL) 280 /* for compatibility with bsh */ 281 if((input=chkopen(sim))==NULL) 282 #endif /* VFORK */ 283 failed(sim,badopen); 284 /* eliminate local aliases and functions */ 285 gscan_some(rmlocal,alias,N_EXPORT,0); 286 gscan_some(rmlocal,prnames,N_EXPORT,0); 287 } 288 cmdadr = sim; 289 comdiv--; 290 #ifdef ACCT 291 initacct(); 292 if(fileno(input) != 0) 293 preacct(cmdadr); 294 #endif /* ACCT */ 295 } 296 } 297 #ifdef pdp11 298 else 299 *execargs=(char *) dolv; /* for `ps' cmd */ 300 #endif /* pdp11 */ 301 states |= is_option(INTFLG); 302 exfile(0); 303 if(states&PROMPT) 304 newline(); 305 done(0); 306 } 307 308 static void exfile(prof) 309 register int prof; 310 { 311 long curtime; 312 TREPTR t; 313 register FILE *fp = input; 314 register struct fixcmd *fixp; 315 register int fno; 316 unsigned execflags; 317 /* move input */ 318 if(fisopen(fp) && (fno=fileno(fp))!=F_STRING) 319 { 320 if(fno > 0) 321 { 322 fp = input = frenumber(fp,INIO); 323 setbuf(fp,(char*)_sibuf); 324 if(prof==0) 325 setbuf(stdin,NIL); 326 } 327 /* if stdin is a pipe it must be unbuffered */ 328 else 329 setbuf(fp,ispipe(fp)?NIL:(char*)_sibuf); 330 } 331 if(states&INTFLG) 332 { 333 if(isnull(PS1NOD)) 334 assign(PS1NOD, (euserid?stdprompt:supprompt)); 335 states |= TTYFLG|PROMPT; 336 ignsig(SIGTERM); 337 hist_open(); 338 if(fc_fix) 339 on_option(FIXFLG); 340 #ifdef JOBS 341 # ifdef BSD 342 if(login_sh<=1) 343 init_jobs(0); 344 # endif /* BSD */ 345 # ifdef SXT 346 if(login_sh<=1) 347 init_jobs(0); 348 # endif /* SXT */ 349 #endif /* JOBS */ 350 } 351 else 352 { 353 if(prof) 354 states |= prof; 355 else 356 { 357 off_option(EDITVI|EMACS|GMACS); 358 on_option(HASHALL|INPROC); 359 } 360 states &= ~(PROMPT|MONITOR); 361 off_option(FIXFLG); 362 } 363 fixp = fc_fix; 364 if(setjmp(errshell) && prof) 365 { 366 fclose(fp); 367 return; 368 } 369 /* error return here */ 370 freturn = (jmp_buf*)errshell; 371 loopcnt=peekc=peekn=0; 372 iopend=0; 373 linked = 0; 374 if(fp!=NULL) 375 initf(fp); 376 if(feof(fp)) 377 goto eof_or_error; 378 /* command loop */ 379 while(1) 380 { 381 tdystak((STKPTR)0); 382 stakchk(); /* may reduce sbrk */ 383 exitset(); 384 states &= ~(ERRFLG|READPR|RWAIT|MONITOR); 385 states |= is_option(READPR)|WAITING|ERRFLG; 386 /* -eim flags don't apply to profiles */ 387 if(prof) 388 states &= ~(INTFLG|ERRFLG|MONITOR); 389 p_setout(stderr); 390 if((states&PROMPT) && standin->fstak==0 && !feof(fp)) 391 { 392 register char *mail; 393 #ifdef JOBS 394 /* allow children to be stopped*/ 395 states &= ~(MONITOR|NONSTOP); 396 states |= is_option(MONITOR); 397 list_jobs(N_FLAG); 398 #endif /* JOBS */ 399 if((mail=valup(MAILPNOD)) || (mail=valup(MAILNOD))) 400 { 401 time(&curtime); 402 if ((curtime - mailtime) >= mailchk) 403 { 404 chkmail(mail); 405 mailtime = curtime; 406 } 407 } 408 if(fixp) 409 hist_eof(); 410 pr_prompt(mactry(valup(PS1NOD))); 411 /* sets timeout for command entry */ 412 #if TIMEOUT!=0 413 if(timeout <= 0 || timeout > TIMEOUT) 414 timeout = TIMEOUT; 415 #endif 416 if(timeout>0) 417 alarm((unsigned)timeout); 418 standin->flin = 1; 419 } 420 trapnote=0; 421 peekc=readc(); 422 if(feof(fp) || ferror(fp)) 423 { 424 eof_or_error: 425 if((states&PROMPT) && standin->fstak==0 && ferror(fp)==0) 426 { 427 clearerr(fp); 428 if(is_option(NOEOF) && !ferror(output)) 429 { 430 p_str(logout,NL); 431 continue; 432 } 433 #ifdef JOBS 434 else if(close_jobs()<0) 435 continue; 436 #endif /* JOBS */ 437 hist_close(); 438 } 439 return; 440 } 441 if(timeout>0) 442 alarm(0); 443 if((states&PROMPT) && fixp) 444 { 445 hist_eof(); 446 putc(peekc,fixp->fixfd); 447 } 448 states |= is_option(FIXFLG); 449 states &= ~ WAITING; 450 t = cmd(NL,MTFLG); 451 if(fixp) 452 hist_flush(); 453 if(t) 454 { 455 execflags = ERRFLG; 456 /* sh -c simple-command may not have to fork */ 457 if(prof==0 && is_option(CFLAG) && (t->tretyp&COMMSK)==TCOM 458 && nextchar(fp)==0) 459 { 460 execflags |= 1; 461 } 462 execute(t,execflags); 463 } 464 } 465 } 466 467 /* 468 * if there is pending input, the prompt is not printed. 469 * prints PS2 if flag is zero otherwise PS3 470 */ 471 472 void chkpr(flag) 473 register int flag; 474 { 475 if(flag || (states&PROMPT) && standin->fstak==0) 476 { 477 /* if characters are in input buffer don't issue prompt */ 478 if((flag?stdin:input)->_cnt > 0) 479 return; 480 p_setout(stderr); 481 fputs(valup(flag?PS3NOD:PS2NOD),output); 482 } 483 } 484 485 486 487 /* prints out messages if files in list have been modified since last call */ 488 static void chkmail(files) 489 char *files; 490 { 491 register char *cp,*sp,*qp; 492 register char save; 493 struct stat statb; 494 if(*(cp=files) == 0) 495 return; 496 sp = cp; 497 do 498 { 499 /* skip to : or end of string saving first '?' */ 500 for(qp=0;*sp && *sp != ':';sp++) 501 if((*sp == '?' || *sp=='%') && qp == 0) 502 qp = sp; 503 save = *sp; 504 *sp = 0; 505 /* change '?' to end-of-string */ 506 if(qp) 507 *qp = 0; 508 gchain = NULL; 509 do 510 { 511 /* see if time has been modified since last checked 512 * and the access time <= the modification time 513 */ 514 if(stat(cp,&statb) >= 0 && statb.st_mtime >= mailtime 515 && statb.st_atime <= statb.st_mtime) 516 { 517 /* check for directory */ 518 if(gchain==NULL && (statb.st_mode&S_IFMT)==S_IFDIR) 519 { 520 /* generate list of directory entries */ 521 f_complete(cp,"/*"); 522 } 523 else 524 { 525 /* 526 * If the file has shrunk, 527 * or if the size is zero 528 * then don't print anything 529 */ 530 if(statb.st_size && 531 ( statb.st_ino != lastmail.st_ino 532 || statb.st_dev != lastmail.st_dev 533 || statb.st_size > lastmail.st_size)) 534 { 535 /* save and restore $_ */ 536 char *save = lastarg; 537 lastarg = cp; 538 p_str(mactry(qp==0?mailmsg:qp+1),NL); 539 lastarg = save; 540 } 541 lastmail = statb; 542 break; 543 } 544 } 545 if(gchain) 546 { 547 cp = gchain->argval; 548 gchain = gchain->argchn; 549 } 550 else 551 cp = NULL; 552 } 553 while(cp); 554 if(qp) 555 *qp = '?'; 556 *sp++ = save; 557 cp = sp; 558 } 559 while(save); 560 tdystak((STKPTR)0); 561 } 562 563 /* 564 * print the primary prompt 565 */ 566 567 static void pr_prompt(string) 568 register char *string; 569 { 570 register char *cp; 571 register int c; 572 static short cmdno; 573 #ifdef BSD 574 int mode; 575 #include <sys/ioctl.h> 576 mode = LFLUSHO; 577 ioctl(fileno(output),TIOCLBIC,&mode); 578 #endif /* BSD */ 579 p_flush(); 580 p_setout(stderr); 581 for(cp=string;c= *cp;cp++) 582 { 583 if(c==FC_CHAR) 584 { 585 /* look at next character */ 586 c = *++cp; 587 /* print out line number if not !! */ 588 if(c!= FC_CHAR) 589 p_num(fc_fix?fc_fix->fixind:++cmdno,0); 590 if(c==0) 591 break; 592 } 593 putc(c,output); 594 } 595 #if VSH || ESH 596 *output->_ptr = 0; 597 /* prompt flushed later */ 598 #else 599 p_flush(); 600 #endif 601 } 602 603 /* make sure PWD is set up correctly */ 604 605 /* 606 * Return the value of the PWD variable 607 * Invokes /bin/pwd if necessary 608 * If mode non-zero, then PWD must have same device/inode as dot 609 */ 610 611 char *getpwd(mode) 612 { 613 register char *sim; 614 register int count = 0; 615 while((sim=valup(PWDNOD))==NULL || *sim==0 || (mode&&!eq_inode(sim,dot))) 616 { 617 if(count++ > 0) 618 return(NULL); 619 if((sim=valup(HOME)) && *sim=='/' && eq_inode(sim,dot)) 620 { 621 fassign(PWDNOD,sim); 622 break; 623 } 624 else 625 { 626 #ifdef apollo 627 char buff[BUFSIZ+1]; 628 extern char *getcwd(); 629 sim=getcwd(buff+1,BUFSIZ); 630 if(buff[1]=='/' && buff[2]=='/') 631 { 632 buff[0]='/'; 633 buff[1] = 'n'; 634 sim = buff; 635 } 636 fassign(PWDNOD,sim); 637 #else 638 /* even restricted shells can pwd */ 639 optflag savflags = flags; 640 off_option(RSHFLG); 641 execexp(setpwd,(FILE*)0); 642 flags = savflags; 643 #endif /* apollo */ 644 } 645 } 646 return(sim); 647 } 648 649 650 /* 651 * This version of access checks against effective uid/gid 652 */ 653 654 access(name, mode) 655 register char *name; 656 register int mode; 657 { 658 struct stat statb; 659 if (stat(name, &statb) == 0) 660 { 661 if(mode == 0) 662 return(mode); 663 else if(euserid == 0) 664 { 665 if((statb.st_mode&S_IFMT) != S_IFREG || mode != 1) 666 return(0); 667 /* root needs execute permission for someone */ 668 mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); 669 } 670 else if(euserid == statb.st_uid) 671 mode <<= 6; 672 else if(egroupid == statb.st_gid) 673 mode <<= 3; 674 #ifdef BSD 675 # ifdef BSD_4_2 676 /* in BSD_4_2 you can be in several groups */ 677 else 678 { 679 int groups[NGROUPS]; 680 register int n; 681 n = getgroups(NGROUPS,groups); 682 while(--n >= 0) 683 { 684 if(groups[n] == statb.st_gid) 685 { 686 mode <<= 3; 687 break; 688 } 689 } 690 } 691 # endif /* BSD_4_2 */ 692 #endif /* BSD */ 693 if(statb.st_mode & mode) 694 return(0); 695 } 696 return(-1); 697 } 698 699