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
main(c,v)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
exfile(prof)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
chkpr(flag)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 */
chkmail(files)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
pr_prompt(string)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
getpwd(mode)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
access(name,mode)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