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 ***********************************************************************/
31 #include "variables.h"
37 #include "FEATURE/dynamic"
38 #include "FEATURE/externs"
40 # ifdef _hdr_exec_attr
41 # include <exec_attr.h>
44 # include <ast_vfork.h>
46 # define vfork() fork()
50 #define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
54 static int canexecute(char*,int);
55 static void funload(Shell_t
*,int,const char*);
56 static void exscript(Shell_t
*,char*, char*[], char**);
57 static int path_chkpaths(Pathcomp_t
*,Pathcomp_t
*,Pathcomp_t
*,int);
58 static void path_checkdup(register Pathcomp_t
*);
60 static const char *std_path
;
62 static int onstdpath(const char *name
)
64 register const char *cp
= std_path
, *sp
;
68 for(sp
=name
; *sp
&& (*cp
== *sp
); sp
++,cp
++);
69 if(*sp
==0 && (*cp
==0 || *cp
==':'))
71 while(*cp
&& *cp
++!=':');
76 static pid_t
path_pfexecve(const char *path
, char *argv
[],char *const envp
[],int spawn
)
80 char resolvedpath
[PATH_MAX
+ 1];
83 while((pid
= vfork()) < 0)
84 _sh_fork(pid
, 0, (int*)0);
88 if(!sh_isoption(SH_PFSH
))
89 return(execve(path
, argv
, envp
));
90 /* Solaris implements realpath(3C) using the resolvepath(2) */
91 /* system call so we can save us to call access(2) first */
92 if (!realpath(path
, resolvedpath
))
95 /* we can exec the command directly instead of via pfexec(1) if */
96 /* there is a matching entry without attributes in exec_attr(4) */
97 if (sh
.user
&& *sh
.user
)
100 if(pf
=getexecuser(sh
.user
, KV_COMMAND
, resolvedpath
, GET_ONE
))
102 if (!pf
->attr
|| pf
->attr
->length
== 0)
104 int r
= execve(path
, argv
, envp
);
118 argv
[1] = resolvedpath
;
119 return(execve("/usr/bin/pfexec", argv
, envp
));
121 return(execve(path
, argv
, envp
));
126 static pid_t
_spawnveg(const char *path
, char* const argv
[], char* const envp
[], pid_t pgid
)
128 int waitsafe
= job
.waitsafe
;
133 sh_stats(STAT_SPAWN
);
134 pid
= spawnveg(path
,argv
,envp
,pgid
);
135 if(pid
>=0 || errno
!=EAGAIN
)
137 _sh_fork(pid
, 0, (int*)0);
139 job
.waitsafe
= waitsafe
;
147 * used with command -x to run the command in multiple passes
148 * spawn is non-zero when invoked via spawn
149 * the exitval is set to the maximum for each execution
151 static pid_t
path_xargs(const char *path
, char *argv
[],char *const envp
[], int spawn
)
153 register char *cp
, **av
, **xv
;
154 char **avlast
= &argv
[sh
.xargmax
], **saveargs
=0;
157 int nlast
=1,n
,exitval
=0;
161 size
= sh
.lim
.arg_max
-1024;
162 for(ev
=envp
; cp
= *ev
; ev
++)
163 size
-= strlen(cp
)-1;
164 for(av
=argv
; (cp
= *av
) && av
< &argv
[sh
.xargmin
]; av
++)
165 size
-= strlen(cp
)-1;
166 for(av
=avlast
; cp
= *av
; av
++,nlast
++)
167 size
-= strlen(cp
)-1;
168 av
= &argv
[sh
.xargmin
];
174 for(xv
=av
,left
=size
; left
>0 && av
<avlast
;)
175 left
-= strlen(*av
++)+1;
176 /* leave at least two for last */
177 if(left
<0 && (avlast
-av
)<2)
179 if(xv
==&argv
[sh
.xargmin
])
181 n
= nlast
*sizeof(char*);
182 saveargs
= (char**)malloc(n
);
183 memcpy((void*)saveargs
, (void*)av
, n
);
184 memcpy((void*)av
,(void*)avlast
,n
);
188 for(n
=sh
.xargmin
; xv
< av
; xv
++)
190 for(xv
=avlast
; cp
= *xv
; xv
++)
194 if(saveargs
|| av
<avlast
|| (exitval
&& !spawn
))
196 if((pid
=_spawnveg(path
,argv
,envp
,0)) < 0)
200 if(sh
.exitval
>exitval
)
201 exitval
= sh
.exitval
;
204 memcpy((void*)av
,saveargs
,n
);
205 free((void*)saveargs
);
209 else if(spawn
&& !sh_isoption(SH_PFSH
))
211 sh
.xargexit
= exitval
;
212 return(_spawnveg(path
,argv
,envp
,spawn
>>1));
215 return(path_pfexecve(path
,argv
,envp
,spawn
));
223 * make sure PWD is set up correctly
224 * Return the present working directory
225 * Invokes getcwd() if flag==0 and if necessary
226 * Sets the PWD variable to this value
228 char *path_pwd(int flag
)
231 register char *dfault
= (char*)e_dot
;
232 register int count
= 0;
235 return((char*)shp
->pwd
);
238 /* try from lowest to highest */
242 cp
= nv_getval(PWDNOD
);
245 cp
= nv_getval(HOME
);
251 cp
= (char*)e_crondir
;
252 if(flag
) /* skip next case when non-zero flag */
257 if(cp
=getcwd(NIL(char*),0))
259 nv_offattr(PWDNOD
,NV_NOFREE
);
261 PWDNOD
->nvalue
.cp
= cp
;
269 if(cp
&& *cp
=='/' && test_inode(cp
,e_dot
))
274 nv_offattr(PWDNOD
,NV_NOFREE
);
275 nv_putval(PWDNOD
,cp
,NV_RDONLY
);
278 nv_onattr(PWDNOD
,NV_NOFREE
|NV_EXPORT
);
279 shp
->pwd
= (char*)(PWDNOD
->nvalue
.cp
);
283 static void free_bltin(Namval_t
*np
,void *data
)
285 register Pathcomp_t
*pp
= (Pathcomp_t
*)data
;
286 if(pp
->flags
&PATH_STD_DIR
)
288 int offset
=staktell();;
289 if(strcmp(pp
->name
,"/bin")==0 || memcmp(pp
->name
,np
->nvname
,pp
->len
) || np
->nvname
[pp
->len
]!='/')
292 stakputs(np
->nvname
+pp
->len
+1);
294 sh_addbuiltin(stakptr(offset
),np
->nvalue
.bfp
,NiL
);
298 if((void*)np
->nvenv
==pp
->bltin_lib
)
299 nv_delete(np
,sh_bltin_tree(),NV_NOFREE
);
303 * delete current Pathcomp_t structure
305 void path_delete(Pathcomp_t
*first
)
307 register Pathcomp_t
*pp
=first
, *old
=0, *ppnext
;
311 if(--pp
->refcount
<=0)
314 free((void*)pp
->lib
);
316 free((void*)pp
->blib
);
317 if(pp
->bltin_lib
|| (pp
->flags
&PATH_STD_DIR
))
319 nv_scan(sh_bltin_tree(),free_bltin
,pp
,0,0);
322 dlclose(pp
->bltin_lib
);
323 #endif /* SHOPT_DYNAMIC */
336 * returns library variable from .paths
337 * The value might be returned on the stack overwriting path
339 static char *path_lib(Pathcomp_t
*pp
, char *path
)
341 register char *last
= strrchr(path
,'/');
348 r
= stat(path
,&statb
);
355 for( ;pp
; pp
=pp
->next
)
358 if(pp
->ino
==statb
.st_ino
&& pp
->dev
==statb
.st_dev
&& pp
->mtime
==statb
.st_mtime
)
363 pcomp
.len
= last
-path
;
364 memcpy((void*)save
, (void*)stakptr(PATH_OFFSET
+pcomp
.len
),sizeof(save
));
365 if(path_chkpaths((Pathcomp_t
*)0,(Pathcomp_t
*)0,&pcomp
,PATH_OFFSET
))
366 return(stakfreeze(1));
367 memcpy((void*)stakptr(PATH_OFFSET
+pcomp
.len
),(void*)save
,sizeof(save
));
373 void path_dump(register Pathcomp_t
*pp
)
375 sfprintf(sfstderr
,"dump\n");
378 sfprintf(sfstderr
,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n",
379 pp
,pp
->dev
,pp
->ino
,pp
->len
,pp
->flags
,pp
->len
,pp
->name
);
386 * check for duplicate directories on PATH
388 static void path_checkdup(register Pathcomp_t
*pp
)
390 register char *name
= pp
->name
;
391 register Pathcomp_t
*oldpp
,*first
;
394 if(stat(name
,&statb
)<0 || !S_ISDIR(statb
.st_mode
))
396 pp
->flags
|= PATH_SKIP
;
397 pp
->dev
= *name
=='/';
400 pp
->mtime
= statb
.st_mtime
;
401 pp
->ino
= statb
.st_ino
;
402 pp
->dev
= statb
.st_dev
;
403 if(*name
=='/' && onstdpath(name
))
405 first
= (pp
->flags
&PATH_CDPATH
)?pp
->shp
->cdpathlist
:path_get("");
406 for(oldpp
=first
; oldpp
&& oldpp
!=pp
; oldpp
=oldpp
->next
)
408 if(pp
->ino
==oldpp
->ino
&& pp
->dev
==oldpp
->dev
&& pp
->mtime
==oldpp
->mtime
)
415 if(((pp
->flags
&(PATH_PATH
|PATH_SKIP
))==PATH_PATH
))
417 int offset
= staktell();
419 path_chkpaths(first
,0,pp
,offset
);
425 * write the next path to search on the current stack
426 * if last is given, all paths that come before <last> are skipped
427 * the next pathcomp is returned.
429 Pathcomp_t
*path_nextcomp(register Pathcomp_t
*pp
, const char *name
, Pathcomp_t
*last
)
432 stakseek(PATH_OFFSET
);
437 for(;pp
&& pp
!=last
;pp
=ppnext
)
440 ppnext
->shp
= pp
->shp
;
441 if(!pp
->dev
&& !pp
->ino
)
443 if(pp
->flags
&PATH_SKIP
)
445 if(!last
|| *pp
->name
!='/')
448 if(!pp
) /* this should not happen */
451 if(pp
&& (pp
->name
[0]!='.' || pp
->name
[1]))
455 stakputs(path_pwd(1));
456 if(*stakptr(staktell()-1)!='/')
459 stakwrite(pp
->name
,pp
->len
);
460 if(pp
->name
[pp
->len
-1]!='/')
465 while(pp
&& pp
!=last
&& (pp
=pp
->next
))
467 if(!(pp
->flags
&PATH_SKIP
))
470 return((Pathcomp_t
*)0);
473 static Pathcomp_t
* defpath_init(Shell_t
*shp
)
475 Pathcomp_t
*pp
= (void*)path_addpath((Pathcomp_t
*)0,(std_path
),PATH_PATH
);
476 if(shp
->defpathlist
= (void*)pp
)
481 static void path_init(Shell_t
*shp
)
485 if(!std_path
&& !(std_path
=astconf("PATH",NIL(char*),NIL(char*))))
486 std_path
= e_defpath
;
487 if(val
=sh_scoped(shp
,(PATHNOD
))->nvalue
.cp
)
489 pp
= (void*)path_addpath((Pathcomp_t
*)shp
->pathlist
,val
,PATH_PATH
);
490 if(shp
->pathlist
= (void*)pp
)
495 if(!(pp
=(Pathcomp_t
*)shp
->defpathlist
))
496 pp
= defpath_init(shp
);
497 shp
->pathlist
= (void*)path_dup(pp
);
499 if(val
=sh_scoped(shp
,(FPATHNOD
))->nvalue
.cp
)
501 pp
= (void*)path_addpath((Pathcomp_t
*)shp
->pathlist
,val
,PATH_FPATH
);
502 if(shp
->pathlist
= (void*)pp
)
508 * returns that pathlist to search
510 Pathcomp_t
*path_get(register const char *name
)
512 register Shell_t
*shp
= &sh
;
513 register Pathcomp_t
*pp
=0;
514 if(*name
&& strchr(name
,'/'))
516 if(!sh_isstate(SH_DEFPATH
))
520 pp
= (Pathcomp_t
*)shp
->pathlist
;
522 if(!pp
&& (!(PATHNOD
)->nvalue
.cp
) || sh_isstate(SH_DEFPATH
))
524 if(!(pp
=(Pathcomp_t
*)shp
->defpathlist
))
525 pp
= defpath_init(shp
);
531 * open file corresponding to name using path give by <pp>
533 static int path_opentype(const char *name
, register Pathcomp_t
*pp
, int fun
)
543 shp
= sh_getinterp();
547 if(!fun
&& strchr(name
,'/'))
549 if(sh_isoption(SH_RESTRICTED
))
550 errormsg(SH_DICT
,ERROR_exit(1),e_restricted
,name
);
554 pp
= path_nextcomp(oldpp
=pp
,name
,0);
555 while(oldpp
&& (oldpp
->flags
&PATH_SKIP
))
557 if(fun
&& (!oldpp
|| !(oldpp
->flags
&PATH_FPATH
)))
559 if((fd
= sh_open(path_relative(stakptr(PATH_OFFSET
)),O_RDONLY
,0)) >= 0)
561 if(fstat(fd
,&statb
)<0 || S_ISDIR(statb
.st_mode
))
570 if(fd
>=0 && (fd
= sh_iomovefd(fd
)) > 0)
572 fcntl(fd
,F_SETFD
,FD_CLOEXEC
);
575 shp
= sh_getinterp();
577 close(0x10001); /* this results in a /var/log/uwin message with "0x10001" for debugging */
580 shp
->fdstatus
[fd
] |= IOCLEX
;
586 * open file corresponding to name using path give by <pp>
588 int path_open(const char *name
, register Pathcomp_t
*pp
)
590 return(path_opentype(name
,pp
,0));
594 * given a pathname return the base name
597 char *path_basename(register const char *name
)
599 register const char *start
= name
;
601 if ((*name
++ == '/') && *name
) /* don't trim trailing / */
603 return ((char*)start
);
606 char *path_fullname(const char *name
)
608 int len
=strlen(name
)+1,dirlen
=0;
613 dirlen
= strlen(pwd
)+1;
615 path
= (char*)malloc(len
+dirlen
);
618 memcpy((void*)path
,(void*)pwd
,dirlen
);
619 path
[dirlen
-1] = '/';
621 memcpy((void*)&path
[dirlen
],(void*)name
,len
);
627 * load functions from file <fno>
629 static void funload(Shell_t
*shp
,int fno
, const char *name
)
631 char *pname
,*oldname
=shp
->st
.filename
, buff
[IOBSIZE
+1];
633 struct Ufunction
*rp
;
634 int savestates
= sh_getstate(), oldload
=shp
->funload
;
635 pname
= path_fullname(stakptr(PATH_OFFSET
));
636 if(shp
->fpathdict
&& (rp
= dtmatch(shp
->fpathdict
,(void*)pname
)))
638 Dt_t
*funtree
= sh_subfuntree(1);
641 if((np
= dtsearch(funtree
,rp
->np
)) && is_afunction(np
))
644 np
->nvalue
.rp
->fdict
= 0;
645 nv_delete(np
,funtree
,NV_NOFREE
);
647 dtinsert(funtree
,rp
->np
);
650 while((rp
=dtnext(shp
->fpathdict
,rp
)) && strcmp(pname
,rp
->fname
)==0);
653 sh_onstate(SH_NOLOG
);
654 sh_onstate(SH_NOALIAS
);
655 shp
->readscript
= (char*)name
;
656 shp
->st
.filename
= pname
;
659 sh_eval(sfnew(NIL(Sfio_t
*),buff
,IOBSIZE
,fno
,SF_READ
),SH_FUNEVAL
);
661 free((void*)shp
->st
.filename
);
662 shp
->funload
= oldload
;
663 shp
->st
.filename
= oldname
;
664 sh_setstate(savestates
);
668 * do a path search and track alias if requested
669 * if flag is 0, or if name not found, then try autoloading function
670 * if flag==2 or 3, returns 1 if name found on FPATH
671 * if flag==3 no tracked alias will be set
672 * returns 1, if function was autoloaded.
673 * If oldpp is not NULL, it will contain a pointer to the path component
674 * where it was found.
677 int path_search(register const char *name
,Pathcomp_t
**oldpp
, int flag
)
679 register Namval_t
*np
;
683 if(name
&& strchr(name
,'/'))
685 stakseek(PATH_OFFSET
);
687 if(canexecute(stakptr(PATH_OFFSET
),0)<0)
689 *stakptr(PATH_OFFSET
) = 0;
694 stakseek(PATH_OFFSET
);
695 stakputs(path_pwd(1));
701 if(sh_isstate(SH_DEFPATH
))
703 if(!shp
->defpathlist
)
706 else if(!shp
->pathlist
)
710 if((np
=nv_search(name
,shp
->track_tree
,0)) && !nv_isattr(np
,NV_NOALIAS
) && (pp
=(Pathcomp_t
*)np
->nvalue
.cp
))
712 stakseek(PATH_OFFSET
);
713 path_nextcomp(pp
,name
,pp
);
717 pp
= path_absolute(name
,oldpp
?*oldpp
:NIL(Pathcomp_t
*));
720 if(!pp
&& (np
=nv_search(name
,shp
->fun_tree
,HASH_NOSCOPE
))&&np
->nvalue
.ip
)
723 *stakptr(PATH_OFFSET
) = 0;
725 if(flag
==0 || !pp
|| (pp
->flags
&PATH_FPATH
))
728 pp
=sh_isstate(SH_DEFPATH
)?shp
->defpathlist
:shp
->pathlist
;
729 if(pp
&& strmatch(name
,e_alphanum
) && (fno
=path_opentype(name
,pp
,1))>=0)
736 funload(shp
,fno
,name
);
739 *stakptr(PATH_OFFSET
) = 0;
742 else if(pp
&& !sh_isstate(SH_DEFPATH
) && *name
!='/' && flag
<3)
744 if(np
=nv_search(name
,shp
->track_tree
,NV_ADD
))
751 * do a path search and find the full pathname of file name
753 Pathcomp_t
*path_absolute(register const char *name
, Pathcomp_t
*pp
)
755 register int f
,isfun
;
760 shp
->path_err
= ENOENT
;
761 if(!pp
&& !(pp
=path_get("")))
767 isfun
= (pp
->flags
&PATH_FPATH
);
770 pp
= path_nextcomp(pp
,name
,0);
771 while(oldpp
->flags
&PATH_SKIP
)
773 if(!(oldpp
=oldpp
->next
))
775 shp
->path_err
= ENOENT
;
781 if(!isfun
&& !sh_isoption(SH_RESTRICTED
))
783 if(*stakptr(PATH_OFFSET
)=='/' && nv_search(stakptr(PATH_OFFSET
),sh
.bltin_tree
,0))
788 typedef int (*Fptr_t
)(int, char*[], void*);
796 if(!oldpp
->bltin_lib
)
798 if(cp
= strrchr(oldpp
->blib
,'/'))
802 if((libcmd
= !strcmp(cp
,LIBCMD
)) && (addr
=(Fptr_t
)dlllook((void*)0,stakptr(n
))))
804 if((np
= sh_addbuiltin(stakptr(PATH_OFFSET
),addr
,NiL
)) && nv_isattr(np
,NV_BLTINOPT
))
807 #if (_AST_VERSION>=20040404)
808 if (oldpp
->bltin_lib
= dllplug(SH_ID
, oldpp
->blib
, NiL
, RTLD_LAZY
, NiL
, 0))
810 if (oldpp
->bltin_lib
= dllfind(oldpp
->blib
, NiL
, RTLD_LAZY
, NiL
, 0))
814 * this detects the 2007-05-11 builtin context change and also
815 * the 2008-03-30 opt_info.num change that hit libcmd::b_head
818 if (libcmd
&& !dlllook(oldpp
->bltin_lib
, "b_pids"))
820 dlclose(oldpp
->bltin_lib
);
821 oldpp
->bltin_lib
= 0;
825 sh_addlib(oldpp
->bltin_lib
);
828 if((addr
=(Fptr_t
)dlllook(oldpp
->bltin_lib
,stakptr(n
))) &&
829 (!(np
= sh_addbuiltin(stakptr(PATH_OFFSET
),NiL
,NiL
)) || np
->nvalue
.bfp
!=addr
) &&
830 (np
= sh_addbuiltin(stakptr(PATH_OFFSET
),addr
,NiL
)))
832 np
->nvenv
= oldpp
->bltin_lib
;
836 #endif /* SHOPT_DYNAMIC */
838 sh_stats(STAT_PATHS
);
839 f
= canexecute(stakptr(PATH_OFFSET
),isfun
);
842 nv_onattr(nv_open(name
,sh_subfuntree(1),NV_NOARRAY
|NV_IDENT
|NV_NOSCOPE
),NV_LTOU
|NV_FUNCTION
);
848 else if(f
>=0 && (oldpp
->flags
& PATH_STD_DIR
))
854 np
= nv_search(stakptr(n
),sh
.bltin_tree
,0);
859 np
= sh_addbuiltin(stakptr(PATH_OFFSET
),np
->nvalue
.bfp
,nv_context(np
));
870 shp
->path_err
= (noexec
?noexec
:ENOENT
);
878 * returns 0 if path can execute
879 * sets exec_err if file is found but can't be executable
883 # define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH)
886 # define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
888 # define S_IXALL 0111
892 static int canexecute(register char *path
, int isfun
)
896 path
= path_relative(path
);
899 if((fd
=open(path
,O_RDONLY
,0))<0 || fstat(fd
,&statb
)<0)
902 else if(stat(path
,&statb
) < 0)
905 /* check for .exe or .bat suffix */
907 if(errno
==ENOENT
&& (!(cp
=strrchr(path
,'.')) || strlen(cp
)>4 || strchr(cp
,'/')))
909 int offset
= staktell()-1;
912 path
= stakptr(PATH_OFFSET
);
913 if(stat(path
,&statb
) < 0)
917 memcpy(stakptr(offset
),".sh",4);
918 if(stat(path
,&statb
) < 0)
927 if(S_ISDIR(statb
.st_mode
))
929 else if((statb
.st_mode
&S_IXALL
)==S_IXALL
|| sh_access(path
,X_OK
)>=0)
938 * Return path relative to present working directory
941 char *path_relative(register const char* file
)
943 register const char *pwd
;
944 register const char *fp
= file
;
945 /* can't relpath when sh.pwd not set */
951 return((char*)e_dot
);
954 if(*pwd
==0 && *fp
== '/')
959 return((char*)e_dot
);
964 void path_exec(register const char *arg0
,register char *argv
[],struct argnod
*local
)
968 Pathcomp_t
*libpath
, *pp
=0;
971 nv_setlist(local
,NV_EXPORT
|NV_IDENT
|NV_ASSIGN
,0);
976 /* name containing / not allowed for restricted shell */
977 if(sh_isoption(SH_RESTRICTED
))
978 errormsg(SH_DICT
,ERROR_exit(1),e_restricted
,arg0
);
982 shp
->path_err
= ENOENT
;
983 sfsync(NIL(Sfio_t
*));
984 timerdel(NIL(void*));
985 /* find first path that has a library component */
986 while(pp
&& (pp
->flags
&PATH_SKIP
))
993 pp
= path_nextcomp(pp
,arg0
,0);
994 opath
= stakfreeze(1)+PATH_OFFSET
;
998 path_spawn(opath
,argv
,envp
,libpath
,0);
999 while(pp
&& (pp
->flags
&PATH_FPATH
))
1000 pp
= path_nextcomp(pp
,arg0
,0);
1004 ((struct checkpt
*)shp
->jmplist
)->mode
= SH_JMPEXIT
;
1005 if((errno
=shp
->path_err
)==ENOENT
)
1006 errormsg(SH_DICT
,ERROR_exit(ERROR_NOENT
),e_found
,arg0
);
1008 errormsg(SH_DICT
,ERROR_system(ERROR_NOEXEC
),e_exec
,arg0
);
1011 pid_t
path_spawn(const char *opath
,register char **argv
, char **envp
, Pathcomp_t
*libpath
, int spawn
)
1013 Shell_t
*shp
= sh_getinterp();
1014 register char *path
;
1015 char **xp
=0, *xval
, *libenv
= (libpath
?libpath
->lib
:0);
1020 /* leave room for inserting _= pathname in environment */
1023 /* save original pathname */
1024 stakseek(PATH_OFFSET
);
1025 pidsize
= sfprintf(stkstd
,"*%d*",spawn
?getpid():getppid());
1027 opath
= stakfreeze(1)+PATH_OFFSET
+pidsize
;
1028 np
=nv_search(argv
[0],shp
->track_tree
,0);
1029 while(libpath
&& !libpath
->lib
)
1030 libpath
=libpath
->next
;
1031 if(libpath
&& (!np
|| nv_size(np
)>0))
1033 /* check for symlink and use symlink name */
1034 char buff
[PATH_MAX
+1];
1035 char save
[PATH_MAX
+1];
1036 stakseek(PATH_OFFSET
);
1038 path
= stakptr(PATH_OFFSET
);
1039 while((n
=readlink(path
,buff
,PATH_MAX
))>0)
1044 if((v
=strrchr(path
,'/')) && *buff
!='/')
1046 if(buff
[0]=='.' && buff
[1]=='.' && (r
= strlen(path
) + 1) <= PATH_MAX
)
1047 memcpy(save
, path
, r
);
1055 path
= stakptr(PATH_OFFSET
);
1056 if(v
&& buff
[0]=='.' && buff
[1]=='.')
1059 if(r
&& access(path
,X_OK
))
1061 memcpy(path
, save
, r
);
1065 if(libenv
= path_lib(libpath
,path
))
1071 if(libenv
&& (v
= strchr(libenv
,'=')))
1075 np
= nv_open(libenv
,shp
->var_tree
,0);
1089 if (strneq(s
, v
, n
) && s
[n
] == '=')
1104 opath
= stakptr(PATH_OFFSET
);
1105 envp
[0] = (char*)opath
-(PATH_OFFSET
+pidsize
);
1110 path
= path_relative(opath
);
1112 if(*path
!='/' && path
!=opath
)
1115 * The following code because execv(foo,) and execv(./foo,)
1116 * may not yield the same results
1118 char *sp
= (char*)malloc(strlen(path
)+3);
1124 #endif /* SHELLMAGIC */
1125 if(spawn
&& !sh_isoption(SH_PFSH
))
1126 pid
= _spawnveg(opath
, &argv
[0],envp
, spawn
>>1);
1128 pid
= path_pfexecve(opath
, &argv
[0] ,envp
,spawn
);
1132 if(*path
=='.' && path
!=opath
)
1135 path
= path_relative(opath
);
1137 #endif /* SHELLMAGIC */
1141 switch(sh
.path_err
= errno
)
1145 * On apollo's execve will fail with eacces when
1146 * file has execute but not read permissions. So,
1147 * for now we will pretend that EACCES and ENOEXEC
1148 * mean the same thing.
1155 /* some systems return EPERM if setuid bit is on */
1168 while(_sh_fork(pid
,0,(int*)0) < 0);
1169 ((struct checkpt
*)shp
->jmplist
)->mode
= SH_JMPEXIT
;
1174 exscript(shp
,path
,argv
,envp
);
1179 if(stat(path
,&statb
)>=0)
1181 if(S_ISDIR(statb
.st_mode
))
1184 if(S_ISSOCK(statb
.st_mode
))
1185 exscript(shp
,path
,argv
,envp
);
1190 #endif /* !apollo */
1193 #endif /* ENAMETOOLONG */
1194 #if !SHOPT_SUID_EXEC
1197 shp
->path_err
= errno
;
1209 pid
= path_xargs(opath
, &argv
[0] ,envp
,spawn
);
1215 errormsg(SH_DICT
,ERROR_system(ERROR_NOEXEC
),e_exec
,path
);
1221 * File is executable but not machine code.
1222 * Assume file is a Shell script and execute it.
1225 static void exscript(Shell_t
*shp
,register char *path
,register char *argv
[],char **envp
)
1227 register Sfio_t
*sp
;
1228 path
= path_relative(path
);
1232 /* clean up any cooperating processes */
1234 sh_pclose(shp
->cpipe
);
1235 if(shp
->cpid
&& shp
->outpipe
)
1236 sh_close(*shp
->outpipe
);
1239 while(sfstack(sp
,SF_POPSTACK
));
1241 if(shp
->infd
>0 && (shp
->fdstatus
[shp
->infd
]&IOCLEX
))
1242 sh_close(shp
->infd
);
1243 sh_setstate(sh_state(SH_FORKED
));
1245 #if SHOPT_SUID_EXEC && !SHOPT_PFSH
1246 /* check if file cannot open for read or script is setuid/setgid */
1248 static char name
[] = "/tmp/euidXXXXXXXXXX";
1250 register uid_t euserid
;
1253 if((n
=sh_open(path
,O_RDONLY
,0)) >= 0)
1255 /* move <n> if n=0,1,2 */
1257 if(fstat(n
,&statb
)>=0 && !(statb
.st_mode
&(S_ISUID
|S_ISGID
)))
1261 if((euserid
=geteuid()) != shp
->userid
)
1263 strncpy(name
+9,fmtbase((long)getpid(),10,0),sizeof(name
)-10);
1264 /* create a suid open file with owner equal effective uid */
1265 if((n
=open(name
,O_CREAT
|O_TRUNC
|O_WRONLY
,S_ISUID
|S_IXUSR
)) < 0)
1268 /* make sure that file has right owner */
1269 if(fstat(n
,&statb
)<0 || statb
.st_uid
!= euserid
)
1274 fcntl(n
, F_DUPFD
, 10);
1281 path_pfexecve(e_suidexec
,argv
,envp
,0);
1284 * The following code is just for compatibility
1286 if((n
=open(path
,O_RDONLY
,0)) < 0)
1287 errormsg(SH_DICT
,ERROR_system(ERROR_NOEXEC
),e_exec
,path
);
1294 if((shp
->infd
= sh_open(path
,O_RDONLY
,0)) < 0)
1295 errormsg(SH_DICT
,ERROR_system(ERROR_NOEXEC
),e_exec
,path
);
1297 shp
->infd
= sh_iomovefd(shp
->infd
);
1299 sh_accbegin(path
) ; /* reset accounting */
1300 #endif /* SHOPT_ACCT */
1301 shp
->arglist
= sh_argcreate(argv
);
1302 shp
->lastarg
= strdup(path
);
1303 /* save name of calling command */
1304 shp
->readscript
= error_info
.id
;
1305 /* close history file if name has changed */
1306 if(shp
->hist_ptr
&& (path
=nv_getval(HISTFILE
)) && strcmp(path
,shp
->hist_ptr
->histname
))
1308 hist_close(shp
->hist_ptr
);
1309 (HISTCUR
)->nvalue
.lp
= 0;
1311 sh_offstate(SH_FORKED
);
1312 if(shp
->sigflag
[SIGCHLD
]==SH_SIGOFF
)
1313 shp
->sigflag
[SIGCHLD
] = SH_SIGFAULT
;
1314 siglongjmp(*shp
->jmplist
,SH_JMPSCRIPT
);
1318 # include <sys/acct.h>
1319 # include "FEATURE/time"
1321 static struct acct sabuf
;
1322 static struct tms buffer
;
1323 static clock_t before
;
1324 static char *SHACCT
; /* set to value of SHACCT environment variable */
1325 static shaccton
; /* non-zero causes accounting record to be written */
1326 static int compress(time_t);
1328 * initialize accounting, i.e., see if SHACCT variable set
1330 void sh_accinit(void)
1332 SHACCT
= getenv("SHACCT");
1335 * suspend accounting until turned on by sh_accbegin()
1337 void sh_accsusp(void)
1341 sabuf
.ac_flag
|= AEXPND
;
1342 #endif /* AEXPAND */
1346 * begin an accounting record by recording start time
1348 void sh_accbegin(const char *cmdname
)
1352 sabuf
.ac_btime
= time(NIL(time_t *));
1353 before
= times(&buffer
);
1354 sabuf
.ac_uid
= getuid();
1355 sabuf
.ac_gid
= getgid();
1356 strncpy(sabuf
.ac_comm
, (char*)path_basename(cmdname
),
1357 sizeof(sabuf
.ac_comm
));
1362 * terminate an accounting record and append to accounting file
1364 void sh_accend(void)
1371 after
= times(&buffer
);
1372 sabuf
.ac_utime
= compress(buffer
.tms_utime
+ buffer
.tms_cutime
);
1373 sabuf
.ac_stime
= compress(buffer
.tms_stime
+ buffer
.tms_cstime
);
1374 sabuf
.ac_etime
= compress( (time_t)(after
-before
));
1375 fd
= open( SHACCT
, O_WRONLY
| O_APPEND
| O_CREAT
,RW_ALL
);
1376 write(fd
, (const char*)&sabuf
, sizeof( sabuf
));
1382 * Produce a pseudo-floating point representation
1383 * with 3 bits base-8 exponent, 13 bits fraction.
1385 static int compress(register time_t t
)
1387 register int exp
= 0, rund
= 0;
1404 return((exp
<<13) + t
);
1406 #endif /* SHOPT_ACCT */
1411 * add a pathcomponent to the path search list and eliminate duplicates
1412 * and non-existing absolute paths.
1414 static Pathcomp_t
*path_addcomp(Pathcomp_t
*first
, Pathcomp_t
*old
,const char *name
, int flag
)
1416 register Pathcomp_t
*pp
, *oldpp
;
1417 int len
, offset
=staktell();
1418 if(!(flag
&PATH_BFPATH
))
1420 register const char *cp
= name
;
1421 while(*cp
&& *cp
!=':')
1423 len
= staktell()-offset
;
1426 name
= (const char*)stakptr(offset
);
1430 for(pp
=first
; pp
; pp
=pp
->next
)
1432 if(memcmp(name
,pp
->name
,len
)==0 && (pp
->name
[len
]==':' || pp
->name
[len
]==0))
1438 for(pp
=first
, oldpp
=0; pp
; oldpp
=pp
, pp
=pp
->next
);
1439 pp
= newof((Pathcomp_t
*)0,Pathcomp_t
,1,len
+1);
1441 memcpy((char*)(pp
+1),name
,len
+1);
1442 pp
->name
= (char*)(pp
+1);
1449 if(strcmp(name
,SH_CMDLIB_DIR
)==0)
1452 pp
->flags
|= PATH_BUILTIN_LIB
;
1453 pp
->blib
= malloc(4);
1454 strcpy(pp
->blib
,LIBCMD
);
1457 if(old
&& ((flag
&(PATH_PATH
|PATH_SKIP
))==PATH_PATH
))
1458 path_chkpaths(first
,old
,pp
,offset
);
1463 * This function checks for the .paths file in directory in <pp>
1464 * it assumes that the directory is on the stack at <offset>
1466 static int path_chkpaths(Pathcomp_t
*first
, Pathcomp_t
* old
,Pathcomp_t
*pp
, int offset
)
1471 stakseek(offset
+pp
->len
);
1472 if(pp
->len
==1 && *stakptr(offset
)=='/')
1474 stakputs("/.paths");
1475 if((fd
=open(stakptr(offset
),O_RDONLY
))>=0)
1479 stakseek(offset
+pp
->len
+n
+2);
1480 sp
= stakptr(offset
+pp
->len
);
1485 for(ep
=0; n
--; cp
++)
1492 else if(*cp
!='\r' && *cp
!='\n')
1494 if(*sp
=='#' || sp
==cp
)
1500 m
= ep
? (ep
-sp
) : 0;
1501 if(m
==0 || m
==6 && memcmp((void*)sp
,(void*)"FPATH=",6)==0)
1505 char *ptr
= stakptr(offset
+pp
->len
+1);
1508 path_addcomp(first
,old
,stakptr(offset
),PATH_FPATH
|PATH_BFPATH
);
1511 else if(m
==12 && memcmp((void*)sp
,(void*)"BUILTIN_LIB=",12)==0)
1513 if(!(pp
->flags
& PATH_BUILTIN_LIB
) || strchr(ep
,'-'))
1515 if ((pp
->flags
& (PATH_BUILTIN_LIB
|PATH_STD_DIR
)) == PATH_BUILTIN_LIB
)
1520 pp
->flags
|= PATH_BUILTIN_LIB
;
1521 if (*ep
== '.' && !*(ep
+ 1))
1522 pp
->flags
|= PATH_STD_DIR
;
1528 pp
->blib
= sp
= malloc(k
);
1531 strcpy(pp
->blib
,pp
->name
);
1541 pp
->lib
= (char*)malloc(cp
-sp
+pp
->len
+2);
1542 memcpy((void*)pp
->lib
,(void*)sp
,m
);
1543 memcpy((void*)&pp
->lib
[m
],stakptr(offset
),pp
->len
);
1544 pp
->lib
[k
=m
+pp
->len
] = '/';
1545 strcpy((void*)&pp
->lib
[k
+1],ep
);
1546 pathcanon(&pp
->lib
[m
],0);
1551 free((void*)pp
->lib
);
1563 Pathcomp_t
*path_addpath(Pathcomp_t
*first
, register const char *path
,int type
)
1565 register const char *cp
;
1567 int offset
= staktell();
1570 if(!path
&& type
!=PATH_PATH
)
1572 if(type
!=PATH_FPATH
)
1578 savptr
= stakfreeze(0);
1579 if(path
) while(*(cp
=path
))
1583 if(type
!=PATH_FPATH
)
1584 first
= path_addcomp(first
,old
,".",type
);
1585 while(*++path
== ':');
1590 while(*path
&& *path
!=':')
1593 first
= path_addcomp(first
,old
,cp
,type
);
1604 Pathcomp_t
*pp
= (Pathcomp_t
*)old
->shp
->defpathlist
;
1606 pp
= defpath_init(old
->shp
);
1607 first
= path_dup(pp
);
1609 if(cp
=(FPATHNOD
)->nvalue
.cp
)
1610 first
= (void*)path_addpath((Pathcomp_t
*)first
,cp
,PATH_FPATH
);
1614 stakset(savptr
,offset
);
1621 * duplicate the path give by <first> by incremented reference counts
1623 Pathcomp_t
*path_dup(Pathcomp_t
*first
)
1625 register Pathcomp_t
*pp
=first
;
1635 * called whenever the directory is changed
1637 void path_newdir(Pathcomp_t
*first
)
1639 register Pathcomp_t
*pp
=first
, *next
, *pq
;
1641 for(pp
=first
; pp
; pp
=pp
->next
)
1643 pp
->flags
&= ~PATH_SKIP
;
1646 /* delete .paths component */
1647 if((next
=pp
->next
) && (next
->flags
&PATH_BFPATH
))
1649 pp
->next
= next
->next
;
1650 if(--next
->refcount
<=0)
1653 if(stat(pp
->name
,&statb
)<0 || !S_ISDIR(statb
.st_mode
))
1659 pp
->dev
= statb
.st_dev
;
1660 pp
->ino
= statb
.st_ino
;
1661 pp
->mtime
= statb
.st_mtime
;
1662 for(pq
=first
;pq
!=pp
;pq
=pq
->next
)
1664 if(pp
->ino
==pq
->ino
&& pp
->dev
==pq
->dev
)
1665 pp
->flags
|= PATH_SKIP
;
1667 for(pq
=pp
;pq
=pq
->next
;)
1669 if(pp
->ino
==pq
->ino
&& pp
->dev
==pq
->dev
)
1670 pq
->flags
|= PATH_SKIP
;
1672 if((pp
->flags
&(PATH_PATH
|PATH_SKIP
))==PATH_PATH
)
1674 /* try to insert .paths component */
1675 int offset
= staktell();
1680 path_chkpaths(first
,(Pathcomp_t
*)0,pp
,offset
);
1691 Pathcomp_t
*path_unsetfpath(Pathcomp_t
*first
)
1693 register Pathcomp_t
*pp
=first
, *old
=0;
1697 struct Ufunction
*rp
, *rpnext
;
1698 for(rp
=(struct Ufunction
*)dtfirst(shp
->fpathdict
);rp
;rp
=rpnext
)
1700 rpnext
= (struct Ufunction
*)dtnext(shp
->fpathdict
,rp
);
1702 nv_delete(rp
->np
,rp
->fdict
,NV_NOFREE
);
1708 if((pp
->flags
&PATH_FPATH
) && !(pp
->flags
&PATH_BFPATH
))
1710 if(pp
->flags
&PATH_PATH
)
1711 pp
->flags
&= ~PATH_FPATH
;
1714 Pathcomp_t
*ppsave
=pp
;
1716 old
->next
= pp
->next
;
1720 if(--ppsave
->refcount
<=0)
1723 free((void*)ppsave
->lib
);
1724 free((void*)ppsave
);
1736 Pathcomp_t
*path_dirfind(Pathcomp_t
*first
,const char *name
,int c
)
1738 register Pathcomp_t
*pp
=first
;
1741 if(memcmp(name
,pp
->name
,pp
->len
)==0 && name
[pp
->len
]==c
)
1749 * get discipline for tracked alias
1751 static char *talias_get(Namval_t
*np
, Namfun_t
*nvp
)
1753 Pathcomp_t
*pp
= (Pathcomp_t
*)np
->nvalue
.cp
;
1757 path_nextcomp(pp
,nv_name(np
),pp
);
1758 ptr
= stakfreeze(0);
1759 return(ptr
+PATH_OFFSET
);
1762 static void talias_put(register Namval_t
* np
,const char *val
,int flags
,Namfun_t
*fp
)
1764 if(!val
&& np
->nvalue
.cp
)
1766 Pathcomp_t
*pp
= (Pathcomp_t
*)np
->nvalue
.cp
;
1767 if(--pp
->refcount
<=0)
1770 nv_putv(np
,val
,flags
,fp
);
1773 static const Namdisc_t talias_disc
= { 0, talias_put
, talias_get
};
1774 static Namfun_t talias_init
= { &talias_disc
, 1 };
1777 * set tracked alias node <np> to value <pp>
1779 void path_alias(register Namval_t
*np
,register Pathcomp_t
*pp
)
1785 nv_offattr(np
,NV_NOPRINT
);
1786 nv_stack(np
,&talias_init
);
1787 np
->nvalue
.cp
= (char*)pp
;
1789 nv_setattr(np
,NV_TAGGED
|NV_NOFREE
);
1790 path_nextcomp(pp
,nv_name(np
),pp
);
1791 sp
= stakptr(PATH_OFFSET
);
1792 if(sp
&& lstat(sp
,&statb
)>=0 && S_ISLNK(statb
.st_mode
))
1793 nv_setsize(np
,statb
.st_size
+1);