1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
25 * Rewritten By David Korn
36 #include "variables.h"
44 #include "FEATURE/time"
45 #include "FEATURE/pstat"
46 #include "FEATURE/execargs"
47 #include "FEATURE/externs"
54 /* These routines are referenced by this module */
55 static void exfile(Shell_t
*, Sfio_t
*,int);
56 static void chkmail(Shell_t
*shp
, char*);
57 #if defined(_lib_fork) && !defined(_NEXT_SOURCE)
58 static void fixargs(char**,int);
64 extern char **environ
;
67 static struct stat lastmail
;
68 static time_t mailtime
;
69 static char beenhere
= 0;
72 void clearsigmask(register int sig
)
75 if(sigvec(sig
,NIL(struct sigvec
*),&vec
)>=0 && vec
.sv_mask
)
78 sigvec(sig
,&vec
,NIL(struct sigvec
*));
81 #endif /* _lib_sigvec */
83 #ifdef _lib_fts_notify
85 /* check for interrupts during tree walks */
86 static int fts_sigcheck(FTS
* fp
, FTSENT
* ep
, void* context
)
88 Shell_t
*shp
= (Shell_t
*)context
;
91 if(shp
->trapnote
&SH_SIGSET
)
98 #endif /* _lib_fts_notify */
101 #define PATHCOMP NIL(Pathcomp_t*)
107 * search for file and exfile() it if it exists
108 * 1 returned if file found, 0 otherwise
111 int sh_source(Shell_t
*shp
, Sfio_t
*iop
, const char *file
)
117 if (!file
|| !*file
|| (fd
= path_open(file
, PATHCOMP
)) < 0)
119 REGRESS(source
, "sh_source", ("%s:ENOENT", file
));
123 nid
= error_info
.id
= strdup(file
);
124 shp
->st
.filename
= path_fullname(stakptr(PATH_OFFSET
));
125 REGRESS(source
, "sh_source", ("%s", file
));
126 exfile(shp
, iop
, fd
);
133 #define REMOTE(m) (S_ISSOCK(m)||!(m))
135 #define REMOTE(m) !(m)
138 int sh_main(int ac
, char *av
[], Shinit_f userinit
)
142 register Sfio_t
*iop
;
143 register Shell_t
*shp
;
145 int i
, rshflag
; /* set for restricted shell */
148 /* This is to clear mask that may be left on by rlogin */
149 clearsigmask(SIGALRM
);
150 clearsigmask(SIGHUP
);
151 clearsigmask(SIGCHLD
);
152 #endif /* _lib_sigvec */
154 _NutConf(_NC_SET_SUFFIXED_SEARCHING
, 1);
157 shp
= sh_init(ac
,av
,userinit
);
159 if(rshflag
=sh_isoption(SH_RESTRICTED
))
160 sh_offoption(SH_RESTRICTED
);
161 #ifdef _lib_fts_notify
162 fts_notify(fts_sigcheck
,(void*)shp
);
163 #endif /* _lib_fts_notify */
164 if(sigsetjmp(*((sigjmp_buf
*)shp
->jmpbuffer
),0))
166 /* begin script execution here */
167 sh_reinit((char**)0);
169 shp
->fn_depth
= shp
->dot_depth
= 0;
170 command
= error_info
.id
;
171 /* set pidname '$$' */
173 srand(shp
->pid
&0x7fff);
174 shp
->ppid
= getppid();
175 if(nv_isnull(PS4NOD
))
176 nv_putval(PS4NOD
,e_traceprompt
,NV_RDONLY
);
180 sh_onoption(SH_BRACEEXPAND
);
184 sh_onstate(SH_PROFILE
);
185 ((Lex_t
*)shp
->lex_context
)->nonstandard
= 0;
188 if(shp
->login_sh
>= 2)
189 sh_onoption(SH_LOGIN_SHELL
);
190 /* decide whether shell is interactive */
191 if(!sh_isoption(SH_INTERACTIVE
) && !sh_isoption(SH_TFLAG
) && !sh_isoption(SH_CFLAG
) &&
192 sh_isoption(SH_SFLAG
) && tty_check(0) && tty_check(ERRIO
))
193 sh_onoption(SH_INTERACTIVE
);
194 if(sh_isoption(SH_INTERACTIVE
))
196 sh_onoption(SH_BGNICE
);
199 if(!sh_isoption(SH_RC
) && (sh_isoption(SH_BASH
) && !sh_isoption(SH_POSIX
)
201 || !fstat(0, &statb
) && REMOTE(statb
.st_mode
)
205 for(i
=0; i
<elementsof(shp
->offoptions
.v
); i
++)
206 shp
->options
.v
[i
] &= ~shp
->offoptions
.v
[i
];
207 if(sh_isoption(SH_INTERACTIVE
))
210 signal(SIGXCPU
,SIG_DFL
);
213 signal(SIGXFSZ
,SIG_DFL
);
215 sh_onoption(SH_MONITOR
);
217 job_init(shp
,sh_isoption(SH_LOGIN_SHELL
));
218 if(sh_isoption(SH_LOGIN_SHELL
))
221 sh_source(shp
, iop
, e_sysprofile
);
222 if(!sh_isoption(SH_NOUSRPROFILE
) && !sh_isoption(SH_PRIVILEGED
))
224 char **files
= shp
->login_files
;
225 while ((name
= *files
++) && !sh_source(shp
, iop
, sh_mactry(shp
,name
)));
228 /* make sure PWD is set up correctly */
230 if(!sh_isoption(SH_NOEXEC
))
232 if(!sh_isoption(SH_NOUSRPROFILE
) && !sh_isoption(SH_PRIVILEGED
) && sh_isoption(SH_RC
))
235 if(sh_isoption(SH_BASH
) && !sh_isoption(SH_POSIX
))
238 sh_source(shp
, iop
, e_bash_sysrc
);
240 sh_source(shp
, iop
, shp
->rcfile
? shp
->rcfile
: sh_mactry(shp
,(char*)e_bash_rc
));
245 if(name
= sh_mactry(shp
,nv_getval(ENVNOD
)))
246 name
= *name
? strdup(name
) : (char*)0;
248 if(!strmatch(name
, "?(.)/./*"))
249 sh_source(shp
, iop
, e_sysrc
);
253 sh_source(shp
, iop
, name
);
258 else if(sh_isoption(SH_INTERACTIVE
) && sh_isoption(SH_PRIVILEGED
))
259 sh_source(shp
, iop
, e_suidprofile
);
261 shp
->st
.cmdname
= error_info
.id
= command
;
262 sh_offstate(SH_PROFILE
);
264 sh_onoption(SH_RESTRICTED
);
265 /* open input file if specified */
269 iop
= sfnew(NIL(Sfio_t
*),shp
->comdiv
,strlen(shp
->comdiv
),0,SF_STRING
|SF_READ
);
273 name
= error_info
.id
;
274 error_info
.id
= shp
->shname
;
275 if(sh_isoption(SH_SFLAG
))
280 /* open stream should have been passed into shell */
281 if(strmatch(name
,e_devfdNN
))
287 fdin
= (int)strtol(name
+8, (char**)0, 10);
288 if(fstat(fdin
,&statb
)<0)
289 errormsg(SH_DICT
,ERROR_system(1),e_open
,name
);
292 * try to undo effect of solaris 2.5+
293 * change for argv for setuid scripts
295 if(((type
= sh_type(cp
= av
[0])) & SH_TYPE_SH
) && (!(name
= nv_getval(L_ARGNOD
)) || !((type
= sh_type(cp
= name
)) & SH_TYPE_SH
)))
297 av
[0] = (type
& SH_TYPE_LOGIN
) ? cp
: path_basename(cp
);
298 /* exec to change $0 for ps */
299 execv(pathshell(),av
);
301 shp
->st
.dolv
[0] = av
[0];
302 fixargs(shp
->st
.dolv
,1);
306 sh_offoption(SH_VERBOSE
);
307 sh_offoption(SH_XTRACE
);
312 if((fdin
=sh_open(name
,O_RDONLY
,0))>=0 &&(fstat(fdin
,&statb
)<0 || S_ISDIR(statb
.st_mode
)))
319 shp
->st
.filename
= path_fullname(name
);
321 if(fdin
< 0 && !strchr(name
,'/'))
324 if(path_absolute(name
,NIL(Pathcomp_t
*)))
325 sp
= stakptr(PATH_OFFSET
);
327 sp
= path_absolute(name
,NIL(char*));
331 if((fdin
=sh_open(sp
,O_RDONLY
,0))>=0)
332 shp
->st
.filename
= path_fullname(sp
);
339 error_info
.id
= av
[0];
340 if(sp
|| errno
!=ENOENT
)
341 errormsg(SH_DICT
,ERROR_system(ERROR_NOEXEC
),e_open
,name
);
342 /* try sh -c 'name "$@"' */
343 sh_onoption(SH_CFLAG
);
344 shp
->comdiv
= (char*)malloc(strlen(name
)+7);
345 name
= strcopy(shp
->comdiv
,name
);
347 strcopy(name
," \"$@\"");
351 fdin
= sh_iomovefd(fdin
);
353 shp
->readscript
= shp
->shname
;
355 error_info
.id
= name
;
360 sh_accbegin(error_info
.id
);
361 #endif /* SHOPT_ACCT */
367 fixargs(shp
->st
.dolv
,1);
369 if(sh_isoption(SH_INTERACTIVE
))
370 sh_onstate(SH_INTERACTIVE
);
371 nv_putval(IFSNOD
,(char*)e_sptbnl
,NV_RDONLY
);
372 exfile(shp
,iop
,fdin
);
379 * iop is not null when the input is a string
380 * fdin is the input file descriptor
383 static void exfile(register Shell_t
*shp
, register Sfio_t
*iop
,register int fno
)
387 int maxtry
=IOMAXTRY
, tdone
=0, execflags
;
390 sh_pushcontext(&buff
,SH_JMPERREXIT
);
391 /* open input stream */
392 nv_putval(SH_PATHNAMENOD
, shp
->st
.filename
,NV_NOFREE
);
398 if(fno
< 10 && ((r
=sh_fcntl(fno
,F_DUPFD
,10))>=10))
400 shp
->fdstatus
[r
] = shp
->fdstatus
[fno
];
404 fcntl(fno
,F_SETFD
,FD_CLOEXEC
);
405 shp
->fdstatus
[fno
] |= IOCLEX
;
406 iop
= sh_iostream((void*)shp
,fno
);
414 if(sh_isstate(SH_INTERACTIVE
))
416 if(nv_isnull(PS1NOD
))
417 nv_putval(PS1NOD
,(shp
->euserid
?e_stdprompt
:e_supprompt
),NV_RDONLY
);
419 if(sh_histinit((void*)shp
))
420 sh_onoption(SH_HISTORY
);
424 if(!sh_isstate(SH_PROFILE
))
426 buff
.mode
= SH_JMPEXIT
;
427 sh_onoption(SH_TRACKALL
);
428 sh_offoption(SH_MONITOR
);
430 sh_offstate(SH_INTERACTIVE
);
431 sh_offstate(SH_MONITOR
);
432 sh_offstate(SH_HISTORY
);
433 sh_offoption(SH_HISTORY
);
435 states
= sh_getstate();
436 jmpval
= sigsetjmp(buff
.buff
,0);
440 sh_iorestore((void*)shp
,0,jmpval
);
441 hist_flush(shp
->hist_ptr
);
442 sfsync(shp
->outpool
);
443 shp
->st
.execbrk
= shp
->st
.breakcnt
= 0;
444 /* check for return from profile or env file */
445 if(sh_isstate(SH_PROFILE
) && (jmpval
==SH_JMPFUN
|| jmpval
==SH_JMPEXIT
))
450 if(!sh_isoption(SH_INTERACTIVE
) || sh_isstate(SH_FORKED
) || (jmpval
> SH_JMPERREXIT
&& job_close(shp
) >=0))
452 sh_offstate(SH_INTERACTIVE
);
453 sh_offstate(SH_MONITOR
);
456 /* skip over remaining input */
461 while(top
=sfstack(iop
,SF_POPSTACK
))
464 /* make sure that we own the terminal */
466 tcsetpgrp(job
.fd
,shp
->pid
);
469 /* error return here */
472 shp
->st
.optindex
= 1;
487 stakset(NIL(char*),0);
489 sh_offstate(SH_STOPOK
);
490 sh_offstate(SH_ERREXIT
);
491 sh_offstate(SH_VERBOSE
);
492 sh_offstate(SH_TIMING
);
493 sh_offstate(SH_GRACE
);
494 sh_offstate(SH_TTYWAIT
);
495 if(sh_isoption(SH_VERBOSE
))
496 sh_onstate(SH_VERBOSE
);
497 sh_onstate(SH_ERREXIT
);
498 /* -eim flags don't apply to profiles */
499 if(sh_isstate(SH_PROFILE
))
501 sh_offstate(SH_INTERACTIVE
);
502 sh_offstate(SH_ERREXIT
);
503 sh_offstate(SH_MONITOR
);
505 if(sh_isstate(SH_INTERACTIVE
) && !tdone
)
509 sh_offstate(SH_MONITOR
);
510 if(sh_isoption(SH_MONITOR
))
511 sh_onstate(SH_MONITOR
);
514 job_walk(sfstderr
,job_list
,JOB_NFLAG
,(char**)0);
518 if((mail
=nv_getval(MAILPNOD
)) || (mail
=nv_getval(MAILNOD
)))
521 if ((curtime
- mailtime
) >= sh_mailchk
)
528 hist_eof(shp
->hist_ptr
);
529 /* sets timeout for command entry */
530 shp
->timeout
= shp
->st
.tmout
;
532 if(shp
->timeout
<= 0 || shp
->timeout
> SHOPT_TIMEOUT
)
533 shp
->timeout
= SHOPT_TIMEOUT
;
534 #endif /* SHOPT_TIMEOUT */
539 if(buff
.mode
== SH_JMPEXIT
)
541 buff
.mode
= SH_JMPERREXIT
;
543 errormsg(SH_DICT
,ERROR_warn(0),"%d: mode changed to JMP_EXIT",getpid());
548 if(tdone
|| !sfreserve(iop
,0,0))
551 if(sh_isstate(SH_INTERACTIVE
) && !sferror(iop
))
553 if(--maxtry
>0 && sh_isoption(SH_IGNOREEOF
) &&
554 !sferror(sfstderr
) && (shp
->fdstatus
[fno
]&IOTTY
))
557 errormsg(SH_DICT
,0,e_logout
);
560 else if(job_close(shp
)<0)
563 if(errno
==0 && sferror(iop
) && --maxtry
>0)
572 if(sh_isstate(SH_INTERACTIVE
) && shp
->hist_ptr
)
575 hist_eof(shp
->hist_ptr
);
578 if(sh_isoption(SH_HISTORY
))
579 sh_onstate(SH_HISTORY
);
580 job
.waitall
= job
.curpgid
= 0;
581 error_info
.flags
|= ERROR_INTERACTIVE
;
582 t
= (Shnode_t
*)sh_parse(shp
,iop
,0);
583 if(!sh_isstate(SH_INTERACTIVE
) && !sh_isstate(SH_CFLAG
))
584 error_info
.flags
&= ~ERROR_INTERACTIVE
;
586 if(sh_isstate(SH_INTERACTIVE
) && shp
->hist_ptr
)
587 hist_flush(shp
->hist_ptr
);
588 sh_offstate(SH_HISTORY
);
591 execflags
= sh_state(SH_ERREXIT
)|sh_state(SH_INTERACTIVE
);
592 /* The last command may not have to fork */
593 if(!sh_isstate(SH_PROFILE
) && !sh_isstate(SH_INTERACTIVE
) &&
594 (fno
<0 || !(shp
->fdstatus
[fno
]&(IOTTY
|IONOSEEK
)))
595 && !sfreserve(iop
,0,0))
597 execflags
|= sh_state(SH_NOFORK
);
600 sh_exec(t
,execflags
);
603 sh_offstate(SH_INTERACTIVE
);
606 /* This is for sh -t */
607 if(sh_isoption(SH_TFLAG
) && !sh_isstate(SH_PROFILE
))
612 sh_popcontext(&buff
);
613 if(sh_isstate(SH_INTERACTIVE
))
615 sfputc(sfstderr
,'\n');
618 if(jmpval
== SH_JMPSCRIPT
)
619 siglongjmp(*shp
->jmplist
,jmpval
);
620 else if(jmpval
== SH_JMPEXIT
)
625 free((void*)shp
->st
.filename
);
626 shp
->st
.filename
= 0;
630 /* prints out messages if files in list have been modified since last call */
631 static void chkmail(Shell_t
*shp
, char *files
)
633 register char *cp
,*sp
,*qp
;
635 struct argnod
*arglist
=0;
636 int offset
= staktell();
637 char *savstak
=stakptr(0);
644 /* skip to : or end of string saving first '?' */
645 for(qp
=0;*sp
&& *sp
!= ':';sp
++)
646 if((*sp
== '?' || *sp
=='%') && qp
== 0)
650 /* change '?' to end-of-string */
655 /* see if time has been modified since last checked
656 * and the access time <= the modification time
658 if(stat(cp
,&statb
) >= 0 && statb
.st_mtime
>= mailtime
659 && statb
.st_atime
<= statb
.st_mtime
)
661 /* check for directory */
662 if(!arglist
&& S_ISDIR(statb
.st_mode
))
664 /* generate list of directory entries */
665 path_complete(cp
,"/*",&arglist
);
670 * If the file has shrunk,
671 * or if the size is zero
672 * then don't print anything
675 ( statb
.st_ino
!= lastmail
.st_ino
676 || statb
.st_dev
!= lastmail
.st_dev
677 || statb
.st_size
> lastmail
.st_size
))
679 /* save and restore $_ */
680 char *save
= shp
->lastarg
;
682 errormsg(SH_DICT
,0,sh_mactry(shp
,qp
?qp
+1:(char*)e_mailmsg
));
691 cp
= arglist
->argval
;
692 arglist
= arglist
->argchn
.ap
;
704 stakset(savstak
,offset
);
709 #if defined(_hdr_execargs) && defined(pdp11)
710 # include <execargs.h>
714 #if defined(_lib_pstat) && defined(_sys_pstat)
715 # include <sys/pstat.h>
719 #if defined(_lib_fork) && !defined(_NEXT_SOURCE)
721 * fix up command line for ps command
722 * mode is 0 for initialization
724 static void fixargs(char **argv
, int mode
)
727 *execargs
=(char *)argv
;
730 static int command_len
;
737 struct pst_static st
;
739 if(pstat(PSTAT_STATIC
, un
, sizeof(struct pst_static
), 1, 0)<0)
741 command_len
= st
.command_length
;
744 stakseek(command_len
+2);
751 command_len
+= strlen(cp
)+1;
752 if(environ
&& *environ
==buff
+command_len
)
754 for(argv
=environ
; cp
= *argv
; cp
++)
756 if(command_len
> CMD_LENGTH
)
758 command_len
= CMD_LENGTH
;
761 *argv
++ = strdup(cp
);
762 command_len
+= strlen(cp
)+1;
771 while((cp
= *argv
++) && offset
< command_len
)
773 if(offset
+ (size
=strlen(cp
)) >= command_len
)
774 size
= command_len
- offset
;
775 memcpy(buff
+offset
,cp
,size
);
777 buff
[offset
++] = ' ';
781 un
.pst_command
= stakptr(0);
782 pstat(PSTAT_SETCMD
,un
,0,0,0);
784 #endif /* EXECARGS */
786 #endif /* _lib_fork */