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