1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * David Korn
23 * AT&T Labs
24 *
25 */
26 /*
27 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
28 */
29
30 #include "defs.h"
31 #include <fcin.h>
32 #include <ls.h>
33 #include <nval.h>
34 #include "variables.h"
35 #include "path.h"
36 #include "io.h"
37 #include "jobs.h"
38 #include "history.h"
39 #include "test.h"
40 #include "FEATURE/dynamic"
41 #include "FEATURE/externs"
42 #if SHOPT_PFSH
43 # ifdef _hdr_exec_attr
44 # include <exec_attr.h>
45 # endif
46 # if _lib_vfork
47 # include <ast_vfork.h>
48 # else
49 # define vfork() fork()
50 # endif
51 #endif
52
53 #define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)
54 #define LIBCMD "cmd"
55
56
57 static int canexecute(Shell_t*,char*,int);
58 static void funload(Shell_t*,int,const char*);
59 static void exscript(Shell_t*,char*, char*[], char**);
60 static int path_chkpaths(Shell_t*,Pathcomp_t*,Pathcomp_t*,Pathcomp_t*,int);
61 static void path_checkdup(Shell_t *shp,register Pathcomp_t*);
62
63 static const char *std_path;
64
onstdpath(const char * name)65 static int onstdpath(const char *name)
66 {
67 register const char *cp = std_path, *sp;
68 if(cp)
69 while(*cp)
70 {
71 for(sp=name; *sp && (*cp == *sp); sp++,cp++);
72 if(*sp==0 && (*cp==0 || *cp==':'))
73 return(1);
74 while(*cp && *cp++!=':');
75 }
76 return(0);
77 }
78
79 #if SHOPT_PFSH
path_xattr(Shell_t * shp,const char * path,char * rpath)80 int path_xattr(Shell_t *shp, const char *path, char *rpath)
81 {
82 char resolvedpath[PATH_MAX + 1];
83 if (shp->gd->user && *shp->gd->user)
84 {
85 execattr_t *pf;
86 if(!rpath)
87 rpath = resolvedpath;
88 if (!realpath(path, resolvedpath))
89 return -1;
90 if(pf=getexecuser(shp->gd->user, KV_COMMAND, resolvedpath, GET_ONE))
91 {
92 if (!pf->attr || pf->attr->length == 0)
93 {
94 free_execattr(pf);
95 return(0);
96 }
97 free_execattr(pf);
98 return(1);
99 }
100 }
101 errno = ENOENT;
102 return(-1);
103 }
104 #endif /* SHOPT_PFSH */
105
path_pfexecve(Shell_t * shp,const char * path,char * argv[],char * const envp[],int spawn)106 static pid_t path_pfexecve(Shell_t *shp,const char *path, char *argv[],char *const envp[],int spawn)
107 {
108 #if SHOPT_PFSH
109 char resolvedpath[PATH_MAX + 1];
110 pid_t pid;
111 if(spawn)
112 {
113 while((pid = vfork()) < 0)
114 _sh_fork(shp,pid, 0, (int*)0);
115 if(pid)
116 return(pid);
117 }
118 if(!sh_isoption(SH_PFSH))
119 return(execve(path, argv, envp));
120 /* Solaris implements realpath(3C) using the resolvepath(2) */
121 /* system call so we can save us to call access(2) first */
122
123 /* we can exec the command directly instead of via pfexec(1) if */
124 /* there is a matching entry without attributes in exec_attr(5) */
125 if(!path_xattr(shp,path,resolvedpath))
126 return(execve(path, argv, envp));
127 --argv;
128 argv[0] = argv[1];
129 argv[1] = resolvedpath;
130 return(execve("/usr/bin/pfexec", argv, envp));
131 #else
132 return(execve(path, argv, envp));
133 #endif
134 }
135
136
_spawnveg(Shell_t * shp,const char * path,char * const argv[],char * const envp[],pid_t pgid)137 static pid_t _spawnveg(Shell_t *shp,const char *path, char* const argv[], char* const envp[], pid_t pgid)
138 {
139 pid_t pid;
140 while(1)
141 {
142 sh_stats(STAT_SPAWN);
143 pid = spawnveg(path,argv,envp,pgid);
144 if(pid>=0 || errno!=EAGAIN)
145 break;
146 }
147 return(pid);
148 }
149
150 /*
151 * used with command -x to run the command in multiple passes
152 * spawn is non-zero when invoked via spawn
153 * the exitval is set to the maximum for each execution
154 */
path_xargs(Shell_t * shp,const char * path,char * argv[],char * const envp[],int spawn)155 static pid_t path_xargs(Shell_t *shp,const char *path, char *argv[],char *const envp[], int spawn)
156 {
157 register char *cp, **av, **xv;
158 char **avlast= &argv[shp->xargmax], **saveargs=0;
159 char *const *ev;
160 long size, left;
161 int nlast=1,n,exitval=0;
162 pid_t pid;
163 if(shp->xargmin < 0)
164 return((pid_t)-1);
165 size = shp->gd->lim.arg_max-1024;
166 for(ev=envp; cp= *ev; ev++)
167 size -= strlen(cp)-1;
168 for(av=argv; (cp= *av) && av< &argv[shp->xargmin]; av++)
169 size -= strlen(cp)-1;
170 for(av=avlast; cp= *av; av++,nlast++)
171 size -= strlen(cp)-1;
172 av = &argv[shp->xargmin];
173 if(!spawn)
174 job_clear();
175 shp->exitval = 0;
176 while(av<avlast)
177 {
178 for(xv=av,left=size; left>0 && av<avlast;)
179 left -= strlen(*av++)+1;
180 /* leave at least two for last */
181 if(left<0 && (avlast-av)<2)
182 av--;
183 if(xv==&argv[shp->xargmin])
184 {
185 n = nlast*sizeof(char*);
186 saveargs = (char**)malloc(n);
187 memcpy((void*)saveargs, (void*)av, n);
188 memcpy((void*)av,(void*)avlast,n);
189 }
190 else
191 {
192 for(n=shp->xargmin; xv < av; xv++)
193 argv[n++] = *xv;
194 for(xv=avlast; cp= *xv; xv++)
195 argv[n++] = cp;
196 argv[n] = 0;
197 }
198 if(saveargs || av<avlast || (exitval && !spawn))
199 {
200 if((pid=_spawnveg(shp,path,argv,envp,0)) < 0)
201 return(-1);
202 job_post(shp,pid,0);
203 job_wait(pid);
204 if(shp->exitval>exitval)
205 exitval = shp->exitval;
206 if(saveargs)
207 {
208 memcpy((void*)av,saveargs,n);
209 free((void*)saveargs);
210 saveargs = 0;
211 }
212 }
213 else if(spawn && !sh_isoption(SH_PFSH))
214 {
215 shp->xargexit = exitval;
216 if(saveargs)
217 free((void*)saveargs);
218 return(_spawnveg(shp,path,argv,envp,spawn>>1));
219 }
220 else
221 {
222 if(saveargs)
223 free((void*)saveargs);
224 return(path_pfexecve(shp,path,argv,envp,spawn));
225 }
226 }
227 if(!spawn)
228 exit(exitval);
229 return((pid_t)-1);
230 }
231
232 /*
233 * make sure PWD is set up correctly
234 * Return the present working directory
235 * Invokes getcwd() if flag==0 and if necessary
236 * Sets the PWD variable to this value
237 */
path_pwd(Shell_t * shp,int flag)238 char *path_pwd(Shell_t *shp,int flag)
239 {
240 register char *cp;
241 register char *dfault = (char*)e_dot;
242 register int count = 0;
243 if(shp->pwd)
244 return((char*)shp->pwd);
245 while(1)
246 {
247 /* try from lowest to highest */
248 switch(count++)
249 {
250 case 0:
251 cp = nv_getval(PWDNOD);
252 break;
253 case 1:
254 cp = nv_getval(HOME);
255 break;
256 case 2:
257 cp = "/";
258 break;
259 case 3:
260 cp = (char*)e_crondir;
261 if(flag) /* skip next case when non-zero flag */
262 ++count;
263 break;
264 case 4:
265 {
266 if(cp=getcwd(NIL(char*),0))
267 {
268 nv_offattr(PWDNOD,NV_NOFREE);
269 _nv_unset(PWDNOD,0);
270 PWDNOD->nvalue.cp = cp;
271 goto skip;
272 }
273 break;
274 }
275 case 5:
276 return(dfault);
277 }
278 if(cp && *cp=='/' && test_inode(cp,e_dot))
279 break;
280 }
281 if(count>1)
282 {
283 nv_offattr(PWDNOD,NV_NOFREE);
284 nv_putval(PWDNOD,cp,NV_RDONLY);
285 }
286 skip:
287 nv_onattr(PWDNOD,NV_NOFREE|NV_EXPORT);
288 shp->pwd = (char*)(PWDNOD->nvalue.cp);
289 return(cp);
290 }
291
292 /*
293 * delete current Pathcomp_t structure
294 */
path_delete(Pathcomp_t * first)295 void path_delete(Pathcomp_t *first)
296 {
297 register Pathcomp_t *pp=first, *old=0, *ppnext;
298 while(pp)
299 {
300 ppnext = pp->next;
301 if(--pp->refcount<=0)
302 {
303 if(pp->lib)
304 free((void*)pp->lib);
305 if(pp->bbuf)
306 free((void*)pp->bbuf);
307 free((void*)pp);
308 if(old)
309 old->next = ppnext;
310 }
311 else
312 old = pp;
313 pp = ppnext;
314 }
315 }
316
317 /*
318 * returns library variable from .paths
319 * The value might be returned on the stack overwriting path
320 */
path_lib(Shell_t * shp,Pathcomp_t * pp,char * path)321 static char *path_lib(Shell_t *shp,Pathcomp_t *pp, char *path)
322 {
323 register char *last = strrchr(path,'/');
324 register int r;
325 struct stat statb;
326 if(last)
327 *last = 0;
328 else
329 path = ".";
330 r = stat(path,&statb);
331 if(last)
332 *last = '/';
333 if(r>=0)
334 {
335 Pathcomp_t pcomp;
336 char save[8];
337 for( ;pp; pp=pp->next)
338 {
339 path_checkdup(shp,pp);
340 if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime)
341 return(pp->lib);
342 }
343 pcomp.len = 0;
344 if(last)
345 pcomp.len = last-path;
346 memcpy((void*)save, (void*)stakptr(PATH_OFFSET+pcomp.len),sizeof(save));
347 if(path_chkpaths(shp,(Pathcomp_t*)0,(Pathcomp_t*)0,&pcomp,PATH_OFFSET))
348 return(stakfreeze(1));
349 memcpy((void*)stakptr(PATH_OFFSET+pcomp.len),(void*)save,sizeof(save));
350 }
351 return(0);
352 }
353
354 #if 0
355 void path_dump(register Pathcomp_t *pp)
356 {
357 sfprintf(sfstderr,"dump\n");
358 while(pp)
359 {
360 sfprintf(sfstderr,"pp=%x dev=%d ino=%d len=%d flags=%o name=%.*s\n",
361 pp,pp->dev,pp->ino,pp->len,pp->flags,pp->len,pp->name);
362 pp = pp->next;
363 }
364 }
365 #endif
366
367 /*
368 * check for duplicate directories on PATH
369 */
path_checkdup(Shell_t * shp,register Pathcomp_t * pp)370 static void path_checkdup(Shell_t *shp,register Pathcomp_t *pp)
371 {
372 register char *name = pp->name;
373 register Pathcomp_t *oldpp,*first;
374 register int flag=0;
375 struct stat statb;
376 if(stat(name,&statb)<0 || !S_ISDIR(statb.st_mode))
377 {
378 pp->flags |= PATH_SKIP;
379 pp->dev = *name=='/';
380 return;
381 }
382 pp->mtime = statb.st_mtime;
383 pp->ino = statb.st_ino;
384 pp->dev = statb.st_dev;
385 if(*name=='/' && onstdpath(name))
386 flag = PATH_STD_DIR;
387 first = (pp->flags&PATH_CDPATH)?(Pathcomp_t*)shp->cdpathlist:path_get(shp,"");
388 for(oldpp=first; oldpp && oldpp!=pp; oldpp=oldpp->next)
389 {
390 if(pp->ino==oldpp->ino && pp->dev==oldpp->dev && pp->mtime==oldpp->mtime)
391 {
392 flag |= PATH_SKIP;
393 break;
394 }
395 }
396 pp->flags |= flag;
397 if(((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH))
398 {
399 int offset = staktell();
400 stakputs(name);
401 path_chkpaths(shp,first,0,pp,offset);
402 stakseek(offset);
403 }
404 }
405
406 /*
407 * write the next path to search on the current stack
408 * if last is given, all paths that come before <last> are skipped
409 * the next pathcomp is returned.
410 */
path_nextcomp(Shell_t * shp,register Pathcomp_t * pp,const char * name,Pathcomp_t * last)411 Pathcomp_t *path_nextcomp(Shell_t *shp,register Pathcomp_t *pp, const char *name, Pathcomp_t *last)
412 {
413 Pathcomp_t *ppnext;
414 stakseek(PATH_OFFSET);
415 if(*name=='/')
416 pp = 0;
417 else
418 {
419 for(;pp && pp!=last;pp=ppnext)
420 {
421 ppnext = pp->next;
422 if(!pp->dev && !pp->ino)
423 path_checkdup(shp,pp);
424 if(pp->flags&PATH_SKIP)
425 return(ppnext);
426 if(!last || *pp->name!='/')
427 break;
428 }
429 if(!pp) /* this should not happen */
430 pp = last;
431 }
432 if(pp && (pp->name[0]!='.' || pp->name[1]))
433 {
434 if(*pp->name!='/')
435 {
436 stakputs(path_pwd(shp,1));
437 if(*stakptr(staktell()-1)!='/')
438 stakputc('/');
439 }
440 stakwrite(pp->name,pp->len);
441 if(pp->name[pp->len-1]!='/')
442 stakputc('/');
443 }
444 stakputs(name);
445 stakputc(0);
446 while(pp && pp!=last && (pp=pp->next))
447 {
448 if(!(pp->flags&PATH_SKIP))
449 return(pp);
450 }
451 return((Pathcomp_t*)0);
452 }
453
defpath_init(Shell_t * shp)454 static Pathcomp_t* defpath_init(Shell_t *shp)
455 {
456 Pathcomp_t *pp = (void*)path_addpath(shp,(Pathcomp_t*)0,(std_path),PATH_PATH);
457 return(pp);
458 }
459
path_init(Shell_t * shp)460 static void path_init(Shell_t *shp)
461 {
462 const char *val;
463 Pathcomp_t *pp;
464 if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*))))
465 std_path = e_defpath;
466 if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp)
467 {
468 shp->pathlist = pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_PATH);
469 }
470 else
471 {
472 if(!(pp=(Pathcomp_t*)shp->defpathlist))
473 pp = defpath_init(shp);
474 shp->pathlist = (void*)path_dup(pp);
475 }
476 if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp)
477 {
478 pp = (void*)path_addpath(shp,(Pathcomp_t*)shp->pathlist,val,PATH_FPATH);
479 }
480 }
481
482 /*
483 * returns that pathlist to search
484 */
path_get(register Shell_t * shp,register const char * name)485 Pathcomp_t *path_get(register Shell_t *shp,register const char *name)
486 {
487 register Pathcomp_t *pp=0;
488 if(*name && strchr(name,'/'))
489 return(0);
490 if(!sh_isstate(SH_DEFPATH))
491 {
492 if(!shp->pathlist)
493 path_init(shp);
494 pp = (Pathcomp_t*)shp->pathlist;
495 }
496 if(!pp && (!(sh_scoped(shp,PATHNOD)->nvalue.cp)) || sh_isstate(SH_DEFPATH))
497 {
498 if(!(pp=(Pathcomp_t*)shp->defpathlist))
499 pp = defpath_init(shp);
500 }
501 return(pp);
502 }
503
504 /*
505 * open file corresponding to name using path give by <pp>
506 */
path_opentype(Shell_t * shp,const char * name,register Pathcomp_t * pp,int fun)507 static int path_opentype(Shell_t *shp,const char *name, register Pathcomp_t *pp, int fun)
508 {
509 register int fd= -1;
510 struct stat statb;
511 Pathcomp_t *oldpp;
512 if(!pp && !shp->pathlist)
513 path_init(shp);
514 if(!fun && strchr(name,'/'))
515 {
516 if(sh_isoption(SH_RESTRICTED))
517 errormsg(SH_DICT,ERROR_exit(1),e_restricted,name);
518 }
519 do
520 {
521 pp = path_nextcomp(shp,oldpp=pp,name,0);
522 while(oldpp && (oldpp->flags&PATH_SKIP))
523 oldpp = oldpp->next;
524 if(fun && (!oldpp || !(oldpp->flags&PATH_FPATH)))
525 continue;
526 if((fd = sh_open(path_relative(shp,stakptr(PATH_OFFSET)),O_RDONLY,0)) >= 0)
527 {
528 if(fstat(fd,&statb)<0 || S_ISDIR(statb.st_mode))
529 {
530 errno = EISDIR;
531 sh_close(fd);
532 fd = -1;
533 }
534 }
535 }
536 while( fd<0 && pp);
537 if(fd>=0 && (fd = sh_iomovefd(fd)) > 0)
538 {
539 fcntl(fd,F_SETFD,FD_CLOEXEC);
540 VALIDATE_FD(shp, fd);
541 shp->fdstatus[fd] |= IOCLEX;
542 }
543 return(fd);
544 }
545
546 /*
547 * open file corresponding to name using path give by <pp>
548 */
path_open(Shell_t * shp,const char * name,register Pathcomp_t * pp)549 int path_open(Shell_t *shp,const char *name, register Pathcomp_t *pp)
550 {
551 return(path_opentype(shp,name,pp,0));
552 }
553
554 /*
555 * given a pathname return the base name
556 */
557
path_basename(register const char * name)558 char *path_basename(register const char *name)
559 {
560 register const char *start = name;
561 while (*name)
562 if ((*name++ == '/') && *name) /* don't trim trailing / */
563 start = name;
564 return ((char*)start);
565 }
566
path_fullname(Shell_t * shp,const char * name)567 char *path_fullname(Shell_t *shp,const char *name)
568 {
569 int len=strlen(name)+1,dirlen=0;
570 char *path,*pwd;
571 if(*name!='/')
572 {
573 pwd = path_pwd(shp,1);
574 dirlen = strlen(pwd)+1;
575 }
576 path = (char*)malloc(len+dirlen);
577 if(dirlen)
578 {
579 memcpy((void*)path,(void*)pwd,dirlen);
580 path[dirlen-1] = '/';
581 }
582 memcpy((void*)&path[dirlen],(void*)name,len);
583 pathcanon(path,0);
584 return(path);
585 }
586
587 /*
588 * load functions from file <fno>
589 */
funload(Shell_t * shp,int fno,const char * name)590 static void funload(Shell_t *shp,int fno, const char *name)
591 {
592 char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1];
593 Namval_t *np;
594 struct Ufunction *rp,*rpfirst;
595 int savestates = sh_getstate(), oldload=shp->funload;
596 pname = path_fullname(shp,stakptr(PATH_OFFSET));
597 if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname)))
598 {
599 Dt_t *funtree = sh_subfuntree(1);
600 while(1)
601 {
602 rpfirst = dtprev(shp->fpathdict,rp);
603 if(!rpfirst || strcmp(pname,rpfirst->fname))
604 break;
605 rp = rpfirst;
606 }
607 do
608 {
609 if((np = dtsearch(funtree,rp->np)) && is_afunction(np))
610 {
611 if(np->nvalue.rp)
612 np->nvalue.rp->fdict = 0;
613 nv_delete(np,funtree,NV_NOFREE);
614 }
615 dtinsert(funtree,rp->np);
616 rp->fdict = funtree;
617 }
618 while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0);
619 sh_close(fno);
620 return;
621 }
622 sh_onstate(SH_NOLOG);
623 sh_onstate(SH_NOALIAS);
624 shp->readscript = (char*)name;
625 shp->st.filename = pname;
626 shp->funload = 1;
627 error_info.line = 0;
628 sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),SH_FUNEVAL);
629 sh_close(fno);
630 shp->readscript = 0;
631 #if SHOPT_NAMESPACE
632 if(shp->namespace)
633 np = sh_fsearch(shp,name,0);
634 else
635 #endif /* SHOPT_NAMESPACE */
636 np = nv_search(name,shp->fun_tree,0);
637 if(!np || !np->nvalue.ip)
638 pname = stakcopy(shp->st.filename);
639 else
640 pname = 0;
641 free((void*)shp->st.filename);
642 shp->funload = oldload;
643 shp->st.filename = oldname;
644 sh_setstate(savestates);
645 if(pname)
646 errormsg(SH_DICT,ERROR_exit(ERROR_NOEXEC),e_funload,name,pname);
647 }
648
649 /*
650 * do a path search and track alias if requested
651 * if flag is 0, or if name not found, then try autoloading function
652 * if flag==2 or 3, returns 1 if name found on FPATH
653 * if flag==3 no tracked alias will be set
654 * returns 1, if function was autoloaded.
655 * If oldpp is not NULL, it will contain a pointer to the path component
656 * where it was found.
657 */
658
path_search(Shell_t * shp,register const char * name,Pathcomp_t ** oldpp,int flag)659 int path_search(Shell_t *shp,register const char *name,Pathcomp_t **oldpp, int flag)
660 {
661 register Namval_t *np;
662 register int fno;
663 Pathcomp_t *pp=0;
664 if(name && strchr(name,'/'))
665 {
666 stakseek(PATH_OFFSET);
667 stakputs(name);
668 if(canexecute(shp,stakptr(PATH_OFFSET),0)<0)
669 {
670 *stakptr(PATH_OFFSET) = 0;
671 return(0);
672 }
673 if(*name=='/')
674 return(1);
675 stakseek(PATH_OFFSET);
676 stakputs(path_pwd(shp,1));
677 stakputc('/');
678 stakputs(name);
679 stakputc(0);
680 return(0);
681 }
682 if(sh_isstate(SH_DEFPATH))
683 {
684 if(!shp->defpathlist)
685 defpath_init(shp);
686 }
687 else if(!shp->pathlist)
688 path_init(shp);
689 if(flag)
690 {
691 if(!(flag&1) && (np=nv_search(name,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && (pp=(Pathcomp_t*)np->nvalue.cp))
692 {
693 stakseek(PATH_OFFSET);
694 path_nextcomp(shp,pp,name,pp);
695 if(oldpp)
696 *oldpp = pp;
697 stakputc(0);
698 return(0);
699 }
700 pp = path_absolute(shp,name,oldpp?*oldpp:NIL(Pathcomp_t*));
701 if(oldpp)
702 *oldpp = pp;
703 if(!pp && (np=nv_search(name,shp->fun_tree,0))&&np->nvalue.ip)
704 return(1);
705 if(!pp)
706 *stakptr(PATH_OFFSET) = 0;
707 }
708 if(flag==0 || !pp || (pp->flags&PATH_FPATH))
709 {
710 if(!pp)
711 pp=sh_isstate(SH_DEFPATH)?shp->defpathlist:shp->pathlist;
712 if(pp && strmatch(name,e_alphanum) && (fno=path_opentype(shp,name,pp,1))>=0)
713 {
714 if(flag==2)
715 {
716 sh_close(fno);
717 return(1);
718 }
719 funload(shp,fno,name);
720 return(1);
721 }
722 *stakptr(PATH_OFFSET) = 0;
723 return(0);
724 }
725 else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3)
726 {
727 if(np=nv_search(name,shp->track_tree,NV_ADD))
728 path_alias(np,pp);
729 }
730 return(0);
731 }
732
733 /*
734 * do a path search and find the full pathname of file name
735 */
path_absolute(Shell_t * shp,register const char * name,Pathcomp_t * pp)736 Pathcomp_t *path_absolute(Shell_t *shp,register const char *name, Pathcomp_t *pp)
737 {
738 register int f,isfun;
739 int noexec=0;
740 Pathcomp_t *oldpp;
741 Namval_t *np;
742 char *cp;
743 char *bp;
744 shp->path_err = ENOENT;
745 if(!pp && !(pp=path_get(shp,"")))
746 return(0);
747 shp->path_err = 0;
748 while(1)
749 {
750 sh_sigcheck(shp);
751 shp->bltin_dir = 0;
752 while(oldpp=pp)
753 {
754 pp = path_nextcomp(shp,pp,name,0);
755 if(!(oldpp->flags&PATH_SKIP))
756 break;
757 }
758 if(!oldpp)
759 {
760 shp->path_err = ENOENT;
761 return(0);
762 }
763 isfun = (oldpp->flags&PATH_FPATH);
764 if(!isfun && !sh_isoption(SH_RESTRICTED))
765 {
766 #if SHOPT_DYNAMIC
767 Shbltin_f addr;
768 int n;
769 #endif
770 if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
771 return(oldpp);
772 #if SHOPT_DYNAMIC
773 n = staktell();
774 stakputs("b_");
775 stakputs(name);
776 stakputc(0);
777 if((addr = sh_getlib(shp, stakptr(n), oldpp)) &&
778 (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
779 nv_isattr(np,NV_BLTINOPT))
780 {
781 shp->bltin_dir = 0;
782 return(oldpp);
783 }
784 stakseek(n);
785 while(bp = oldpp->blib)
786 {
787 char *fp;
788 void *dll;
789 int m;
790 if(fp = strchr(bp, ':'))
791 {
792 *fp++ = 0;
793 oldpp->blib = fp;
794 fp = 0;
795 }
796 else
797 {
798 fp = oldpp->bbuf;
799 oldpp->blib = oldpp->bbuf = 0;
800 }
801 n = staktell();
802 stakputs("b_");
803 stakputs(name);
804 stakputc(0);
805 m = staktell();
806 shp->bltin_dir = oldpp->name;
807 if(*bp!='/')
808 {
809 stakputs(oldpp->name);
810 stakputc('/');
811 }
812 stakputs(bp);
813 stakputc(0);
814 if(cp = strrchr(stakptr(m),'/'))
815 cp++;
816 else
817 cp = stakptr(m);
818 if(!strcmp(cp,LIBCMD) &&
819 (addr=(Shbltin_f)dlllook((void*)0,stakptr(n))) &&
820 (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) &&
821 nv_isattr(np,NV_BLTINOPT))
822 {
823 found:
824 if(fp)
825 free(fp);
826 shp->bltin_dir = 0;
827 return(oldpp);
828 }
829 #ifdef SH_PLUGIN_VERSION
830 if (dll = dllplugin(SH_ID, stakptr(m), NiL, SH_PLUGIN_VERSION, NiL, RTLD_LAZY, NiL, 0))
831 sh_addlib(shp,dll,stakptr(m),oldpp);
832 #else
833 #if (_AST_VERSION>=20040404)
834 if (dll = dllplug(SH_ID, stakptr(m), NiL, RTLD_LAZY, NiL, 0))
835 #else
836 if (dll = dllfind(stakptr(m), NiL, RTLD_LAZY, NiL, 0))
837 #endif
838 {
839 /*
840 * this detects the 2007-05-11 builtin context change and also
841 * the 2008-03-30 opt_info.num change that hit libcmd::b_head
842 */
843
844 if (libcmd && !dlllook(dll, "b_pids"))
845 {
846 dlclose(dll);
847 dll = 0;
848 }
849 else
850 sh_addlib(shp,dll,stakptr(m),oldpp);
851 }
852 #endif
853 if(dll &&
854 (addr=(Shbltin_f)dlllook(dll,stakptr(n))) &&
855 (!(np = sh_addbuiltin(stakptr(PATH_OFFSET),NiL,NiL)) || np->nvalue.bfp!=(Nambfp_f)addr) &&
856 (np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)))
857 {
858 np->nvenv = dll;
859 goto found;
860 }
861 if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),shp->bltin_tree,0))
862 goto found;
863 if(fp)
864 free(fp);
865 stakseek(n);
866 }
867 #endif /* SHOPT_DYNAMIC */
868 }
869 shp->bltin_dir = 0;
870 sh_stats(STAT_PATHS);
871 f = canexecute(shp,stakptr(PATH_OFFSET),isfun);
872 if(isfun && f>=0 && (cp = strrchr(name,'.')))
873 {
874 *cp = 0;
875 if(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE))
876 f = -1;
877 *cp = '.';
878 }
879 if(isfun && f>=0)
880 {
881 nv_onattr(nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE),NV_LTOU|NV_FUNCTION);
882 funload(shp,f,name);
883 close(f);
884 f = -1;
885 return(0);
886 }
887 else if(f>=0 && (oldpp->flags & PATH_STD_DIR))
888 {
889 int n = staktell();
890 stakputs("/bin/");
891 stakputs(name);
892 stakputc(0);
893 np = nv_search(stakptr(n),shp->bltin_tree,0);
894 stakseek(n);
895 if(np)
896 {
897 n = np->nvflag;
898 np = sh_addbuiltin(stakptr(PATH_OFFSET),(Shbltin_f)np->nvalue.bfp,nv_context(np));
899 np->nvflag = n;
900 }
901 }
902 if(!pp || f>=0)
903 break;
904 if(errno!=ENOENT)
905 noexec = errno;
906 }
907 if(f<0)
908 {
909 shp->path_err = (noexec?noexec:ENOENT);
910 return(0);
911 }
912 stakputc(0);
913 return(oldpp);
914 }
915
916 /*
917 * returns 0 if path can execute
918 * sets exec_err if file is found but can't be executable
919 */
920 #undef S_IXALL
921 #ifdef S_IXUSR
922 # define S_IXALL (S_IXUSR|S_IXGRP|S_IXOTH)
923 #else
924 # ifdef S_IEXEC
925 # define S_IXALL (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))
926 # else
927 # define S_IXALL 0111
928 # endif /*S_EXEC */
929 #endif /* S_IXUSR */
930
canexecute(Shell_t * shp,register char * path,int isfun)931 static int canexecute(Shell_t *shp,register char *path, int isfun)
932 {
933 struct stat statb;
934 register int fd=0;
935 path = path_relative(shp,path);
936 if(isfun)
937 {
938 if((fd=open(path,O_RDONLY,0))<0 || fstat(fd,&statb)<0)
939 goto err;
940 }
941 else if(stat(path,&statb) < 0)
942 {
943 #if _WINIX
944 /* check for .exe or .bat suffix */
945 char *cp;
946 if(errno==ENOENT && (!(cp=strrchr(path,'.')) || strlen(cp)>4 || strchr(cp,'/')))
947 {
948 int offset = staktell()-1;
949 stakseek(offset);
950 stakputs(".bat");
951 path = stakptr(PATH_OFFSET);
952 if(stat(path,&statb) < 0)
953 {
954 if(errno!=ENOENT)
955 goto err;
956 memcpy(stakptr(offset),".sh",4);
957 if(stat(path,&statb) < 0)
958 goto err;
959 }
960 }
961 else
962 #endif /* _WINIX */
963 goto err;
964 }
965 errno = EPERM;
966 if(S_ISDIR(statb.st_mode))
967 errno = EISDIR;
968 else if((statb.st_mode&S_IXALL)==S_IXALL || sh_access(path,X_OK)>=0)
969 return(fd);
970 if(isfun && fd>=0)
971 sh_close(fd);
972 err:
973 return(-1);
974 }
975
976 /*
977 * Return path relative to present working directory
978 */
979
path_relative(Shell_t * shp,register const char * file)980 char *path_relative(Shell_t *shp,register const char* file)
981 {
982 register const char *pwd;
983 register const char *fp = file;
984 /* can't relpath when shp->pwd not set */
985 if(!(pwd=shp->pwd))
986 return((char*)fp);
987 while(*pwd==*fp)
988 {
989 if(*pwd++==0)
990 return((char*)e_dot);
991 fp++;
992 }
993 if(*pwd==0 && *fp == '/')
994 {
995 while(*++fp=='/');
996 if(*fp)
997 return((char*)fp);
998 return((char*)e_dot);
999 }
1000 return((char*)file);
1001 }
1002
path_exec(Shell_t * shp,register const char * arg0,register char * argv[],struct argnod * local)1003 void path_exec(Shell_t *shp,register const char *arg0,register char *argv[],struct argnod *local)
1004 {
1005 char **envp;
1006 const char *opath;
1007 Pathcomp_t *libpath, *pp=0;
1008 int slash=0;
1009 nv_setlist(local,NV_EXPORT|NV_IDENT|NV_ASSIGN,0);
1010 envp = sh_envgen();
1011 if(strchr(arg0,'/'))
1012 {
1013 slash=1;
1014 /* name containing / not allowed for restricted shell */
1015 if(sh_isoption(SH_RESTRICTED))
1016 errormsg(SH_DICT,ERROR_exit(1),e_restricted,arg0);
1017 }
1018 else
1019 pp=path_get(shp,arg0);
1020 shp->path_err= ENOENT;
1021 sfsync(NIL(Sfio_t*));
1022 timerdel(NIL(void*));
1023 /* find first path that has a library component */
1024 while(pp && (pp->flags&PATH_SKIP))
1025 pp = pp->next;
1026 if(pp || slash) do
1027 {
1028 sh_sigcheck(shp);
1029 if(libpath=pp)
1030 {
1031 pp = path_nextcomp(shp,pp,arg0,0);
1032 opath = stakfreeze(1)+PATH_OFFSET;
1033 }
1034 else
1035 opath = arg0;
1036 path_spawn(shp,opath,argv,envp,libpath,0);
1037 while(pp && (pp->flags&PATH_FPATH))
1038 pp = path_nextcomp(shp,pp,arg0,0);
1039 }
1040 while(pp);
1041 /* force an exit */
1042 ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
1043 if((errno=shp->path_err)==ENOENT)
1044 errormsg(SH_DICT,ERROR_exit(ERROR_NOENT),e_found,arg0);
1045 else
1046 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,arg0);
1047 }
1048
path_spawn(Shell_t * shp,const char * opath,register char ** argv,char ** envp,Pathcomp_t * libpath,int spawn)1049 pid_t path_spawn(Shell_t *shp,const char *opath,register char **argv, char **envp, Pathcomp_t *libpath, int spawn)
1050 {
1051 register char *path;
1052 char **xp=0, *xval, *libenv = (libpath?libpath->lib:0);
1053 Namval_t* np;
1054 char *s, *v;
1055 int r, n, pidsize;
1056 pid_t pid= -1;
1057 /* leave room for inserting _= pathname in environment */
1058 envp--;
1059 #if _lib_readlink
1060 /* save original pathname */
1061 stakseek(PATH_OFFSET);
1062 pidsize = sfprintf(stkstd,"*%d*",spawn?getpid():getppid());
1063 stakputs(opath);
1064 opath = stakfreeze(1)+PATH_OFFSET+pidsize;
1065 np=nv_search(argv[0],shp->track_tree,0);
1066 while(libpath && !libpath->lib)
1067 libpath=libpath->next;
1068 if(libpath && (!np || nv_size(np)>0))
1069 {
1070 /* check for symlink and use symlink name */
1071 char buff[PATH_MAX+1];
1072 char save[PATH_MAX+1];
1073 stakseek(PATH_OFFSET);
1074 stakputs(opath);
1075 path = stakptr(PATH_OFFSET);
1076 while((n=readlink(path,buff,PATH_MAX))>0)
1077 {
1078 buff[n] = 0;
1079 n = PATH_OFFSET;
1080 r = 0;
1081 if((v=strrchr(path,'/')) && *buff!='/')
1082 {
1083 if(buff[0]=='.' && buff[1]=='.' && (r = strlen(path) + 1) <= PATH_MAX)
1084 memcpy(save, path, r);
1085 else
1086 r = 0;
1087 n += (v+1-path);
1088 }
1089 stakseek(n);
1090 stakputs(buff);
1091 stakputc(0);
1092 path = stakptr(PATH_OFFSET);
1093 if(v && buff[0]=='.' && buff[1]=='.')
1094 {
1095 pathcanon(path, 0);
1096 if(r && access(path,X_OK))
1097 {
1098 memcpy(path, save, r);
1099 break;
1100 }
1101 }
1102 if(libenv = path_lib(shp,libpath,path))
1103 break;
1104 }
1105 stakseek(0);
1106 }
1107 #endif
1108 if(libenv && (v = strchr(libenv,'=')))
1109 {
1110 n = v - libenv;
1111 *v = 0;
1112 np = nv_open(libenv,shp->var_tree,0);
1113 *v = '=';
1114 s = nv_getval(np);
1115 stakputs(libenv);
1116 if(s)
1117 {
1118 stakputc(':');
1119 stakputs(s);
1120 }
1121 v = stakfreeze(1);
1122 r = 1;
1123 xp = envp + 1;
1124 while (s = *xp++)
1125 {
1126 if (strneq(s, v, n) && s[n] == '=')
1127 {
1128 xval = *--xp;
1129 *xp = v;
1130 r = 0;
1131 break;
1132 }
1133 }
1134 if (r)
1135 {
1136 *envp-- = v;
1137 xp = 0;
1138 }
1139 }
1140 if(!opath)
1141 opath = stakptr(PATH_OFFSET);
1142 envp[0] = (char*)opath-(PATH_OFFSET+pidsize);
1143 envp[0][0] = '_';
1144 envp[0][1] = '=';
1145 sfsync(sfstderr);
1146 sh_sigcheck(shp);
1147 path = path_relative(shp,opath);
1148 #ifdef SHELLMAGIC
1149 if(*path!='/' && path!=opath)
1150 {
1151 /*
1152 * The following code because execv(foo,) and execv(./foo,)
1153 * may not yield the same results
1154 */
1155 char *sp = (char*)malloc(strlen(path)+3);
1156 sp[0] = '.';
1157 sp[1] = '/';
1158 strcpy(sp+2,path);
1159 path = sp;
1160 }
1161 #endif /* SHELLMAGIC */
1162 if(spawn && !sh_isoption(SH_PFSH))
1163 pid = _spawnveg(shp,opath, &argv[0],envp, spawn>>1);
1164 else
1165 pid = path_pfexecve(shp,opath, &argv[0] ,envp,spawn);
1166 if(xp)
1167 *xp = xval;
1168 #ifdef SHELLMAGIC
1169 if(*path=='.' && path!=opath)
1170 {
1171 free(path);
1172 path = path_relative(shp,opath);
1173 }
1174 #endif /* SHELLMAGIC */
1175 if(pid>0)
1176 return(pid);
1177 retry:
1178 switch(shp->path_err = errno)
1179 {
1180 #ifdef apollo
1181 /*
1182 * On apollo's execve will fail with eacces when
1183 * file has execute but not read permissions. So,
1184 * for now we will pretend that EACCES and ENOEXEC
1185 * mean the same thing.
1186 */
1187 case EACCES:
1188 #endif /* apollo */
1189 case ENOEXEC:
1190 #if SHOPT_SUID_EXEC
1191 case EPERM:
1192 /* some systems return EPERM if setuid bit is on */
1193 #endif
1194 errno = ENOEXEC;
1195 if(spawn)
1196 {
1197 #ifdef _lib_fork
1198 if(shp->subshell)
1199 return(-1);
1200 do
1201 {
1202 if((pid=fork())>0)
1203 return(pid);
1204 }
1205 while(_sh_fork(shp,pid,0,(int*)0) < 0);
1206 ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT;
1207 #else
1208 return(-1);
1209 #endif
1210 }
1211 exscript(shp,path,argv,envp);
1212 #ifndef apollo
1213 /* FALLTHROUGH */
1214 case EACCES:
1215 {
1216 struct stat statb;
1217 if(stat(path,&statb)>=0)
1218 {
1219 if(S_ISDIR(statb.st_mode))
1220 errno = EISDIR;
1221 #ifdef S_ISSOCK
1222 if(S_ISSOCK(statb.st_mode))
1223 exscript(shp,path,argv,envp);
1224 #endif
1225 }
1226 }
1227 #endif /* !apollo */
1228 #ifdef ENAMETOOLONG
1229 /* FALLTHROUGH */
1230 case ENAMETOOLONG:
1231 #endif /* ENAMETOOLONG */
1232 #if !SHOPT_SUID_EXEC
1233 case EPERM:
1234 #endif
1235 shp->path_err = errno;
1236 return(-1);
1237 case ENOTDIR:
1238 case ENOENT:
1239 case EINTR:
1240 #ifdef EMLINK
1241 case EMLINK:
1242 #endif /* EMLINK */
1243 return(-1);
1244 case E2BIG:
1245 if(shp->xargmin)
1246 {
1247 pid = path_xargs(shp,opath, &argv[0] ,envp,spawn);
1248 if(pid<0)
1249 goto retry;
1250 return(pid);
1251 }
1252 /* FALLTHROUGH */
1253 default:
1254 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1255 }
1256 return 0;
1257 }
1258
1259 /*
1260 * File is executable but not machine code.
1261 * Assume file is a Shell script and execute it.
1262 */
1263
exscript(Shell_t * shp,register char * path,register char * argv[],char ** envp)1264 static void exscript(Shell_t *shp,register char *path,register char *argv[],char **envp)
1265 {
1266 register Sfio_t *sp;
1267 path = path_relative(shp,path);
1268 shp->comdiv=0;
1269 shp->bckpid = 0;
1270 shp->coshell = 0;
1271 shp->st.ioset=0;
1272 /* clean up any cooperating processes */
1273 if(shp->cpipe[0]>0)
1274 sh_pclose(shp->cpipe);
1275 if(shp->cpid && shp->outpipe)
1276 sh_close(*shp->outpipe);
1277 shp->cpid = 0;
1278 if(sp=fcfile())
1279 while(sfstack(sp,SF_POPSTACK));
1280 job_clear();
1281 VALIDATE_FD(shp, shp->infd);
1282 if(shp->infd>0 && (shp->fdstatus[shp->infd]&IOCLEX))
1283 sh_close(shp->infd);
1284 sh_setstate(sh_state(SH_FORKED));
1285 sfsync(sfstderr);
1286 #if SHOPT_SUID_EXEC && !SHOPT_PFSH
1287 /* check if file cannot open for read or script is setuid/setgid */
1288 {
1289 static char name[] = "/tmp/euidXXXXXXXXXX";
1290 register int n;
1291 register uid_t euserid;
1292 char *savet=0;
1293 struct stat statb;
1294 if((n=sh_open(path,O_RDONLY,0)) >= 0)
1295 {
1296 /* move <n> if n=0,1,2 */
1297 n = sh_iomovefd(n);
1298 if(fstat(n,&statb)>=0 && !(statb.st_mode&(S_ISUID|S_ISGID)))
1299 goto openok;
1300 sh_close(n);
1301 }
1302 if((euserid=geteuid()) != shp->gd->userid)
1303 {
1304 strncpy(name+9,fmtbase((long)getpid(),10,0),sizeof(name)-10);
1305 /* create a suid open file with owner equal effective uid */
1306 if((n=open(name,O_CREAT|O_TRUNC|O_WRONLY,S_ISUID|S_IXUSR)) < 0)
1307 goto fail;
1308 unlink(name);
1309 /* make sure that file has right owner */
1310 if(fstat(n,&statb)<0 || statb.st_uid != euserid)
1311 goto fail;
1312 if(n!=10)
1313 {
1314 sh_close(10);
1315 fcntl(n, F_DUPFD, 10);
1316 sh_close(n);
1317 n=10;
1318 }
1319 }
1320 savet = *--argv;
1321 *argv = path;
1322 path_pfexecve(shp,e_suidexec,argv,envp,0);
1323 fail:
1324 /*
1325 * The following code is just for compatibility
1326 */
1327 if((n=open(path,O_RDONLY,0)) < 0)
1328 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1329 if(savet)
1330 *argv++ = savet;
1331 openok:
1332 shp->infd = n;
1333 }
1334 #else
1335 if((shp->infd = sh_open(path,O_RDONLY,0)) < 0)
1336 errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path);
1337 #endif
1338 shp->infd = sh_iomovefd(shp->infd);
1339 #if SHOPT_ACCT
1340 sh_accbegin(path) ; /* reset accounting */
1341 #endif /* SHOPT_ACCT */
1342 shp->arglist = sh_argcreate(argv);
1343 shp->lastarg = strdup(path);
1344 /* save name of calling command */
1345 shp->readscript = error_info.id;
1346 /* close history file if name has changed */
1347 if(shp->gd->hist_ptr && (path=nv_getval(HISTFILE)) && strcmp(path,shp->gd->hist_ptr->histname))
1348 {
1349 hist_close(shp->gd->hist_ptr);
1350 (HISTCUR)->nvalue.lp = 0;
1351 }
1352 sh_offstate(SH_FORKED);
1353 if(shp->sigflag[SIGCHLD]==SH_SIGOFF)
1354 shp->sigflag[SIGCHLD] = SH_SIGFAULT;
1355 siglongjmp(*shp->jmplist,SH_JMPSCRIPT);
1356 }
1357
1358 #if SHOPT_ACCT
1359 # include <sys/acct.h>
1360 # include "FEATURE/time"
1361
1362 static struct acct sabuf;
1363 static struct tms buffer;
1364 static clock_t before;
1365 static char *SHACCT; /* set to value of SHACCT environment variable */
1366 static shaccton; /* non-zero causes accounting record to be written */
1367 static int compress(time_t);
1368 /*
1369 * initialize accounting, i.e., see if SHACCT variable set
1370 */
sh_accinit(void)1371 void sh_accinit(void)
1372 {
1373 SHACCT = getenv("SHACCT");
1374 }
1375 /*
1376 * suspend accounting until turned on by sh_accbegin()
1377 */
sh_accsusp(void)1378 void sh_accsusp(void)
1379 {
1380 shaccton=0;
1381 #ifdef AEXPAND
1382 sabuf.ac_flag |= AEXPND;
1383 #endif /* AEXPAND */
1384 }
1385
1386 /*
1387 * begin an accounting record by recording start time
1388 */
sh_accbegin(const char * cmdname)1389 void sh_accbegin(const char *cmdname)
1390 {
1391 if(SHACCT)
1392 {
1393 sabuf.ac_btime = time(NIL(time_t *));
1394 before = times(&buffer);
1395 sabuf.ac_uid = getuid();
1396 sabuf.ac_gid = getgid();
1397 strncpy(sabuf.ac_comm, (char*)path_basename(cmdname),
1398 sizeof(sabuf.ac_comm));
1399 shaccton = 1;
1400 }
1401 }
1402 /*
1403 * terminate an accounting record and append to accounting file
1404 */
sh_accend(void)1405 void sh_accend(void)
1406 {
1407 int fd;
1408 clock_t after;
1409
1410 if(shaccton)
1411 {
1412 after = times(&buffer);
1413 sabuf.ac_utime = compress(buffer.tms_utime + buffer.tms_cutime);
1414 sabuf.ac_stime = compress(buffer.tms_stime + buffer.tms_cstime);
1415 sabuf.ac_etime = compress( (time_t)(after-before));
1416 fd = open( SHACCT , O_WRONLY | O_APPEND | O_CREAT,RW_ALL);
1417 write(fd, (const char*)&sabuf, sizeof( sabuf ));
1418 close( fd);
1419 }
1420 }
1421
1422 /*
1423 * Produce a pseudo-floating point representation
1424 * with 3 bits base-8 exponent, 13 bits fraction.
1425 */
compress(register time_t t)1426 static int compress(register time_t t)
1427 {
1428 register int exp = 0, rund = 0;
1429
1430 while (t >= 8192)
1431 {
1432 exp++;
1433 rund = t&04;
1434 t >>= 3;
1435 }
1436 if (rund)
1437 {
1438 t++;
1439 if (t >= 8192)
1440 {
1441 t >>= 3;
1442 exp++;
1443 }
1444 }
1445 return((exp<<13) + t);
1446 }
1447 #endif /* SHOPT_ACCT */
1448
1449
1450
1451 /*
1452 * add a pathcomponent to the path search list and eliminate duplicates
1453 * and non-existing absolute paths.
1454 */
path_addcomp(Shell_t * shp,Pathcomp_t * first,Pathcomp_t * old,const char * name,int flag)1455 static Pathcomp_t *path_addcomp(Shell_t *shp,Pathcomp_t *first, Pathcomp_t *old,const char *name, int flag)
1456 {
1457 register Pathcomp_t *pp, *oldpp;
1458 int len, offset=staktell();
1459 if(!(flag&PATH_BFPATH))
1460 {
1461 register const char *cp = name;
1462 while(*cp && *cp!=':')
1463 stakputc(*cp++);
1464 len = staktell()-offset;
1465 stakputc(0);
1466 stakseek(offset);
1467 name = (const char*)stakptr(offset);
1468 }
1469 else
1470 len = strlen(name);
1471 for(pp=first; pp; pp=pp->next)
1472 {
1473 if(len == pp->len && memcmp(name,pp->name,len)==0)
1474 {
1475 pp->flags |= flag;
1476 return(first);
1477 }
1478 }
1479 for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next);
1480 pp = newof((Pathcomp_t*)0,Pathcomp_t,1,len+1);
1481 pp->shp = shp;
1482 pp->refcount = 1;
1483 memcpy((char*)(pp+1),name,len+1);
1484 pp->name = (char*)(pp+1);
1485 pp->len = len;
1486 if(oldpp)
1487 oldpp->next = pp;
1488 else
1489 first = pp;
1490 pp->flags = flag;
1491 if(strcmp(name,SH_CMDLIB_DIR)==0)
1492 {
1493 pp->dev = 1;
1494 pp->flags |= PATH_BUILTIN_LIB;
1495 pp->blib = pp->bbuf = malloc(sizeof(LIBCMD));
1496 strcpy(pp->blib,LIBCMD);
1497 return(first);
1498 }
1499 if((old||shp->pathinit) && ((flag&(PATH_PATH|PATH_SKIP))==PATH_PATH))
1500 path_chkpaths(shp,first,old,pp,offset);
1501 return(first);
1502 }
1503
1504 /*
1505 * This function checks for the .paths file in directory in <pp>
1506 * it assumes that the directory is on the stack at <offset>
1507 */
path_chkpaths(Shell_t * shp,Pathcomp_t * first,Pathcomp_t * old,Pathcomp_t * pp,int offset)1508 static int path_chkpaths(Shell_t *shp,Pathcomp_t *first, Pathcomp_t* old,Pathcomp_t *pp, int offset)
1509 {
1510 struct stat statb;
1511 int k,m,n,fd;
1512 char *sp,*cp,*ep;
1513 stakseek(offset+pp->len);
1514 if(pp->len==1 && *stakptr(offset)=='/')
1515 stakseek(offset);
1516 stakputs("/.paths");
1517 if((fd=open(stakptr(offset),O_RDONLY))>=0)
1518 {
1519 fstat(fd,&statb);
1520 n = statb.st_size;
1521 stakseek(offset+pp->len+n+2);
1522 sp = stakptr(offset+pp->len);
1523 *sp++ = '/';
1524 n=read(fd,cp=sp,n);
1525 sp[n] = 0;
1526 close(fd);
1527 for(ep=0; n--; cp++)
1528 {
1529 if(*cp=='=')
1530 {
1531 ep = cp+1;
1532 continue;
1533 }
1534 else if(*cp!='\r' && *cp!='\n')
1535 continue;
1536 if(*sp=='#' || sp==cp)
1537 {
1538 sp = cp+1;
1539 continue;
1540 }
1541 *cp = 0;
1542 m = ep ? (ep-sp) : 0;
1543 if(m==0 || m==6 && memcmp((void*)sp,(void*)"FPATH=",m)==0)
1544 {
1545 if(first)
1546 {
1547 char *ptr = stakptr(offset+pp->len+1);
1548 if(ep)
1549 strcpy(ptr,ep);
1550 path_addcomp(shp,first,old,stakptr(offset),PATH_FPATH|PATH_BFPATH);
1551 }
1552 }
1553 else if(m==11 && memcmp((void*)sp,(void*)"PLUGIN_LIB=",m)==0)
1554 {
1555 if(pp->bbuf)
1556 free(pp->bbuf);
1557 pp->blib = pp->bbuf = strdup(ep);
1558 }
1559 else if(m)
1560 {
1561 pp->lib = (char*)malloc(cp-sp+pp->len+2);
1562 memcpy((void*)pp->lib,(void*)sp,m);
1563 memcpy((void*)&pp->lib[m],stakptr(offset),pp->len);
1564 pp->lib[k=m+pp->len] = '/';
1565 strcpy((void*)&pp->lib[k+1],ep);
1566 pathcanon(&pp->lib[m],0);
1567 if(!first)
1568 {
1569 stakseek(0);
1570 stakputs(pp->lib);
1571 free((void*)pp->lib);
1572 return(1);
1573 }
1574 }
1575 sp = cp+1;
1576 ep = 0;
1577 }
1578 }
1579 return(0);
1580 }
1581
1582
path_addpath(Shell_t * shp,Pathcomp_t * first,register const char * path,int type)1583 Pathcomp_t *path_addpath(Shell_t *shp,Pathcomp_t *first, register const char *path,int type)
1584 {
1585 register const char *cp;
1586 Pathcomp_t *old=0;
1587 int offset = staktell();
1588 char *savptr;
1589
1590 if(!path && type!=PATH_PATH)
1591 return(first);
1592 if(type!=PATH_FPATH)
1593 {
1594 old = first;
1595 first = 0;
1596 }
1597 if(offset)
1598 savptr = stakfreeze(0);
1599 if(path) while(*(cp=path))
1600 {
1601 if(*cp==':')
1602 {
1603 if(type!=PATH_FPATH)
1604 first = path_addcomp(shp,first,old,".",type);
1605 while(*++path == ':');
1606 }
1607 else
1608 {
1609 int c;
1610 while(*path && *path!=':')
1611 path++;
1612 c = *path++;
1613 first = path_addcomp(shp,first,old,cp,type);
1614 if(c==0)
1615 break;
1616 if(*path==0)
1617 path--;
1618 }
1619 }
1620 if(old)
1621 {
1622 if(!first && !path)
1623 {
1624 Pathcomp_t *pp = (Pathcomp_t*)shp->defpathlist;
1625 if(!pp)
1626 pp = defpath_init(shp);
1627 first = path_dup(pp);
1628 }
1629 if(cp=(sh_scoped(shp,FPATHNOD))->nvalue.cp)
1630 first = (void*)path_addpath(shp,(Pathcomp_t*)first,cp,PATH_FPATH);
1631 path_delete(old);
1632 }
1633 if(offset)
1634 stakset(savptr,offset);
1635 else
1636 stakseek(0);
1637 return(first);
1638 }
1639
1640 /*
1641 * duplicate the path give by <first> by incremented reference counts
1642 */
path_dup(Pathcomp_t * first)1643 Pathcomp_t *path_dup(Pathcomp_t *first)
1644 {
1645 register Pathcomp_t *pp=first;
1646 while(pp)
1647 {
1648 pp->refcount++;
1649 pp = pp->next;
1650 }
1651 return(first);
1652 }
1653
1654 /*
1655 * called whenever the directory is changed
1656 */
path_newdir(Shell_t * shp,Pathcomp_t * first)1657 void path_newdir(Shell_t *shp,Pathcomp_t *first)
1658 {
1659 register Pathcomp_t *pp=first, *next, *pq;
1660 struct stat statb;
1661 for(pp=first; pp; pp=pp->next)
1662 {
1663 pp->flags &= ~PATH_SKIP;
1664 if(*pp->name=='/')
1665 continue;
1666 /* delete .paths component */
1667 if((next=pp->next) && (next->flags&PATH_BFPATH))
1668 {
1669 pp->next = next->next;
1670 if(--next->refcount<=0)
1671 free((void*)next);
1672 }
1673 if(stat(pp->name,&statb)<0 || !S_ISDIR(statb.st_mode))
1674 {
1675 pp->dev = 0;
1676 pp->ino = 0;
1677 continue;
1678 }
1679 pp->dev = statb.st_dev;
1680 pp->ino = statb.st_ino;
1681 pp->mtime = statb.st_mtime;
1682 for(pq=first;pq!=pp;pq=pq->next)
1683 {
1684 if(pp->ino==pq->ino && pp->dev==pq->dev)
1685 pp->flags |= PATH_SKIP;
1686 }
1687 for(pq=pp;pq=pq->next;)
1688 {
1689 if(pp->ino==pq->ino && pp->dev==pq->dev)
1690 pq->flags |= PATH_SKIP;
1691 }
1692 if((pp->flags&(PATH_PATH|PATH_SKIP))==PATH_PATH)
1693 {
1694 /* try to insert .paths component */
1695 int offset = staktell();
1696 stakputs(pp->name);
1697 stakseek(offset);
1698 next = pp->next;
1699 pp->next = 0;
1700 path_chkpaths(shp,first,(Pathcomp_t*)0,pp,offset);
1701 if(pp->next)
1702 pp = pp->next;
1703 pp->next = next;
1704 }
1705 }
1706 #if 0
1707 path_dump(first);
1708 #endif
1709 }
1710
path_unsetfpath(Shell_t * shp)1711 Pathcomp_t *path_unsetfpath(Shell_t *shp)
1712 {
1713 Pathcomp_t *first = (Pathcomp_t*)shp->pathlist;
1714 register Pathcomp_t *pp=first, *old=0;
1715 if(shp->fpathdict)
1716 {
1717 struct Ufunction *rp, *rpnext;
1718 for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext)
1719 {
1720 rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp);
1721 if(rp->fdict)
1722 nv_delete(rp->np,rp->fdict,NV_NOFREE);
1723 rp->fdict = 0;
1724 }
1725 }
1726 while(pp)
1727 {
1728 if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH))
1729 {
1730 if(pp->flags&PATH_PATH)
1731 pp->flags &= ~PATH_FPATH;
1732 else
1733 {
1734 Pathcomp_t *ppsave=pp;
1735 if(old)
1736 old->next = pp->next;
1737 else
1738 first = pp->next;
1739 pp = pp->next;
1740 if(--ppsave->refcount<=0)
1741 {
1742 if(ppsave->lib)
1743 free((void*)ppsave->lib);
1744 free((void*)ppsave);
1745 }
1746 continue;
1747 }
1748
1749 }
1750 old = pp;
1751 pp = pp->next;
1752 }
1753 return(first);
1754 }
1755
path_dirfind(Pathcomp_t * first,const char * name,int c)1756 Pathcomp_t *path_dirfind(Pathcomp_t *first,const char *name,int c)
1757 {
1758 register Pathcomp_t *pp=first;
1759 while(pp)
1760 {
1761 if(memcmp(name,pp->name,pp->len)==0 && name[pp->len]==c)
1762 return(pp);
1763 pp = pp->next;
1764 }
1765 return(0);
1766 }
1767
1768 /*
1769 * get discipline for tracked alias
1770 */
talias_get(Namval_t * np,Namfun_t * nvp)1771 static char *talias_get(Namval_t *np, Namfun_t *nvp)
1772 {
1773 Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1774 char *ptr;
1775 if(!pp)
1776 return(NULL);
1777 pp->shp->last_table = 0;
1778 path_nextcomp(pp->shp,pp,nv_name(np),pp);
1779 ptr = stakfreeze(0);
1780 return(ptr+PATH_OFFSET);
1781 }
1782
talias_put(register Namval_t * np,const char * val,int flags,Namfun_t * fp)1783 static void talias_put(register Namval_t* np,const char *val,int flags,Namfun_t *fp)
1784 {
1785 if(!val && np->nvalue.cp)
1786 {
1787 Pathcomp_t *pp = (Pathcomp_t*)np->nvalue.cp;
1788 if(--pp->refcount<=0)
1789 free((void*)pp);
1790 }
1791 nv_putv(np,val,flags,fp);
1792 }
1793
1794 static const Namdisc_t talias_disc = { 0, talias_put, talias_get };
1795 static Namfun_t talias_init = { &talias_disc, 1 };
1796
1797 /*
1798 * set tracked alias node <np> to value <pp>
1799 */
path_alias(register Namval_t * np,register Pathcomp_t * pp)1800 void path_alias(register Namval_t *np,register Pathcomp_t *pp)
1801 {
1802 if(pp)
1803 {
1804 struct stat statb;
1805 char *sp;
1806 nv_offattr(np,NV_NOPRINT);
1807 nv_stack(np,&talias_init);
1808 np->nvalue.cp = (char*)pp;
1809 pp->refcount++;
1810 nv_setattr(np,NV_TAGGED|NV_NOFREE);
1811 path_nextcomp(pp->shp,pp,nv_name(np),pp);
1812 sp = stakptr(PATH_OFFSET);
1813 if(sp && lstat(sp,&statb)>=0 && S_ISLNK(statb.st_mode))
1814 nv_setsize(np,statb.st_size+1);
1815 else
1816 nv_setsize(np,0);
1817 }
1818 else
1819 _nv_unset(np,0);
1820 }
1821
1822