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
initio(iop,flag)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
getpath(s)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
pathopen(name)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
fullname(fname)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
catpath(path,name)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
execa(at,local)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 */
prune(path,fullname)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 */
ex_xenix(file)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
execs(ap,t)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
exscript(p,t)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
exfunct(t,com,execflg,envlist)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 */
postclr()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
post(pcsid)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
await(i,bckg)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 */
initacct()845 void initacct()
846 {
847
848 SHACCT = valup(ACCTNOD);
849 }
850 /*
851 * suspend accounting unitl turned on by preacct()
852 */
suspacct()853 void suspacct()
854 {
855 shaccton=0;
856 }
857
preacct(cmdname)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>
doacct()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 */
compress(t)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