1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23
24 /*
25 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28
29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
31
32 #if defined(sun)
33 #pragma ident "@(#)hashserv.c 1.14 06/06/16 SMI"
34 #endif
35
36 #include "defs.h"
37
38 /*
39 * Copyright 2008-2019 J. Schilling
40 *
41 * @(#)hashserv.c 1.38 19/01/09 2008-2019 J. Schilling
42 */
43 #ifndef lint
44 static UConst char sccsid[] =
45 "@(#)hashserv.c 1.38 19/01/09 2008-2019 J. Schilling";
46 #endif
47
48 /*
49 * UNIX shell
50 */
51
52 #include "hash.h"
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <errno.h>
56 #include <schily/fcntl.h> /* for faccessat() */
57
58 static unsigned char cost;
59 static int dotpath; /* PATH index for "::" */
60 static int multrel; /* Multiple "::" entries exist */
61 static struct entry relcmd; /* List of "relative" cmds */
62
63 short pathlook __PR((unsigned char *com,
64 int flg, struct argnod *arg));
65 static void zapentry __PR((ENTRY *h));
66 void zaphash __PR((void));
67 void zapcd __PR((void));
68 static void hashout __PR((ENTRY *h));
69 void hashpr __PR((void));
70 void set_dotpath __PR((void));
71 void hash_func __PR((unsigned char *name));
72 void func_unhash __PR((unsigned char *name));
73 short hash_cmd __PR((unsigned char *name));
74 int what_is_path __PR((unsigned char *name, int verbose));
75 static int findpath __PR((unsigned char *name, int oldpath));
76 int chk_access __PR((unsigned char *name,
77 mode_t mode, int regflag));
78 static void pr_path __PR((unsigned char *name, int count));
79 static int argpath __PR((struct argnod *arg));
80
81 /*
82 * The central PATH and builtin / function search routine.
83 */
84 short
pathlook(com,flg,arg)85 pathlook(com, flg, arg)
86 unsigned char *com; /* The command name to look for */
87 int flg; /* Whether to manage cache hits */
88 struct argnod *arg; /* Additional environment for cmd */
89 {
90 unsigned char *name = com;
91 ENTRY *h;
92
93 ENTRY hentry;
94 const struct sysnod *sn;
95 #ifdef HAVE_LOADABLE_LIBS
96 const struct sysnod2 *sn2;
97 #endif
98 int count = 0;
99 int i;
100 int pathset = 0;
101 int oldpath = 0;
102
103 hentry.data = 0;
104
105 if (any('/', name))
106 return (COMMAND);
107
108 h = hfind(name);
109 if (h && (flags & ppath)) {
110 if (h->data & (BUILTIN | FUNCTION))
111 if (!(h->data & FUNCTION) || !(flags & nofuncs))
112 return (h->data);
113 /*
114 * Do not hash regular commands with "command -p ..."
115 */
116 h = NULL;
117 }
118
119 if (h) {
120 if (h->data & (BUILTIN | FUNCTION)) {
121 if (flg)
122 h->hits++;
123 if (!(h->data & FUNCTION) || !(flags & nofuncs))
124 return (h->data);
125 }
126
127 if (arg && (pathset = argpath(arg)))
128 return (PATH_COMMAND);
129
130 if ((h->data & DOT_COMMAND) == DOT_COMMAND) {
131 if (multrel == 0 && hashdata(h->data) > dotpath)
132 oldpath = hashdata(h->data);
133 else
134 oldpath = dotpath;
135
136 h->data = 0;
137 goto pathsrch;
138 }
139
140 if (h->data & (COMMAND | REL_COMMAND)) {
141 if (flg)
142 h->hits++;
143 return (h->data);
144 }
145
146 /*
147 * If we are called from "command", do not modify the hashes
148 * as we may retrieve different results from what we usually
149 * expexct.
150 */
151 if (!(flags & nofuncs)) {
152 h->data = 0;
153 h->cost = 0;
154 }
155 }
156
157 #ifdef HAVE_LOADABLE_LIBS
158 if ((sn2 = sh_findbuiltin(name)) != NULL) {
159 i = SYSLOADABLE;
160 hentry.data = (BUILTIN | i);
161 if ((flags & (ppath|nofuncs)))
162 return (hentry.data);
163 count = 1;
164 } else
165 #endif
166 if ((sn = sysnlook(name, commands, no_commands)) != NULL) {
167 i = sn->sysval;
168 if (sn->sysflg & BLT_SPC)
169 i |= SPC_BUILTIN;
170 hentry.data = (BUILTIN | i);
171 /*
172 * If we are called from "command", do not modify the hashes
173 * as we may retrieve different results from what we usually
174 * expexct.
175 */
176 if ((flags & (ppath|nofuncs)))
177 return (hentry.data);
178 count = 1;
179 } else {
180 if (arg && (pathset = argpath(arg)))
181 return (PATH_COMMAND);
182 pathsrch:
183 count = findpath(name, oldpath);
184 /*
185 * If we are called from "command", do not modify the hashes
186 * as we may retrieve different results from what we usually
187 * expexct.
188 */
189 if ((flags & (ppath|nofuncs))) {
190 if (count > 0)
191 return (COMMAND | count);
192 else
193 return (-count);
194 }
195 }
196
197 if (count > 0) {
198 if (h == 0) {
199 hentry.cost = 0;
200 hentry.key = make(name);
201 h = henter(hentry);
202 }
203
204 if (h->data == 0) {
205 if (count < dotpath) {
206 h->data = COMMAND | count;
207 } else {
208 h->data = REL_COMMAND | count;
209 h->next = relcmd.next;
210 relcmd.next = h;
211 }
212 }
213
214 h->hits = flg;
215 h->cost += cost;
216 return (h->data);
217 } else {
218 return (-count);
219 }
220 }
221
222 /*
223 * Remove any PATH related attributes from entry.
224 */
225 static void
zapentry(h)226 zapentry(h)
227 ENTRY *h;
228 {
229 h->data &= HASHZAP;
230 }
231
232 /*
233 * Remove any PATH related attributes from whole hash database.
234 */
235 void
zaphash()236 zaphash()
237 {
238 hscan(zapentry);
239 relcmd.next = 0;
240 }
241
242 /*
243 * Mark all relative commands as outdated after a chdir()
244 */
245 void
zapcd()246 zapcd()
247 {
248 ENTRY *ptr = relcmd.next;
249
250 while (ptr) {
251 ptr->data |= CDMARK;
252 ptr = ptr->next;
253 }
254 relcmd.next = 0;
255 }
256
257 /*
258 * Print hash attributes for entry.
259 */
260 static void
hashout(h)261 hashout(h)
262 ENTRY *h;
263 {
264 sigchk();
265
266 if (hashtype(h->data) == NOTFOUND)
267 return;
268
269 if (h->data & (BUILTIN | FUNCTION))
270 return;
271
272 prn_buff(h->hits);
273
274 if (h->data & REL_COMMAND)
275 prc_buff('*');
276
277 prc_buff(TAB);
278 prn_buff(h->cost);
279 prc_buff(TAB);
280
281 pr_path(h->key, hashdata(h->data));
282 prc_buff(NL);
283 }
284
285 /*
286 * Print hash statistics.
287 */
288 void
hashpr()289 hashpr()
290 {
291 prs_buff(_gettext("hits cost command\n"));
292 hscan(hashout);
293 }
294
295 /*
296 * Find index of "::" in PATH and remember the result in 'dotpath'.
297 */
298 void
set_dotpath()299 set_dotpath()
300 {
301 unsigned char *path;
302 int cnt = 1;
303
304 dotpath = 10000;
305 path = getpath((unsigned char *)"");
306
307 while (path && *path) {
308 if (*path == '/') {
309 cnt++;
310 } else {
311 if (dotpath == 10000) {
312 dotpath = cnt;
313 } else {
314 multrel = 1;
315 return;
316 }
317 }
318 path = nextpath(path);
319 }
320 multrel = 0;
321 }
322
323 /*
324 * Add new function to hash.
325 */
326 void
hash_func(name)327 hash_func(name)
328 unsigned char *name;
329 {
330 ENTRY *h;
331 ENTRY hentry;
332
333 h = hfind(name);
334
335 if (h) {
336 h->data = FUNCTION;
337 } else {
338 hentry.data = FUNCTION;
339 hentry.key = make(name);
340 hentry.cost = 0;
341 hentry.hits = 0;
342 hentry.next = NULL;
343 henter(hentry);
344 }
345 }
346
347 /*
348 * Remove function from hash.
349 * Reestablish builtin if function did hide the builtin.
350 */
351 void
func_unhash(name)352 func_unhash(name)
353 unsigned char *name;
354 {
355 const struct sysnod *sn;
356 #ifdef HAVE_LOADABLE_LIBS
357 const struct sysnod2 *sn2;
358 #endif
359 ENTRY *h;
360
361 h = hfind(name);
362
363 if (h && (h->data & FUNCTION)) {
364
365 #ifdef HAVE_LOADABLE_LIBS
366 if ((sn2 = sh_findbuiltin(name)) != NULL) {
367 int i;
368
369 i = SYSLOADABLE;
370 h->data = (BUILTIN | i);
371 } else
372 #endif
373 if ((sn = sysnlook(name, commands, no_commands)) != NULL) {
374 int i;
375
376 i = sn->sysval;
377 if (sn->sysflg & BLT_SPC)
378 i |= SPC_BUILTIN;
379 h->data = (BUILTIN | i);
380 } else {
381 h->data = NOTFOUND;
382 }
383 }
384 }
385
386 /*
387 * Hash search / entry code for "hash" builtin.
388 */
389 short
hash_cmd(name)390 hash_cmd(name)
391 unsigned char *name;
392 {
393 ENTRY *h;
394
395 if (any('/', name))
396 return (COMMAND);
397
398 h = hfind(name);
399
400 if (h) {
401 if (h->data & (BUILTIN | FUNCTION)) {
402 return (h->data);
403 } else if ((h->data & REL_COMMAND) == REL_COMMAND) {
404 /*
405 * unlink h from relative command list
406 */
407 ENTRY *ptr = &relcmd;
408 while (ptr-> next != h)
409 ptr = ptr->next;
410 ptr->next = h->next;
411 }
412 zapentry(h);
413 }
414
415 return (pathlook(name, 0, (struct argnod *)0));
416 }
417
418
419 /*
420 * Search command an print command type.
421 * Return 0 if found, 1 if not.
422 */
423 int
what_is_path(name,verbose)424 what_is_path(name, verbose)
425 unsigned char *name;
426 int verbose;
427 {
428 ENTRY *h;
429 int cnt;
430 int amt;
431 short hashval;
432 const struct sysnod *sp;
433 #ifdef HAVE_LOADABLE_LIBS
434 const struct sysnod2 *sp2;
435 struct sysnod sn;
436 #endif
437
438 h = hfind(name);
439 if (h && (flags & ppath)) {
440 /*
441 * Do not hash regular commands with "command -p ..."
442 */
443 if (!(h->data & (BUILTIN | FUNCTION)))
444 h = NULL;
445 }
446
447 amt = prs_buff(name);
448 if (h) {
449 hashval = hashdata(h->data);
450
451 switch (hashtype(h->data)) {
452
453 case BUILTIN:
454 if (!verbose) {
455 prc_buff(NL);
456 return (0);
457 }
458 #ifdef DO_POSIX_TYPE
459 /*
460 * In POSIX mode, we distinct three different types of
461 * builtins and thus need to check explicitly.
462 */
463 goto checkbuiltin;
464 #else
465 prs_buff(_gettext(" is a shell builtin\n"));
466 return (0);
467 #endif
468
469 case FUNCTION: {
470 struct namnod *n = lookup(name);
471 struct fndnod *f = fndptr(n->funcval);
472
473 if (!verbose) {
474 prc_buff(NL);
475 return (0);
476 }
477 prs_buff(_gettext(" is a function\n"));
478 if (verbose <= 1)
479 return (0);
480 prs_buff(name);
481 prs_buff((unsigned char *)"(){\n");
482 if (f != NULL)
483 prf((struct trenod *)f->fndval);
484 prs_buff((unsigned char *)"\n}\n");
485 return (0);
486 }
487
488 case REL_COMMAND: {
489 short hash;
490
491 if ((h->data & DOT_COMMAND) == DOT_COMMAND) {
492 hash = pathlook(name, 0, (struct argnod *)0);
493 if (hashtype(hash) == NOTFOUND) {
494 if (!verbose) {
495 unprs_buff(amt);
496 return (ERR_NOTFOUND);
497 }
498 prs_buff(_gettext(" not found\n"));
499 return (ERR_NOTFOUND);
500 } else {
501 hashval = hashdata(hash);
502 }
503 }
504 }
505 /* FALLTHROUGH */
506
507 case COMMAND:
508 if (!verbose) {
509 unprs_buff(amt);
510 pr_path(name, hashval);
511 prc_buff(NL);
512 return (0);
513 }
514 prs_buff(_gettext(" is hashed ("));
515 pr_path(name, hashval);
516 prs_buff((unsigned char *)")\n");
517 return (0);
518 }
519 }
520
521 #ifdef DO_POSIX_TYPE
522 if (syslook(name, reserved, no_reserved)) {
523 if (!verbose) {
524 prc_buff(NL);
525 return (0);
526 }
527 prs_buff(_gettext(" is a keyword\n"));
528 return (0);
529 }
530
531 checkbuiltin:
532 #endif
533
534 #if defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN)
535 if ((sp2 = sh_findbuiltin(name)) != NULL) {
536 sn.sysflg = 0;
537 sp = &sn;
538 goto isbltin;
539 } else
540 #endif
541 if ((sp = sysnlook(name, commands, no_commands)) != NULL) {
542 #if defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN)
543 isbltin:
544 #endif
545 if (!verbose) {
546 prc_buff(NL);
547 return (0);
548 }
549 #ifdef DO_POSIX_TYPE
550 if (sp->sysflg & BLT_SPC)
551 prs_buff(_gettext(" is a special shell builtin\n"));
552 else if (sp->sysflg & BLT_INT)
553 prs_buff(_gettext(" is a shell intrinsic\n"));
554 else
555 #endif
556 prs_buff(_gettext(" is a shell builtin\n"));
557 return (0);
558 }
559
560 if ((cnt = findpath(name, 0)) > 0) {
561 if (!verbose) {
562 unprs_buff(amt);
563 pr_path(name, cnt);
564 prc_buff(NL);
565 return (0);
566 }
567 prs_buff(_gettext(" is "));
568 pr_path(name, cnt);
569 prc_buff(NL);
570 return (0);
571 } else {
572 if (!verbose) {
573 unprs_buff(amt);
574 return (ERR_NOTFOUND);
575 }
576 prs_buff(_gettext(" not found\n"));
577 return (ERR_NOTFOUND);
578 }
579 }
580
581 /*
582 * Find 'name' in PATH and return index.
583 * Start search at 'oldpath'.
584 */
585 static int
findpath(name,oldpath)586 findpath(name, oldpath)
587 unsigned char *name;
588 int oldpath;
589 {
590 unsigned char *path;
591 int count = 1;
592
593 unsigned char *p;
594 int ok = 1;
595 int e_code = 1;
596
597 cost = 0;
598 path = getpath(name);
599
600 if (oldpath) {
601 count = dotpath;
602 while (--count && path)
603 path = nextpath(path);
604
605 if (oldpath > dotpath && path) {
606 catpath(path, name);
607 p = curstak();
608 cost = 1;
609
610 if ((ok = chk_access(p, S_IEXEC, 1)) == 0)
611 return (dotpath);
612 else
613 return (oldpath);
614 } else {
615 count = dotpath;
616 }
617 }
618
619 while (path) {
620 path = catpath(path, name);
621 cost++;
622 p = curstak();
623
624 if ((ok = chk_access(p, S_IEXEC, 1)) == 0)
625 break;
626 else
627 e_code = max(e_code, ok);
628
629 count++;
630 }
631
632 return (ok ? -e_code : count);
633 }
634
635 /*
636 * Determine if file given by name is accessible with permissions
637 * given by mode.
638 * Regflag argument non-zero means not to consider
639 * a non-regular file as executable.
640 *
641 * Return codes:
642 * 0 Access OK
643 * 1 Other error (e.g. file not found)
644 * 2 Executable but not regular
645 * 3 File found but access error
646 */
647 #ifdef PROTOTYPES
648 int
chk_access(unsigned char * name,mode_t mode,int regflag)649 chk_access(unsigned char *name, mode_t mode, int regflag)
650 #else
651 int
652 chk_access(name, mode, regflag)
653 unsigned char *name; /* The filename */
654 mode_t mode; /* F_OK, S_IREAD, S_IWRITE, S_IEXEC */
655 int regflag; /* if TRUE: file must be regular */
656 #endif
657 {
658 static int flag;
659 static uid_t euid;
660 struct stat statb;
661 mode_t ftype;
662
663 if (flag == 0) {
664 euid = geteuid();
665 flag = 1;
666 }
667 if (stat((char *)name, &statb) == 0) {
668 int amode = 0;
669
670 ftype = statb.st_mode & S_IFMT;
671 if (mode == S_IEXEC && regflag && ftype != S_IFREG)
672 return (2);
673
674 if (mode == F_OK)
675 amode = F_OK;
676 if (mode & S_IREAD)
677 amode |= R_OK;
678 if (mode & S_IWRITE)
679 amode |= W_OK;
680 if (mode & S_IEXEC)
681 amode |= X_OK;
682
683 #ifdef HAVE_FACCESSAT
684 if (faccessat(AT_FDCWD, (char *)name, amode, AT_EACCESS) == 0) {
685 #else
686 #ifdef HAVE_ACCESS_E_OK
687 if (access((char *)name, 010|amode) == 0) {
688 #else
689 if (access((char *)name, amode) == 0) {
690 #endif
691 #endif
692 if (euid == 0) {
693 if (ftype != S_IFREG || mode != S_IEXEC)
694 return (0);
695 /*
696 * root can execute file as long as it has
697 * execute permission for someone
698 */
699 if (statb.st_mode &
700 (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) {
701 return (0);
702 }
703 return (3);
704 }
705 return (0);
706 }
707 }
708 return (errno == EACCES ? 3 : 1);
709 }
710
711 /*
712 * Print path for command based on command name and index in PATH
713 */
714 static void
pr_path(name,count)715 pr_path(name, count)
716 unsigned char *name;
717 int count;
718 {
719 unsigned char *path;
720
721 path = getpath(name);
722
723 while (--count && path)
724 path = nextpath(path);
725
726 if (path == NULL) /* Paranoia for Coverity */
727 return;
728
729 catpath(path, name);
730 prs_buff(curstak());
731 }
732
733 /*
734 * Return 1 if argnode contains a PATH= definition.
735 */
736 static int
argpath(arg)737 argpath(arg)
738 struct argnod *arg;
739 {
740 unsigned char *s;
741 unsigned char *start;
742
743 while (arg) {
744 s = arg->argval;
745 start = s;
746
747 if (letter(*s)) {
748 while (alphanum(*s))
749 s++;
750
751 if (*s == '=') {
752 *s = 0;
753
754 if (eq(start, pathname)) {
755 *s = '=';
756 return (1);
757 } else {
758 *s = '=';
759 }
760 }
761 }
762 arg = arg->argnxt;
763 }
764
765 return (0);
766 }
767