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 /* @(#)service.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 #include <errno.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include "flags.h" 27 #include "defs.h" 28 #include "sym.h" 29 #include "io.h" 30 #include "name.h" 31 #include "brkincr.h" 32 #include "mode.h" 33 #include "stak.h" 34 #include "shtype.h" 35 #include "builtins.h" 36 #include "jobs.h" 37 38 #ifndef BSD_4_2 39 # ifdef BSD 40 # define fcntl(a,b,c) dup2(a,b) 41 # endif /* BSD */ 42 #endif /* BSD_4_2 */ 43 44 FILE *pathopen(); 45 char *getpath(); 46 char *fullname(); 47 int initio(); 48 void exfunct(); 49 #ifndef JOBS 50 void postclr(); 51 int post(); 52 void await(); 53 #endif /* JOBS */ 54 55 extern void arg_clear(); 56 extern DOLPTR arg_new(); 57 extern void arg_set(); 58 extern void arg_reset(); 59 extern char *catpath(); 60 extern FILE *chkopen(); 61 extern STKPTR cpystak(); 62 extern FILE *create(); 63 extern void exitsh(); 64 extern void failed(); 65 extern NAMPTR findnod(); 66 extern void free(); 67 extern void gscan_some(); 68 extern char *itos(); 69 extern STKPTR locstak(); 70 extern void mac_subst(); 71 extern char *mactrim(); 72 extern void mem_scope(); 73 extern void mem_unscope(); 74 extern char *movstr(); 75 extern void p_setout(); 76 extern void p_flush(); 77 extern void p_prp(); 78 #ifdef JOBS 79 extern void postclr(); 80 #endif /* JOBS */ 81 extern char *qvalup(); 82 extern char *realias(); 83 extern void rmlocal(); 84 extern char **setenv(); 85 extern void setlist(); 86 extern char *simple(); 87 extern char *strchr(); 88 extern FILE *tmp_open(); 89 extern void trim(); 90 extern char *valup(); 91 92 static char *prune(); 93 #ifndef VFORK 94 static void exscript(); 95 #endif /* VFORK */ 96 static char *execs(); 97 98 #define LOBYTE 0377 99 #ifdef u370 100 #define MAXP 75 101 #else 102 #define MAXP 32 103 #endif /* u370 */ 104 #define MAXDEPTH (32*sizeof(int)) /* maximum levels of recursion */ 105 106 107 #ifndef JOBS 108 static int maxpost; /* highest number running process */ 109 static int numpost; /* number of running processes */ 110 111 /* for processes to be waited for */ 112 static struct process pwlist[MAXP]; 113 #endif /* JOBS */ 114 static char *xecmsg; 115 static char **xecenv; 116 #ifdef VFORK 117 NAMPTR lookup(); 118 #endif /* VFORK */ 119 120 121 /* 122 * service routines for `execute' 123 * flag > 0 if files are to be restored 124 * flag < 0 if files are to be closed on exec 125 */ 126 127 int initio(iop,flag) 128 IOPTR iop; 129 { 130 register char *ion; 131 register int iof; 132 register FILE *fd; 133 char fname[TMPSIZ]; 134 int fn; 135 int mark = MARK; 136 int indx = topfd; 137 if(flag<0) 138 mark = 0; 139 for(;iop;iop=iop->ionxt) 140 { 141 iof=iop->iofile; 142 if(flag>0) 143 { 144 /* save current file descriptor */ 145 savefd(iof&IOUFD,indx); 146 } 147 ion=mactrim(iop->ioname,1); 148 if(*ion && is_option(NOEXEC)==0) 149 { 150 if(iof&IODOC) 151 { 152 fd = tmp_open(fname); 153 unlink(fname); 154 mac_subst(chkopen(ion),fd); 155 } 156 else if(iof&IOMOV) 157 { 158 if(eq(minus,ion)) 159 { 160 fclose(file_fd(iof&IOUFD)); 161 fd = NULL; 162 } 163 else if(((fn=atoi(ion))>=USERIO) || 164 (fd=fdopen(dup(fn),(iof&IOPUT)?"w+":"r")) == NULL) 165 failed(ion,badfile); 166 } 167 else if((iof&IOPUT)==0) 168 { 169 fd=chkopen(ion); 170 } 171 else if(is_option(RSHFLG)) 172 failed(ion,restricted); 173 else if((iof&IOAPP) == 0 || (fd=fdopen(open(ion,1),"a"))==NULL) 174 { 175 if((fd=create(ion)) == NULL) 176 failed(ion,badcreate); 177 } 178 if(fd!=NULL) 179 frenumber(fd,(iof&IOUFD)|mark); 180 } 181 } 182 return(indx); 183 } 184 185 /* 186 * given <s> return a colon separated list of directories to search on the stack 187 * This routine adds names to the tracked alias list, if possible, and returns 188 * a reduced path string for tracked aliases 189 */ 190 191 char *getpath(s) 192 char *s; 193 { 194 register char *path; 195 register NAMPTR np; 196 if(strchr(s,'/')) 197 return(nullstr); 198 path = qvalup(PATHNOD); 199 if(path==NULL) 200 path = defpath; 201 path = cpystak(path); 202 /* track alias if possible */ 203 np=findnod(s,alias,CHK_FOR); 204 if(np==NULL && is_option(HASHALL) && (np=findnod(s,alias,CHK_FOR|1))) 205 attrib(np, N_EXPORT|NO_ALIAS|T_FLAG); 206 if(np && attest(np,T_FLAG)) 207 { 208 if(attest(np,NO_ALIAS)) 209 { 210 /* don't bother to look up alias if forked */ 211 if(states&FORKED) 212 return(path); 213 /* if realias fails then a search won't find anything */ 214 if(realias(np)==NIL) 215 return(nullstr); 216 } 217 /* tracked alias use reduced path */ 218 path = prune(path,valup(np)); 219 } 220 return(path); 221 } 222 223 FILE *pathopen(name) 224 register char *name; 225 { 226 register char *path; 227 register int n; 228 struct stat statb; 229 if(strchr(name,'/')) 230 { 231 if(is_option(RSHFLG)) 232 failed(name, restricted); 233 else 234 path = nullstr; 235 } 236 else 237 { 238 path = qvalup(PATHNOD); 239 if(path==NULL) 240 path = defpath; 241 path = cpystak(path); 242 } 243 do 244 { 245 path=catpath(path,name); 246 if((n = open(curstak(),0)) >= 0) 247 { 248 if(fstat(n,&statb)<0 || (statb.st_mode&S_IFMT)!=S_IFREG) 249 { 250 close(n); 251 n = -1; 252 } 253 } 254 } 255 while( n<0 && path); 256 return(fdopen(n,"r")); 257 } 258 259 260 /* 261 * do a path search and find the full pathname of file name 262 * if name is not a simple name, then name is a tracked alias 263 */ 264 265 char *fullname(fname) 266 char *fname; 267 { 268 register char *name; 269 register int f; 270 register char *path; 271 if(*fname!='/') 272 name=fname; 273 else 274 name = simple(fname); 275 path = getpath(name); 276 do 277 { 278 path=catpath(path,name); 279 if((f=access(curstak(),1))>=0) 280 { 281 if(!ftype(curstak(),S_IFMT,S_IFREG)) 282 f = -1; 283 } 284 } 285 while(f<0 && path); 286 return(f<0?0:curstak()); 287 } 288 289 char *catpath(path,name) 290 register char *path; 291 char * name; 292 { 293 /* leaves result on top of stack */ 294 register char *scanp = path; 295 register char *argp = locstak(); 296 while(*scanp && *scanp!=':') 297 *argp++ = *scanp++; 298 if(scanp!=path) 299 { 300 *argp++= '/'; 301 /* position past ":" unless a trailing colon after pathname */ 302 if(*scanp && *++scanp==0) 303 scanp--; 304 } 305 else 306 while(*scanp == ':') 307 scanp++; 308 path=(*scanp ? scanp : 0); 309 scanp=name; 310 while((*argp++ = *scanp++)); 311 staktop = argp; 312 return(path); 313 } 314 315 316 void execa(at,local) 317 char * at[]; 318 ARGPTR local; /* local environment modification */ 319 { 320 register char *path = nullstr; 321 register char **t = at; 322 if(is_option(NOEXEC)==0) 323 { 324 xecmsg=notfound; 325 #ifdef VFORK 326 if(local) 327 mem_scope(local); 328 xecenv=setenv(); 329 #else 330 setlist(local,N_EXPORT); 331 xecenv=setenv(); 332 #endif /* VFORK */ 333 if(strchr(t[0],'/')) 334 { 335 /* name containing / not allowed for restricted shell */ 336 if(is_option(RSHFLG)) 337 failed(t[0],restricted); 338 } 339 else 340 path=getpath(*t); 341 #ifdef VFORK 342 if(local) 343 mem_unscope(); 344 #endif /* VFORK */ 345 /* insert _= onto stack in front of pathname */ 346 *--xecenv = stakbot; 347 *stakbot++ = '_'; 348 *stakbot++ = '='; 349 while(path=execs(path,t)); 350 failed(*t,xecmsg); 351 } 352 } 353 354 /* 355 * This routine constructs a short path consisting of all 356 * Relative directories up to the directory of fullname <name> 357 */ 358 static char *prune(path,fullname) 359 register char *path; 360 char *fullname; 361 { 362 register char *p = path; 363 register char *s; 364 int n = 1; 365 char *base; 366 char *inpath = path; 367 if(fullname==NULL || *fullname != '/' || *path==0) 368 return(path); 369 base = simple(fullname); 370 do 371 { 372 /* a null path means current directory */ 373 if(*path == ':') 374 { 375 *p++ = ':'; 376 path++; 377 continue; 378 } 379 s = path; 380 path=catpath(path,base); 381 if(*s != '/' || (n=strcmp(curstak(),fullname))==0) 382 { 383 /* position p past end of path */ 384 while(*s && *s!=':') 385 *p++ = *s++; 386 if(n==0) 387 { 388 *p = 0; 389 return(inpath); 390 } 391 *p++ = ':'; 392 } 393 } 394 while(path); 395 /* if there is no match just return path */ 396 path = qvalup(PATHNOD); 397 if(path==NULL) 398 path = defpath; 399 strcpy(inpath,path); 400 return(inpath); 401 } 402 403 #ifdef XENIX 404 /* 405 * This code takes care of a bug in the XENIX exec routine 406 * Contributed by Pat Wood 407 */ 408 static ex_xenix(file) 409 char *file; 410 { 411 struct stat stats; 412 register int fd; 413 unsigned short magic; 414 if((fd = open(file,0)) == -1) /* can't read, so can't be shell prog */ 415 return(1); 416 read(fd, &magic, sizeof(magic)); 417 if(magic == 01006) /* magic for xenix executable */ 418 { 419 close(fd); 420 return(1); 421 } 422 fstat(fd, &stats); 423 close(fd); 424 errno = ENOEXEC; 425 if(!geteuid()) 426 { 427 if(!(stats.st_mode & 0111)) 428 errno = EACCES; 429 return(0); 430 } 431 if((geteuid() == stats.st_uid)) 432 { 433 if(!(stats.st_mode & 0100)) 434 errno = EACCES; 435 return(0); 436 } 437 if((getegid() == stats.st_gid)) 438 { 439 if(!(stats.st_mode & 0010)) 440 errno = EACCES; 441 return(0); 442 } 443 if(!(stats.st_mode & 0001)) 444 errno = EACCES; 445 return(0); 446 } 447 #endif /* XENIX */ 448 449 static char *execs(ap,t) 450 char * ap; 451 register char **t; 452 { 453 register char *p, *prefix; 454 prefix=catpath(ap,t[0]); 455 trim(p=curstak()); 456 p_flush(); 457 if(trapnote&SIGSET) 458 exitsh(SIGFAIL); 459 #ifdef XENIX 460 if(ex_xenix(p)) 461 #endif /* XENIX */ 462 execve(p, &t[0] ,xecenv); 463 switch(errno) 464 { 465 case ENOEXEC: 466 #ifdef VFORK 467 { 468 /* this code handles the !# interpreter name convention */ 469 char iname[256]; 470 #ifdef SUID_EXEC 471 /* check if file cannot open for read or script is setuid/setgid */ 472 static char name[] = "/tmp/euidXXXXXX"; 473 register int n; 474 register int euserid; 475 struct stat statb; 476 if((n=open(p,0)) >= 0) 477 { 478 if(fstat(n,&statb)==0) 479 { 480 if((statb.st_mode&(S_ISUID|S_ISGID))==0) 481 goto openok; 482 } 483 close(n); 484 } 485 if((euserid=geteuid()) != userid) 486 { 487 strcpy(name+9,itos(getpid())); 488 /* create a suid open file with owner equal effective uid */ 489 if((n=creat(name,04100)) < 0) 490 goto fail; 491 unlink(name); 492 /* make sure that file has right owner */ 493 if(fstat(n,&statb)<0 || statb.st_uid != euserid) 494 goto fail; 495 if(n!=10) 496 { 497 close(10); 498 fcntl(n,0,10); 499 close(n); 500 } 501 } 502 *--t = p; 503 execve(suid_exec,t,xecenv); 504 fail: 505 failed(p, badopen); 506 openok: 507 close(n); 508 #endif SUID_EXEC 509 /* get name returns the interpreter name */ 510 if(get_shell(p, iname)<0) 511 failed(p, badexec); 512 t--; 513 t[0] = iname; 514 execve(iname, t, xecenv); 515 if(access(iname,0)==0) 516 xecmsg=badexec; 517 failed(iname, xecmsg); 518 } 519 #else 520 exscript(p,t); 521 #endif /* VFORK */ 522 523 case ENOMEM: 524 failed(p,toobig); 525 526 case E2BIG: 527 failed(p,arglist); 528 529 case ETXTBSY: 530 failed(p,txtbsy); 531 532 default: 533 if(access(p,0)==0) 534 xecmsg=badexec; 535 case ENOENT: 536 return(prefix); 537 } 538 } 539 540 /* 541 * File is executable but not machine code. 542 * Assume file is a Shell script and execute it. 543 */ 544 545 546 static void exscript(p,t) 547 register char *p; 548 register char *t[]; 549 { 550 char *savet; 551 flags=0; 552 states = 0; 553 comdiv=0; 554 ioset=0; 555 arg_clear(); /* remove open files and for loop junk */ 556 postclr(); 557 if(fileno(input)) 558 fclose(input); 559 p_flush(); 560 standout= stdout; 561 setbuf(stdin,(char*)_sibuf); 562 #ifdef SUID_EXEC 563 /* check if file cannot open for read or script is setuid/setgid */ 564 { 565 static char name[] = "/tmp/euidXXXXXX"; 566 register int n; 567 register int euserid; 568 struct stat statb; 569 if((n=open(p,0)) >= 0) 570 { 571 if(fstat(n,&statb)==0) 572 { 573 if((statb.st_mode&(S_ISUID|S_ISGID))==0) 574 goto openok; 575 } 576 close(n); 577 } 578 if((euserid=geteuid()) != userid) 579 { 580 strcpy(name+9,itos(getpid())); 581 /* create a suid open file with owner equal effective uid */ 582 if((n=creat(name,04100)) < 0) 583 goto fail; 584 unlink(name); 585 /* make sure that file has right owner */ 586 if(fstat(n,&statb)<0 || statb.st_uid != euserid) 587 goto fail; 588 if(n!=10) 589 { 590 close(10); 591 fcntl(n,0,10); 592 close(n); 593 } 594 } 595 savet = *--t; 596 *t = p; 597 execve(suid_exec,t,xecenv); 598 fail: 599 /* 600 * The following code is just for compatibility 601 * It should be replaced with the line failed(p,badexec); 602 */ 603 n = open(p,0); 604 if(n < 0) 605 failed(p, badopen); 606 *t++ = savet; 607 close(10); 608 609 openok: 610 input = fdopen(n,"r"); 611 } 612 #else 613 input = chkopen(p); 614 #endif /* SUID_EXEC */ 615 #ifdef ACCT 616 preacct(p) ; /* reset accounting */ 617 #endif /* ACCT */ 618 gscan_some(rmlocal,namep,N_EXPORT,0); /* remove local variables*/ 619 gscan_some(rmlocal,alias,N_EXPORT,0); /* remove local aliases*/ 620 gscan_some(rmlocal,prnames,N_EXPORT,0); /* remove local functions*/ 621 if(attest(IFSNOD,N_EXPORT)==0) 622 assign(IFSNOD,sptbnl); 623 /* set up new args */ 624 arg_set(t); 625 longjmp(subshell,1); 626 } 627 628 /* 629 * The following routine is used to execute shell functions and command subs 630 * when com!=NULL $* is saved and restored 631 */ 632 633 void exfunct(t,com,execflg,envlist) 634 TREPTR t; 635 register char *com[]; 636 register unsigned execflg; 637 ARGPTR envlist; 638 { 639 /* execute user defined function */ 640 register char *trap; 641 jmp_buf retbuf; 642 jmp_buf *savreturn = freturn; 643 DOLPTR argsav=0; 644 int mode; 645 DOLPTR savargfor; 646 char *savtrap0 = trapcom[0]; 647 char *savtrap1 = trapcom[MAXTRAP]; 648 SHFILE savstandin; 649 struct State savst; 650 savst = st; 651 loopcnt = 0; 652 if(com) 653 { 654 mem_scope(envlist); 655 if(execflg&EXECPR) 656 on_option(EXECPR); 657 else 658 off_option(EXECPR); 659 execflg &= ~EXECPR; 660 cmdadr = com[0]; 661 trapcom[MAXTRAP] = 0; 662 argsav = arg_new(com,&savargfor); 663 } 664 freturn = (jmp_buf*)retbuf; 665 if((mode=setjmp(retbuf)) == 0) 666 { 667 states |= FUNCTION; 668 if(fn_depth++ > MAXDEPTH) 669 longjmp(*freturn,2); 670 else 671 execute(t,execflg); 672 } 673 fn_depth--; 674 freturn = savreturn; 675 if(com) 676 { 677 mem_unscope(); 678 arg_reset(argsav,savargfor); 679 trapcom[MAXTRAP] = savtrap1; 680 trapnote = 0; 681 } 682 savstandin = standin; 683 st = savst; 684 while((savstandin != standin) && pop(0)); 685 if(mode == 2) 686 { 687 if(fn_depth==0) 688 failed(com[0],recursive); 689 else 690 longjmp(*freturn,2); 691 } 692 if(com && (trap=trapcom[0]) && (savtrap0!=trap)) 693 { 694 trapcom[0] = savtrap0; 695 execexp(trap,(FILE*)0); 696 free(trap); 697 } 698 } 699 700 #ifndef JOBS 701 /* 702 * These routines have been moved to jobs.c when compiling with JOBS option 703 */ 704 705 /* 706 * Initialize the process posting array 707 */ 708 void postclr() 709 { 710 register struct process *pw = pwlist; 711 while(pw < &pwlist[maxpost]) 712 (pw++)->p_pid = 0; 713 numpost=0; 714 maxpost=0; 715 } 716 717 int post(pcsid) 718 int pcsid; 719 { 720 register struct process *pw = pwlist; 721 if(pcsid) 722 { 723 while(pw->p_pid) 724 pw++; 725 if(numpost >= MAXP-1) 726 pw--; 727 else 728 numpost++; 729 if(numpost > maxpost) 730 maxpost = numpost; 731 pw->p_pid = pcsid; 732 if(numpost >= MAXP-1) 733 await(0,0); 734 return(pw-pwlist); 735 } 736 return(-1); 737 } 738 739 void await(i, bckg) 740 int i, bckg; 741 { 742 int rc=0, wx=0; 743 int w; 744 post(i); 745 while(numpost) 746 { 747 register int p; 748 register int sig; 749 int w_hi; 750 int found = 0; 751 { 752 register struct process *pw=pwlist; 753 errno = 0; 754 p=wait(&w); 755 if((p== -1) && bckg && errno==EINTR) 756 break; 757 while(pw <= &pwlist[maxpost]) 758 { 759 if(pw->p_pid==p) 760 { 761 if(p==cpid) 762 { 763 cpid = 0; 764 cpipe[1] = NULL; 765 } 766 pw->p_pid=0; 767 numpost--; 768 found++; 769 } 770 else 771 pw++; 772 } 773 } 774 if(p == -1) 775 { 776 if(bckg) 777 { 778 register struct process *pw =pwlist; 779 while(pw <= &pwlist[maxpost] && i != pw->p_pid) 780 pw++; 781 if(i == pw->p_pid) 782 { 783 pw->p_pid = 0; 784 numpost--; 785 } 786 } 787 continue; 788 } 789 w_hi = (w>>8)&LOBYTE; 790 if(sig = w&0177) 791 { 792 p_setout(stderr); 793 if(sig == 0177 /* ptrace! return */) 794 { 795 fputs(ptrace,output); 796 sig = w_hi; 797 } 798 #ifdef apollo 799 if(*sysmsg[sig]) 800 #else 801 if(sysmsg[sig]) 802 #endif /* apollo */ 803 { 804 if(i!=p || (states&PROMPT)==0) 805 { 806 p_prp(itos(p),SP); 807 } 808 fputs(sysmsg[sig],output); 809 if(w&HIGHBIT) 810 fputs(coredump,output); 811 } 812 newline(); 813 } 814 wx |= w; 815 if(p == i) 816 { 817 rc = (sig ? sig|SIGFLG : w_hi); 818 break; 819 } 820 } 821 exitval=rc; 822 exitset(); 823 } 824 #endif /* JOBS */ 825 826 #ifdef ACCT 827 #include <acctdef.h> 828 #include <sys/acct.h> 829 #include <sys/times.h> 830 831 static int compress(); 832 833 struct acct sabuf; 834 struct tms buffer; 835 extern long times(); 836 static long before; 837 static char *SHACCT; /* 0 environment variable SHACCT not set so never acct 838 ptr to SHACCT value if set, so acct if shell procedure*/ 839 static shaccton; /* 0 implies do not write record on exit 840 1 implies write acct record on exit 841 */ 842 /* 843 * initialize accounting, i.e., see if SHACCT variable set 844 */ 845 void initacct() 846 { 847 848 SHACCT = valup(ACCTNOD); 849 } 850 /* 851 * suspend accounting unitl turned on by preacct() 852 */ 853 void suspacct() 854 { 855 shaccton=0; 856 } 857 858 int preacct(cmdname) 859 char *cmdname; 860 { 861 char * strncpy(); 862 if(SHACCT) 863 { 864 sabuf.ac_btime = time((long *)0); 865 before = times(&buffer); 866 sabuf.ac_uid = getuid(); 867 sabuf.ac_gid = getgid(); 868 strncpy(sabuf.ac_comm, (char*)simple(cmdname), 869 sizeof(sabuf.ac_comm)); 870 shaccton = 1; 871 } 872 } 873 #include <fcntl.h> 874 void doacct() 875 { 876 int fd; 877 long after; 878 879 if(shaccton) 880 { 881 after = times(&buffer); 882 sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime); 883 sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime); 884 sabuf.ac_etime = compress( after - before); 885 fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,0666); 886 write(fd, &sabuf, sizeof( sabuf )); 887 close( fd); 888 } 889 } 890 891 /* 892 * Produce a pseudo-floating point representation 893 * with 3 bits base-8 exponent, 13 bits fraction. 894 */ 895 static int compress(t) 896 register time_t t; 897 { 898 register int exp = 0, rund = 0; 899 900 while (t >= 8192) 901 { 902 exp++; 903 rund = t&04; 904 t >>= 3; 905 } 906 if (rund) 907 { 908 t++; 909 if (t >= 8192) 910 { 911 t >>= 3; 912 exp++; 913 } 914 } 915 return((exp<<13) + t); 916 } 917 #endif /* ACCT */ 918 919