/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #if defined(sun) #pragma ident "@(#)hashserv.c 1.14 06/06/16 SMI" #endif #include "defs.h" /* * Copyright 2008-2019 J. Schilling * * @(#)hashserv.c 1.38 19/01/09 2008-2019 J. Schilling */ #ifndef lint static UConst char sccsid[] = "@(#)hashserv.c 1.38 19/01/09 2008-2019 J. Schilling"; #endif /* * UNIX shell */ #include "hash.h" #include #include #include #include /* for faccessat() */ static unsigned char cost; static int dotpath; /* PATH index for "::" */ static int multrel; /* Multiple "::" entries exist */ static struct entry relcmd; /* List of "relative" cmds */ short pathlook __PR((unsigned char *com, int flg, struct argnod *arg)); static void zapentry __PR((ENTRY *h)); void zaphash __PR((void)); void zapcd __PR((void)); static void hashout __PR((ENTRY *h)); void hashpr __PR((void)); void set_dotpath __PR((void)); void hash_func __PR((unsigned char *name)); void func_unhash __PR((unsigned char *name)); short hash_cmd __PR((unsigned char *name)); int what_is_path __PR((unsigned char *name, int verbose)); static int findpath __PR((unsigned char *name, int oldpath)); int chk_access __PR((unsigned char *name, mode_t mode, int regflag)); static void pr_path __PR((unsigned char *name, int count)); static int argpath __PR((struct argnod *arg)); /* * The central PATH and builtin / function search routine. */ short pathlook(com, flg, arg) unsigned char *com; /* The command name to look for */ int flg; /* Whether to manage cache hits */ struct argnod *arg; /* Additional environment for cmd */ { unsigned char *name = com; ENTRY *h; ENTRY hentry; const struct sysnod *sn; #ifdef HAVE_LOADABLE_LIBS const struct sysnod2 *sn2; #endif int count = 0; int i; int pathset = 0; int oldpath = 0; hentry.data = 0; if (any('/', name)) return (COMMAND); h = hfind(name); if (h && (flags & ppath)) { if (h->data & (BUILTIN | FUNCTION)) if (!(h->data & FUNCTION) || !(flags & nofuncs)) return (h->data); /* * Do not hash regular commands with "command -p ..." */ h = NULL; } if (h) { if (h->data & (BUILTIN | FUNCTION)) { if (flg) h->hits++; if (!(h->data & FUNCTION) || !(flags & nofuncs)) return (h->data); } if (arg && (pathset = argpath(arg))) return (PATH_COMMAND); if ((h->data & DOT_COMMAND) == DOT_COMMAND) { if (multrel == 0 && hashdata(h->data) > dotpath) oldpath = hashdata(h->data); else oldpath = dotpath; h->data = 0; goto pathsrch; } if (h->data & (COMMAND | REL_COMMAND)) { if (flg) h->hits++; return (h->data); } /* * If we are called from "command", do not modify the hashes * as we may retrieve different results from what we usually * expexct. */ if (!(flags & nofuncs)) { h->data = 0; h->cost = 0; } } #ifdef HAVE_LOADABLE_LIBS if ((sn2 = sh_findbuiltin(name)) != NULL) { i = SYSLOADABLE; hentry.data = (BUILTIN | i); if ((flags & (ppath|nofuncs))) return (hentry.data); count = 1; } else #endif if ((sn = sysnlook(name, commands, no_commands)) != NULL) { i = sn->sysval; if (sn->sysflg & BLT_SPC) i |= SPC_BUILTIN; hentry.data = (BUILTIN | i); /* * If we are called from "command", do not modify the hashes * as we may retrieve different results from what we usually * expexct. */ if ((flags & (ppath|nofuncs))) return (hentry.data); count = 1; } else { if (arg && (pathset = argpath(arg))) return (PATH_COMMAND); pathsrch: count = findpath(name, oldpath); /* * If we are called from "command", do not modify the hashes * as we may retrieve different results from what we usually * expexct. */ if ((flags & (ppath|nofuncs))) { if (count > 0) return (COMMAND | count); else return (-count); } } if (count > 0) { if (h == 0) { hentry.cost = 0; hentry.key = make(name); h = henter(hentry); } if (h->data == 0) { if (count < dotpath) { h->data = COMMAND | count; } else { h->data = REL_COMMAND | count; h->next = relcmd.next; relcmd.next = h; } } h->hits = flg; h->cost += cost; return (h->data); } else { return (-count); } } /* * Remove any PATH related attributes from entry. */ static void zapentry(h) ENTRY *h; { h->data &= HASHZAP; } /* * Remove any PATH related attributes from whole hash database. */ void zaphash() { hscan(zapentry); relcmd.next = 0; } /* * Mark all relative commands as outdated after a chdir() */ void zapcd() { ENTRY *ptr = relcmd.next; while (ptr) { ptr->data |= CDMARK; ptr = ptr->next; } relcmd.next = 0; } /* * Print hash attributes for entry. */ static void hashout(h) ENTRY *h; { sigchk(); if (hashtype(h->data) == NOTFOUND) return; if (h->data & (BUILTIN | FUNCTION)) return; prn_buff(h->hits); if (h->data & REL_COMMAND) prc_buff('*'); prc_buff(TAB); prn_buff(h->cost); prc_buff(TAB); pr_path(h->key, hashdata(h->data)); prc_buff(NL); } /* * Print hash statistics. */ void hashpr() { prs_buff(_gettext("hits cost command\n")); hscan(hashout); } /* * Find index of "::" in PATH and remember the result in 'dotpath'. */ void set_dotpath() { unsigned char *path; int cnt = 1; dotpath = 10000; path = getpath((unsigned char *)""); while (path && *path) { if (*path == '/') { cnt++; } else { if (dotpath == 10000) { dotpath = cnt; } else { multrel = 1; return; } } path = nextpath(path); } multrel = 0; } /* * Add new function to hash. */ void hash_func(name) unsigned char *name; { ENTRY *h; ENTRY hentry; h = hfind(name); if (h) { h->data = FUNCTION; } else { hentry.data = FUNCTION; hentry.key = make(name); hentry.cost = 0; hentry.hits = 0; hentry.next = NULL; henter(hentry); } } /* * Remove function from hash. * Reestablish builtin if function did hide the builtin. */ void func_unhash(name) unsigned char *name; { const struct sysnod *sn; #ifdef HAVE_LOADABLE_LIBS const struct sysnod2 *sn2; #endif ENTRY *h; h = hfind(name); if (h && (h->data & FUNCTION)) { #ifdef HAVE_LOADABLE_LIBS if ((sn2 = sh_findbuiltin(name)) != NULL) { int i; i = SYSLOADABLE; h->data = (BUILTIN | i); } else #endif if ((sn = sysnlook(name, commands, no_commands)) != NULL) { int i; i = sn->sysval; if (sn->sysflg & BLT_SPC) i |= SPC_BUILTIN; h->data = (BUILTIN | i); } else { h->data = NOTFOUND; } } } /* * Hash search / entry code for "hash" builtin. */ short hash_cmd(name) unsigned char *name; { ENTRY *h; if (any('/', name)) return (COMMAND); h = hfind(name); if (h) { if (h->data & (BUILTIN | FUNCTION)) { return (h->data); } else if ((h->data & REL_COMMAND) == REL_COMMAND) { /* * unlink h from relative command list */ ENTRY *ptr = &relcmd; while (ptr-> next != h) ptr = ptr->next; ptr->next = h->next; } zapentry(h); } return (pathlook(name, 0, (struct argnod *)0)); } /* * Search command an print command type. * Return 0 if found, 1 if not. */ int what_is_path(name, verbose) unsigned char *name; int verbose; { ENTRY *h; int cnt; int amt; short hashval; const struct sysnod *sp; #ifdef HAVE_LOADABLE_LIBS const struct sysnod2 *sp2; struct sysnod sn; #endif h = hfind(name); if (h && (flags & ppath)) { /* * Do not hash regular commands with "command -p ..." */ if (!(h->data & (BUILTIN | FUNCTION))) h = NULL; } amt = prs_buff(name); if (h) { hashval = hashdata(h->data); switch (hashtype(h->data)) { case BUILTIN: if (!verbose) { prc_buff(NL); return (0); } #ifdef DO_POSIX_TYPE /* * In POSIX mode, we distinct three different types of * builtins and thus need to check explicitly. */ goto checkbuiltin; #else prs_buff(_gettext(" is a shell builtin\n")); return (0); #endif case FUNCTION: { struct namnod *n = lookup(name); struct fndnod *f = fndptr(n->funcval); if (!verbose) { prc_buff(NL); return (0); } prs_buff(_gettext(" is a function\n")); if (verbose <= 1) return (0); prs_buff(name); prs_buff((unsigned char *)"(){\n"); if (f != NULL) prf((struct trenod *)f->fndval); prs_buff((unsigned char *)"\n}\n"); return (0); } case REL_COMMAND: { short hash; if ((h->data & DOT_COMMAND) == DOT_COMMAND) { hash = pathlook(name, 0, (struct argnod *)0); if (hashtype(hash) == NOTFOUND) { if (!verbose) { unprs_buff(amt); return (ERR_NOTFOUND); } prs_buff(_gettext(" not found\n")); return (ERR_NOTFOUND); } else { hashval = hashdata(hash); } } } /* FALLTHROUGH */ case COMMAND: if (!verbose) { unprs_buff(amt); pr_path(name, hashval); prc_buff(NL); return (0); } prs_buff(_gettext(" is hashed (")); pr_path(name, hashval); prs_buff((unsigned char *)")\n"); return (0); } } #ifdef DO_POSIX_TYPE if (syslook(name, reserved, no_reserved)) { if (!verbose) { prc_buff(NL); return (0); } prs_buff(_gettext(" is a keyword\n")); return (0); } checkbuiltin: #endif #if defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN) if ((sp2 = sh_findbuiltin(name)) != NULL) { sn.sysflg = 0; sp = &sn; goto isbltin; } else #endif if ((sp = sysnlook(name, commands, no_commands)) != NULL) { #if defined(HAVE_LOADABLE_LIBS) && defined(DO_SYSBUILTIN) isbltin: #endif if (!verbose) { prc_buff(NL); return (0); } #ifdef DO_POSIX_TYPE if (sp->sysflg & BLT_SPC) prs_buff(_gettext(" is a special shell builtin\n")); else if (sp->sysflg & BLT_INT) prs_buff(_gettext(" is a shell intrinsic\n")); else #endif prs_buff(_gettext(" is a shell builtin\n")); return (0); } if ((cnt = findpath(name, 0)) > 0) { if (!verbose) { unprs_buff(amt); pr_path(name, cnt); prc_buff(NL); return (0); } prs_buff(_gettext(" is ")); pr_path(name, cnt); prc_buff(NL); return (0); } else { if (!verbose) { unprs_buff(amt); return (ERR_NOTFOUND); } prs_buff(_gettext(" not found\n")); return (ERR_NOTFOUND); } } /* * Find 'name' in PATH and return index. * Start search at 'oldpath'. */ static int findpath(name, oldpath) unsigned char *name; int oldpath; { unsigned char *path; int count = 1; unsigned char *p; int ok = 1; int e_code = 1; cost = 0; path = getpath(name); if (oldpath) { count = dotpath; while (--count && path) path = nextpath(path); if (oldpath > dotpath && path) { catpath(path, name); p = curstak(); cost = 1; if ((ok = chk_access(p, S_IEXEC, 1)) == 0) return (dotpath); else return (oldpath); } else { count = dotpath; } } while (path) { path = catpath(path, name); cost++; p = curstak(); if ((ok = chk_access(p, S_IEXEC, 1)) == 0) break; else e_code = max(e_code, ok); count++; } return (ok ? -e_code : count); } /* * Determine if file given by name is accessible with permissions * given by mode. * Regflag argument non-zero means not to consider * a non-regular file as executable. * * Return codes: * 0 Access OK * 1 Other error (e.g. file not found) * 2 Executable but not regular * 3 File found but access error */ #ifdef PROTOTYPES int chk_access(unsigned char *name, mode_t mode, int regflag) #else int chk_access(name, mode, regflag) unsigned char *name; /* The filename */ mode_t mode; /* F_OK, S_IREAD, S_IWRITE, S_IEXEC */ int regflag; /* if TRUE: file must be regular */ #endif { static int flag; static uid_t euid; struct stat statb; mode_t ftype; if (flag == 0) { euid = geteuid(); flag = 1; } if (stat((char *)name, &statb) == 0) { int amode = 0; ftype = statb.st_mode & S_IFMT; if (mode == S_IEXEC && regflag && ftype != S_IFREG) return (2); if (mode == F_OK) amode = F_OK; if (mode & S_IREAD) amode |= R_OK; if (mode & S_IWRITE) amode |= W_OK; if (mode & S_IEXEC) amode |= X_OK; #ifdef HAVE_FACCESSAT if (faccessat(AT_FDCWD, (char *)name, amode, AT_EACCESS) == 0) { #else #ifdef HAVE_ACCESS_E_OK if (access((char *)name, 010|amode) == 0) { #else if (access((char *)name, amode) == 0) { #endif #endif if (euid == 0) { if (ftype != S_IFREG || mode != S_IEXEC) return (0); /* * root can execute file as long as it has * execute permission for someone */ if (statb.st_mode & (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6))) { return (0); } return (3); } return (0); } } return (errno == EACCES ? 3 : 1); } /* * Print path for command based on command name and index in PATH */ static void pr_path(name, count) unsigned char *name; int count; { unsigned char *path; path = getpath(name); while (--count && path) path = nextpath(path); if (path == NULL) /* Paranoia for Coverity */ return; catpath(path, name); prs_buff(curstak()); } /* * Return 1 if argnode contains a PATH= definition. */ static int argpath(arg) struct argnod *arg; { unsigned char *s; unsigned char *start; while (arg) { s = arg->argval; start = s; if (letter(*s)) { while (alphanum(*s)) s++; if (*s == '=') { *s = 0; if (eq(start, pathname)) { *s = '='; return (1); } else { *s = '='; } } } arg = arg->argnxt; } return (0); }