2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: path.c /main/3 1995/11/01 16:50:31 rswiston $ */
24 /***************************************************************
26 * AT&T - PROPRIETARY *
28 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF *
29 * AT&T BELL LABORATORIES *
30 * AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN *
31 * ACCORDANCE WITH APPLICABLE AGREEMENTS *
33 * Copyright (c) 1995 AT&T Corp. *
34 * Unpublished & Not for Publication *
35 * All Rights Reserved *
37 * The copyright notice above does not evidence any *
38 * actual or intended publication of such source code *
40 * This software was created by the *
41 * Advanced Software Technology Department *
42 * AT&T Bell Laboratories *
44 * For further information contact *
45 * {research,attmail}!dgk *
47 ***************************************************************/
49 /* : : generated by proto : : */
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__ "C"
59 #define __PROTO__(x) x
61 #define __PARAM__(n,o) n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
74 #define __PROTO__(x) ()
75 #define __OTORP__(x) x
76 #define __PARAM__(n,o) o
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__ ...
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a) va_start(p,a)
92 #define __VA_START__(p,a) va_start(p)
98 #include "variables.h"
104 #include "FEATURE/externs"
106 #define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
108 static const char usrbin[] = "/usr/bin";
109 static char *prune __PROTO__((char*, const char*));
110 static char *execs __PROTO__((const char*, const char*, char**));
111 static int canexecute __PROTO__((char*,int));
112 static char *path_to_stak __PROTO__((const char*));
113 static int path_inpath __PROTO__((const char*,const char*));
114 static void funload __PROTO__((int,const char*));
115 static void exscript __PROTO__((char*, char*[]));
117 static int suffix_offset;
118 extern __MANGLE__ char *suffix_list[];
119 #endif /* SHOPT_VPIX */
121 static Namval_t *tracknod;
122 static char **xecenv;
127 * make sure PWD is set up correctly
128 * Return the present working directory
129 * Invokes getcwd() if flag==0 and if necessary
130 * Sets the PWD variable to this value
132 char *path_pwd __PARAM__((int flag), (flag)) __OTORP__(int flag;){
134 register char *dfault = (char*)e_dot;
135 register int count = 0;
137 return((char*)sh.pwd);
140 /* try from lowest to highest */
144 cp = nv_getval(PWDNOD);
147 cp = nv_getval(HOME);
153 cp = (char*)e_crondir;
154 if(flag) /* skip next case when non-zero flag */
159 if(cp=getcwd(NIL(char*),0))
161 nv_offattr(PWDNOD,NV_NOFREE);
163 PWDNOD->nvalue.cp = cp;
171 if(cp && *cp=='/' && test_inode(cp,e_dot))
176 nv_offattr(PWDNOD,NV_NOFREE);
177 nv_putval(PWDNOD,cp,NV_RDONLY);
180 nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
181 sh.pwd = (char*)(PWDNOD->nvalue.cp);
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
191 char *path_get __PARAM__((const char *name), (name)) __OTORP__(const char *name;){
192 register char *path=0;
193 register char *sp = sh.lastpath;
194 static int bin_is_usrbin = -1;
197 if(!sh_isstate(SH_DEFPATH))
198 path = nv_getval(nv_scoped(PATHNOD));
200 path = (char*)e_defpath;
201 if(bin_is_usrbin < 0)
202 bin_is_usrbin = test_inode(usrbin+4,usrbin);
204 path = path_to_stak(path);
206 path = stakcopy(path);
207 if(sh_isstate(SH_DEFPATH))
209 if(sp || *name && ((tracknod=nv_search(name,sh.track_tree,0)) &&
210 nv_isattr(tracknod,NV_NOALIAS)==0 &&
211 (sp=nv_getval(tracknod))))
213 path = prune(path,sp);
220 * convert each /usr/bin to /bin and eliminate multiple /bin from <path>
221 * The resulting path is placed on the stack
223 static char *path_to_stak __PARAM__((register const char *path), (path)) __OTORP__(register const char *path;){
224 register const char *cp=path, *base;
225 register int n, nbin=0;
232 while((n= *cp) && n!=':')
234 if((n=cp-base)==8 && strncmp(usrbin,base,8)==0)
236 else if(n!=4 || strncmp(usrbin+4,base,4))
238 if((n=base-path) > 0)
242 stakwrite(usrbin+4,4);
249 return(stakfreeze(1));
252 int path_open __PARAM__((const char *name,char *path), (name, path)) __OTORP__(const char *name;char *path;){
257 if(sh_isoption(SH_RESTRICTED))
258 error(ERROR_exit(1),e_restricted,name);
263 path = (char*)e_defpath;
264 path = stakcopy(path);
268 path=path_join(path,name);
269 if((fd = sh_open(path_relative(stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
271 if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
279 while( fd<0 && path);
280 if((fd = sh_iomovefd(fd)) > 0)
282 fcntl(fd,F_SETFD,FD_CLOEXEC);
283 sh.fdstatus[fd] |= IOCLEX;
289 * This routine returns 1 if first directory in <path> is also in <fpath>
290 * If <path> begins with :, or first directory is ., $PWD must be in <fpath>
291 * Otherwise, 0 is returned
294 static int path_inpath __PARAM__((const char *path, const char *fpath), (path, fpath)) __OTORP__(const char *path; const char *fpath;){
295 register const char *dp = fpath;
296 register const char *sp = path;
297 register int c, match=1;
298 if(!dp || !sp || *sp==0)
300 if(*sp==':' || (*sp=='.' && ((c=sp[1])==0 || c==':')))
301 sp = path = path_pwd(1);
304 if((c= *dp++)==0 || c == ':')
306 if(match==1 && sp>path && (*sp==0 || *sp==':'))
323 * set tracked alias node <np> to value <sp>
326 void path_alias __PARAM__((register Namval_t *np,register char *sp), (np, sp)) __OTORP__(register Namval_t *np;register char *sp;){
331 register const char *vp = np->nvalue.cp;
333 register int nofree = nv_isattr(np,NV_NOFREE);
334 nv_offattr(np,NV_NOPRINT);
335 if(!vp || (n=strcmp(sp,vp))!=0)
337 int subshell = sh.subshell;
340 sh.subshell = subshell;
342 nv_setattr(np,NV_TAGGED|NV_EXPORT);
344 nv_onattr(np,NV_NOFREE);
350 * given a pathname return the base name
353 char *path_basename __PARAM__((register const char *name), (name)) __OTORP__(register const char *name;){
354 register const char *start = name;
356 if ((*name++ == '/') && *name) /* don't trim trailing / */
358 return ((char*)start);
362 * load functions from file <fno>
364 static void funload __PARAM__((int fno, const char *name), (fno, name)) __OTORP__(int fno; const char *name;){
365 char buff[IOBSIZE+1];
366 int savestates = sh_isstate(~0);
367 sh_onstate(SH_NOLOG|SH_NOALIAS);
368 sh.readscript = (char*)name;
369 sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),0);
371 sh_setstate(savestates);
375 * do a path search and track alias if requested
376 * if flag is 0, or if name not found, then try autoloading function
377 * if flag==2, returns 1 if name found on FPATH
378 * returns 1, if function was autoloaded.
379 * If endpath!=NULL, Path search ends when path matches endpath.
382 int path_search __PARAM__((register const char *name,const char *endpath, int flag), (name, endpath, flag)) __OTORP__(register const char *name;const char *endpath; int flag;){
383 register Namval_t *np;
387 /* if not found on pruned path, rehash and try again */
388 while(!(sh.lastpath=path_absolute(name,endpath)) && pruned)
389 nv_onattr(tracknod,NV_NOALIAS);
390 if(!sh.lastpath && (np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip)
394 if(flag==0 || !sh.lastpath)
396 register char *path=0;
397 if(strmatch(name,e_alphanum))
398 path = nv_getval(nv_scoped(FPATHNOD));
399 if(path && (fno=path_open(name,path))>=0)
407 if((np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip)
412 else if(!sh_isstate(SH_DEFPATH))
414 if((np=tracknod) || ((endpath||sh_isoption(SH_TRACKALL)) &&
415 (np=nv_search(name,sh.track_tree,NV_ADD)))
417 path_alias(np,sh.lastpath);
424 * do a path search and find the full pathname of file name
425 * end search of path matches endpath without checking execute permission
428 char *path_absolute __PARAM__((register const char *name, const char *endpath), (name, endpath)) __OTORP__(register const char *name; const char *endpath;){
431 register const char *fpath=0;
436 #endif /* SHOPT_VPIX */
438 path = path_get(name);
439 if(*path && strmatch(name,e_alphanum))
440 fpath = nv_getval(nv_scoped(FPATHNOD));
444 isfun = (fpath && path_inpath(path,fpath));
448 if(!isfun && path_inpath(path,nv_getval(nv_scoped(DOSPATHNOD))))
449 suffix = suffix_list;
450 path=path_join(path,name);
452 top = stakptr(suffix_offset);
464 path=path_join(path,name);
465 #endif /* SHOPT_VPIX */
466 if(endpath && strcmp(endpath,stakptr(PATH_OFFSET))==0)
467 return((char*)endpath);
468 f = canexecute(stakptr(PATH_OFFSET),isfun);
471 funload(f,stakptr(PATH_OFFSET));
477 while(f<0 && (path||suffix));
480 #endif /* SHOPT_VPIX */
483 /* check for relative pathname */
484 if(*stakptr(PATH_OFFSET)!='/')
485 path_join(path_pwd(1),(char*)stakfreeze(1)+PATH_OFFSET);
486 return((char*)stakfreeze(1)+PATH_OFFSET);
490 * returns 0 if path can execute
491 * sets exec_err if file is found but can't be executable
495 # define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH)
498 # define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
500 # define S_IXALL 0111
504 static int canexecute __PARAM__((register char *path, int isfun), (path, isfun)) __OTORP__(register char *path; int isfun;){
507 path = path_relative(path);
510 if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
513 else if(stat(path,&statb) < 0)
516 /* check for .exe suffix */
520 path = stakptr(PATH_OFFSET);
521 if(stat(path,&statb) < 0)
529 if(S_ISDIR(statb.st_mode))
531 else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
542 * Return path relative to present working directory
545 char *path_relative __PARAM__((register const char* file), (file)) __OTORP__(register const char* file;){
546 register const char *pwd;
547 register const char *fp = file;
548 /* can't relpath when sh.pwd not set */
554 return((char*)e_dot);
557 if(*pwd==0 && *fp == '/')
562 return((char*)e_dot);
567 char *path_join __PARAM__((register char *path,const char *name), (path, name)) __OTORP__(register char *path;const char *name;){
568 /* leaves result on top of stack */
569 register char *scanp = path;
571 stakseek(PATH_OFFSET);
574 if((c= *++scanp)==0 || c==':')
581 while(*scanp && *scanp!=':')
586 /* position past ":" unless a trailing colon after pathname */
587 if(*scanp && *++scanp==0)
593 path=(*scanp ? scanp : 0);
596 /* make sure that there is room for suffixes */
597 suffix_offset = staktell();
598 stakputs(*suffix_list);
599 *stakptr(suffix_offset) = 0;
600 #endif /*SHOPT_VPIX */
605 void path_exec __PARAM__((register const char *arg0,register char *argv[],struct argnod *local), (arg0, argv, local)) __OTORP__(register const char *arg0;register char *argv[];struct argnod *local;){
606 register const char *path = "";
607 nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN);
611 /* name containing / not allowed for restricted shell */
612 if(sh_isoption(SH_RESTRICTED))
613 error(ERROR_exit(1),e_restricted,arg0);
617 /* leave room for inserting _= pathname in environment */
620 sfsync(NIL(Sfio_t*));
621 timerdel(NIL(__V_*));
622 while(path=execs(path,arg0,argv));
624 ((struct checkpt*)sh.jmplist)->mode = SH_JMPEXIT;
625 if((errno = exec_err)==ENOENT)
626 error(ERROR_exit(ERROR_NOENT),e_found,arg0);
628 error(ERROR_system(ERROR_NOEXEC),e_exec,arg0);
632 * This routine constructs a short path consisting of all
633 * Relative directories up to the directory of fullname <name>
635 static char *prune __PARAM__((register char *path,const char *fullname), (path, fullname)) __OTORP__(register char *path;const char *fullname;){
636 register char *p = path;
641 if(!fullname || *fullname != '/' || *path==0)
643 base = path_basename(fullname);
646 /* a null path means current directory */
654 path=path_join(path,base);
655 if(*s != '/' || (n=strcmp(stakptr(PATH_OFFSET),fullname))==0)
657 /* position p past end of path */
669 /* if there is no match just return path */
670 path = nv_getval(nv_scoped(PATHNOD));
672 path = (char*)e_defpath;
678 static char *execs __PARAM__((const char *ap,const char *arg0,register char **argv), (ap, arg0, argv)) __OTORP__(const char *ap;const char *arg0;register char **argv;){
679 register char *path, *prefix;
681 prefix=path_join((char*)ap,arg0);
682 xecenv[0] = stakptr(0);
685 path=stakptr(PATH_OFFSET);
688 if(path_inpath(ap,nv_getval(nv_scoped(DOSPATHNOD))))
691 char *savet = argv[0];
693 argv[-2] = (char*)e_vpix+1;
695 suffix = suffix_list;
699 strcpy(stakptr(suffix_offset),*suffix++);
700 if(canexecute(path,0)>=0)
703 if(path=nv_getval(nv_scoped(VPIXNOD)))
708 execve(stakptr(0), &argv[-2] ,xecenv);
712 error(ERROR_system(ERROR_NOENT),e_found,vp);
714 error(ERROR_system(ERROR_NOEXEC),e_exec,vp);
719 *stakptr(suffix_offset) = 0;
721 #endif /* SHOPT_VPIX */
723 path = path_relative(path);
725 if(*path!='/' && path!=stakptr(PATH_OFFSET))
728 * The following code because execv(foo,) and execv(./foo,)
729 * may not yield the same resulst
731 char *sp = (char*)malloc(strlen(path)+3);
737 #endif /* SHELLMAGIC */
738 execve(path, &argv[0] ,xecenv);
740 if(*path=='.' && path!=stakptr(PATH_OFFSET))
743 path = path_relative(stakptr(PATH_OFFSET));
745 #endif /* SHELLMAGIC */
750 * On apollo's execve will fail with eacces when
751 * file has execute but not read permissions. So,
752 * for now we will pretend that EACCES and ENOEXEC
753 * mean the same thing.
763 if(stat(path,&statb)>=0 && S_ISDIR(statb.st_mode))
770 #endif /* ENAMETOOLONG */
781 error(ERROR_system(ERROR_NOEXEC),e_exec,path);
786 * File is executable but not machine code.
787 * Assume file is a Shell script and execute it.
791 static void exscript __PARAM__((register char *path,register char *argv[]), (path, argv)) __OTORP__(register char *path;register char *argv[];){
796 /* clean up any cooperating processes */
800 sh_close(*sh.outpipe);
803 while(sfstack(sp,SF_POPSTACK));
807 sh_setstate(SH_FORKED);
809 #ifdef SHOPT_SUID_EXEC
810 /* check if file cannot open for read or script is setuid/setgid */
812 static char name[] = "/tmp/euidXXXXXXXXXX";
814 register uid_t euserid;
817 if((n=sh_open(path,O_RDONLY,0)) >= 0)
819 /* move <n> if n=0,1,2 */
821 if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
825 if((euserid=geteuid()) != sh.userid)
827 strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
828 /* create a suid open file with owner equal effective uid */
829 if((n=creat(name,S_ISUID|S_IXUSR)) < 0)
832 /* make sure that file has right owner */
833 if(fstat(n,&statb)<0 || statb.st_uid != euserid)
838 fcntl(n, F_DUPFD, 10);
845 execve(e_suidexec,argv,xecenv);
848 * The following code is just for compatibility
850 if((n=open(path,O_RDONLY,0)) < 0)
851 error(ERROR_system(1),e_open,path);
857 sh.infd = sh_chkopen(path);
858 #endif /* SHOPT_SUID_EXEC */
860 sh_accbegin(path) ; /* reset accounting */
861 #endif /* SHOPT_ACCT */
863 sh.lastarg = strdup(path);
864 /* save name of calling command */
865 sh.readscript = error_info.id;
868 sfclose(sh.heredocs);
871 /* close history file if name has changed */
872 if(sh.hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,sh.hist_ptr->histname))
874 hist_close(sh.hist_ptr);
875 (HISTCUR)->nvalue.lp = 0;
877 sh_offstate(SH_FORKED);
878 siglongjmp(*sh.jmplist,SH_JMPSCRIPT);
882 # include <sys/acct.h>
883 # include "FEATURE/time"
885 static struct acct sabuf;
886 static struct tms buffer;
887 static clock_t before;
888 static char *SHACCT; /* set to value of SHACCT environment variable */
889 static shaccton; /* non-zero causes accounting record to be written */
890 static int compress __PROTO__((time_t));
892 * initialize accounting, i.e., see if SHACCT variable set
894 void sh_accinit __PARAM__((void), ()){
895 SHACCT = getenv("SHACCT");
898 * suspend accounting unitl turned on by sh_accbegin()
900 void sh_accsusp __PARAM__((void), ()){
903 sabuf.ac_flag |= AEXPND;
908 * begin an accounting record by recording start time
910 void sh_accbegin __PARAM__((const char *cmdname), (cmdname)) __OTORP__(const char *cmdname;){
913 sabuf.ac_btime = time(NIL(time_t *));
914 before = times(&buffer);
915 sabuf.ac_uid = getuid();
916 sabuf.ac_gid = getgid();
917 strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
918 sizeof(sabuf.ac_comm));
923 * terminate an accounting record and append to accounting file
925 void sh_accend __PARAM__((void), ()){
931 after = times(&buffer);
932 sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
933 sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
934 sabuf.ac_etime = compress( (time_t)(after-before));
935 fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
936 write(fd, (const char*)&sabuf, sizeof( sabuf ));
942 * Produce a pseudo-floating point representation
943 * with 3 bits base-8 exponent, 13 bits fraction.
945 static int compress __PARAM__((register time_t t), (t)) __OTORP__(register time_t t;){
946 register int exp = 0, rund = 0;
963 return((exp<<13) + t);
965 #endif /* SHOPT_ACCT */