| 1 | /*- |
| 2 | * Copyright (c) 1980, 1991 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by the University of |
| 16 | * California, Berkeley and its contributors. |
| 17 | * 4. Neither the name of the University nor the names of its contributors |
| 18 | * may be used to endorse or promote products derived from this software |
| 19 | * without specific prior written permission. |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | * SUCH DAMAGE. |
| 32 | */ |
| 33 | |
| 34 | #ifndef lint |
| 35 | static char sccsid[] = "@(#)exec.c 5.17 (Berkeley) 6/17/91"; |
| 36 | #endif /* not lint */ |
| 37 | |
| 38 | #include <sys/types.h> |
| 39 | #include <dirent.h> |
| 40 | #include <fcntl.h> |
| 41 | #include <errno.h> |
| 42 | #include <stdlib.h> |
| 43 | #include <string.h> |
| 44 | #include <unistd.h> |
| 45 | #if __STDC__ |
| 46 | # include <stdarg.h> |
| 47 | #else |
| 48 | # include <varargs.h> |
| 49 | #endif |
| 50 | |
| 51 | #include "csh.h" |
| 52 | #include "extern.h" |
| 53 | |
| 54 | /* |
| 55 | * System level search and execute of a command. We look in each directory |
| 56 | * for the specified command name. If the name contains a '/' then we |
| 57 | * execute only the full path name. If there is no search path then we |
| 58 | * execute only full path names. |
| 59 | */ |
| 60 | extern char **environ; |
| 61 | |
| 62 | /* |
| 63 | * As we search for the command we note the first non-trivial error |
| 64 | * message for presentation to the user. This allows us often |
| 65 | * to show that a file has the wrong mode/no access when the file |
| 66 | * is not in the last component of the search path, so we must |
| 67 | * go on after first detecting the error. |
| 68 | */ |
| 69 | static char *exerr; /* Execution error message */ |
| 70 | static Char *expath; /* Path for exerr */ |
| 71 | |
| 72 | /* |
| 73 | * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used |
| 74 | * to hash execs. If it is allocated (havhash true), then to tell |
| 75 | * whether ``name'' is (possibly) present in the i'th component |
| 76 | * of the variable path, you look at the bit in xhash indexed by |
| 77 | * hash(hashname("name"), i). This is setup automatically |
| 78 | * after .login is executed, and recomputed whenever ``path'' is |
| 79 | * changed. |
| 80 | * The two part hash function is designed to let texec() call the |
| 81 | * more expensive hashname() only once and the simple hash() several |
| 82 | * times (once for each path component checked). |
| 83 | * Byte size is assumed to be 8. |
| 84 | */ |
| 85 | #define HSHSIZ 8192 /* 1k bytes */ |
| 86 | #define HSHMASK (HSHSIZ - 1) |
| 87 | #define HSHMUL 243 |
| 88 | static char xhash[HSHSIZ / 8]; |
| 89 | |
| 90 | #define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) |
| 91 | #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ |
| 92 | #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ |
| 93 | static int hits, misses; |
| 94 | |
| 95 | /* Dummy search path for just absolute search when no path */ |
| 96 | static Char *justabs[] = {STRNULL, 0}; |
| 97 | |
| 98 | static void pexerr __P((void)); |
| 99 | static void texec __P((Char *, Char **)); |
| 100 | static int hashname __P((Char *)); |
| 101 | |
| 102 | void |
| 103 | doexec(t) |
| 104 | register struct command *t; |
| 105 | { |
| 106 | register Char *dp, **pv, **av, *sav; |
| 107 | register struct varent *v; |
| 108 | register bool slash; |
| 109 | register int hashval = 0, hashval1, i; |
| 110 | Char *blk[2]; |
| 111 | |
| 112 | /* |
| 113 | * Glob the command name. We will search $path even if this does something, |
| 114 | * as in sh but not in csh. One special case: if there is no PATH, then we |
| 115 | * execute only commands which start with '/'. |
| 116 | */ |
| 117 | blk[0] = t->t_dcom[0]; |
| 118 | blk[1] = 0; |
| 119 | gflag = 0, tglob(blk); |
| 120 | if (gflag) { |
| 121 | pv = globall(blk); |
| 122 | if (pv == 0) { |
| 123 | setname(short2str(blk[0])); |
| 124 | stderror(ERR_NAME | ERR_NOMATCH); |
| 125 | } |
| 126 | gargv = 0; |
| 127 | } |
| 128 | else |
| 129 | pv = saveblk(blk); |
| 130 | |
| 131 | trim(pv); |
| 132 | |
| 133 | exerr = 0; |
| 134 | expath = Strsave(pv[0]); |
| 135 | Vexpath = expath; |
| 136 | |
| 137 | v = adrof(STRpath); |
| 138 | if (v == 0 && expath[0] != '/') { |
| 139 | blkfree(pv); |
| 140 | pexerr(); |
| 141 | } |
| 142 | slash = any(short2str(expath), '/'); |
| 143 | |
| 144 | /* |
| 145 | * Glob the argument list, if necessary. Otherwise trim off the quote bits. |
| 146 | */ |
| 147 | gflag = 0; |
| 148 | av = &t->t_dcom[1]; |
| 149 | tglob(av); |
| 150 | if (gflag) { |
| 151 | av = globall(av); |
| 152 | if (av == 0) { |
| 153 | blkfree(pv); |
| 154 | setname(short2str(expath)); |
| 155 | stderror(ERR_NAME | ERR_NOMATCH); |
| 156 | } |
| 157 | gargv = 0; |
| 158 | } |
| 159 | else |
| 160 | av = saveblk(av); |
| 161 | |
| 162 | blkfree(t->t_dcom); |
| 163 | t->t_dcom = blkspl(pv, av); |
| 164 | xfree((ptr_t) pv); |
| 165 | xfree((ptr_t) av); |
| 166 | av = t->t_dcom; |
| 167 | trim(av); |
| 168 | |
| 169 | if (*av == NULL || **av == '\0') |
| 170 | pexerr(); |
| 171 | |
| 172 | xechoit(av); /* Echo command if -x */ |
| 173 | /* |
| 174 | * Since all internal file descriptors are set to close on exec, we don't |
| 175 | * need to close them explicitly here. Just reorient ourselves for error |
| 176 | * messages. |
| 177 | */ |
| 178 | SHIN = 0; |
| 179 | SHOUT = 1; |
| 180 | SHDIAG = 2; |
| 181 | OLDSTD = 0; |
| 182 | /* |
| 183 | * We must do this AFTER any possible forking (like `foo` in glob) so that |
| 184 | * this shell can still do subprocesses. |
| 185 | */ |
| 186 | (void) sigsetmask((sigset_t) 0); |
| 187 | /* |
| 188 | * If no path, no words in path, or a / in the filename then restrict the |
| 189 | * command search. |
| 190 | */ |
| 191 | if (v == 0 || v->vec[0] == 0 || slash) |
| 192 | pv = justabs; |
| 193 | else |
| 194 | pv = v->vec; |
| 195 | sav = Strspl(STRslash, *av);/* / command name for postpending */ |
| 196 | Vsav = sav; |
| 197 | if (havhash) |
| 198 | hashval = hashname(*av); |
| 199 | i = 0; |
| 200 | hits++; |
| 201 | do { |
| 202 | /* |
| 203 | * Try to save time by looking at the hash table for where this command |
| 204 | * could be. If we are doing delayed hashing, then we put the names in |
| 205 | * one at a time, as the user enters them. This is kinda like Korn |
| 206 | * Shell's "tracked aliases". |
| 207 | */ |
| 208 | if (!slash && pv[0][0] == '/' && havhash) { |
| 209 | hashval1 = hash(hashval, i); |
| 210 | if (!bit(xhash, hashval1)) |
| 211 | goto cont; |
| 212 | } |
| 213 | if (pv[0][0] == 0 || eq(pv[0], STRdot)) /* don't make ./xxx */ |
| 214 | texec(*av, av); |
| 215 | else { |
| 216 | dp = Strspl(*pv, sav); |
| 217 | Vdp = dp; |
| 218 | texec(dp, av); |
| 219 | Vdp = 0; |
| 220 | xfree((ptr_t) dp); |
| 221 | } |
| 222 | misses++; |
| 223 | cont: |
| 224 | pv++; |
| 225 | i++; |
| 226 | } while (*pv); |
| 227 | hits--; |
| 228 | Vsav = 0; |
| 229 | xfree((ptr_t) sav); |
| 230 | pexerr(); |
| 231 | } |
| 232 | |
| 233 | static void |
| 234 | pexerr() |
| 235 | { |
| 236 | /* Couldn't find the damn thing */ |
| 237 | if (expath) { |
| 238 | setname(short2str(expath)); |
| 239 | Vexpath = 0; |
| 240 | xfree((ptr_t) expath); |
| 241 | expath = 0; |
| 242 | } |
| 243 | else |
| 244 | setname(""); |
| 245 | if (exerr) |
| 246 | stderror(ERR_NAME | ERR_STRING, exerr); |
| 247 | stderror(ERR_NAME | ERR_COMMAND); |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | * Execute command f, arg list t. |
| 252 | * Record error message if not found. |
| 253 | * Also do shell scripts here. |
| 254 | */ |
| 255 | static void |
| 256 | texec(sf, st) |
| 257 | Char *sf; |
| 258 | register Char **st; |
| 259 | { |
| 260 | register char **t; |
| 261 | register char *f; |
| 262 | register struct varent *v; |
| 263 | register Char **vp; |
| 264 | Char *lastsh[2]; |
| 265 | int fd; |
| 266 | unsigned char c; |
| 267 | Char *st0, **ost; |
| 268 | |
| 269 | /* The order for the conversions is significant */ |
| 270 | t = short2blk(st); |
| 271 | f = short2str(sf); |
| 272 | Vt = t; |
| 273 | errno = 0; /* don't use a previous error */ |
| 274 | (void) execve(f, t, environ); |
| 275 | Vt = 0; |
| 276 | blkfree((Char **) t); |
| 277 | switch (errno) { |
| 278 | |
| 279 | case ENOEXEC: |
| 280 | /* |
| 281 | * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute |
| 282 | * it, don't feed it to the shell if it looks like a binary! |
| 283 | */ |
| 284 | if ((fd = open(f, O_RDONLY)) != -1) { |
| 285 | if (read(fd, (char *) &c, 1) == 1) { |
| 286 | if (!Isprint(c) && (c != '\n' && c != '\t')) { |
| 287 | (void) close(fd); |
| 288 | /* |
| 289 | * We *know* what ENOEXEC means. |
| 290 | */ |
| 291 | stderror(ERR_ARCH, f, strerror(errno)); |
| 292 | } |
| 293 | } |
| 294 | #ifdef _PATH_BSHELL |
| 295 | else |
| 296 | c = '#'; |
| 297 | #endif |
| 298 | (void) close(fd); |
| 299 | } |
| 300 | /* |
| 301 | * If there is an alias for shell, then put the words of the alias in |
| 302 | * front of the argument list replacing the command name. Note no |
| 303 | * interpretation of the words at this point. |
| 304 | */ |
| 305 | v = adrof1(STRshell, &aliases); |
| 306 | if (v == 0) { |
| 307 | vp = lastsh; |
| 308 | vp[0] = adrof(STRshell) ? value(STRshell) : STR_SHELLPATH; |
| 309 | vp[1] = NULL; |
| 310 | #ifdef _PATH_BSHELL |
| 311 | if (fd != -1 && c != '#') |
| 312 | vp[0] = STR_BSHELL; |
| 313 | #endif |
| 314 | } |
| 315 | else |
| 316 | vp = v->vec; |
| 317 | st0 = st[0]; |
| 318 | st[0] = sf; |
| 319 | ost = st; |
| 320 | st = blkspl(vp, st); /* Splice up the new arglst */ |
| 321 | ost[0] = st0; |
| 322 | sf = *st; |
| 323 | /* The order for the conversions is significant */ |
| 324 | t = short2blk(st); |
| 325 | f = short2str(sf); |
| 326 | xfree((ptr_t) st); |
| 327 | Vt = t; |
| 328 | (void) execve(f, t, environ); |
| 329 | Vt = 0; |
| 330 | blkfree((Char **) t); |
| 331 | /* The sky is falling, the sky is falling! */ |
| 332 | |
| 333 | case ENOMEM: |
| 334 | stderror(ERR_SYSTEM, f, strerror(errno)); |
| 335 | |
| 336 | case ENOENT: |
| 337 | break; |
| 338 | |
| 339 | default: |
| 340 | if (exerr == 0) { |
| 341 | exerr = strerror(errno); |
| 342 | if (expath) |
| 343 | xfree((ptr_t) expath); |
| 344 | expath = Strsave(sf); |
| 345 | Vexpath = expath; |
| 346 | } |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | /*ARGSUSED*/ |
| 351 | void |
| 352 | execash(t, kp) |
| 353 | char **t; |
| 354 | register struct command *kp; |
| 355 | { |
| 356 | if (chkstop == 0 && setintr) |
| 357 | panystop(0); |
| 358 | rechist(); |
| 359 | (void) signal(SIGINT, parintr); |
| 360 | (void) signal(SIGQUIT, parintr); |
| 361 | (void) signal(SIGTERM, parterm); /* if doexec loses, screw */ |
| 362 | lshift(kp->t_dcom, 1); |
| 363 | exiterr = 1; |
| 364 | doexec(kp); |
| 365 | /* NOTREACHED */ |
| 366 | } |
| 367 | |
| 368 | void |
| 369 | xechoit(t) |
| 370 | Char **t; |
| 371 | { |
| 372 | if (adrof(STRecho)) { |
| 373 | flush(); |
| 374 | haderr = 1; |
| 375 | blkpr(t), xputchar('\n'); |
| 376 | haderr = 0; |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | /*VARARGS0*/ |
| 381 | void |
| 382 | dohash() |
| 383 | { |
| 384 | DIR *dirp; |
| 385 | register struct dirent *dp; |
| 386 | register int cnt; |
| 387 | int i = 0; |
| 388 | struct varent *v = adrof(STRpath); |
| 389 | Char **pv; |
| 390 | int hashval; |
| 391 | |
| 392 | havhash = 1; |
| 393 | for (cnt = 0; cnt < sizeof xhash; cnt++) |
| 394 | xhash[cnt] = 0; |
| 395 | if (v == 0) |
| 396 | return; |
| 397 | for (pv = v->vec; *pv; pv++, i++) { |
| 398 | if (pv[0][0] != '/') |
| 399 | continue; |
| 400 | dirp = opendir(short2str(*pv)); |
| 401 | if (dirp == NULL) |
| 402 | continue; |
| 403 | while ((dp = readdir(dirp)) != NULL) { |
| 404 | if (dp->d_ino == 0) |
| 405 | continue; |
| 406 | if (dp->d_name[0] == '.' && |
| 407 | (dp->d_name[1] == '\0' || |
| 408 | dp->d_name[1] == '.' && dp->d_name[2] == '\0')) |
| 409 | continue; |
| 410 | hashval = hash(hashname(str2short(dp->d_name)), i); |
| 411 | bis(xhash, hashval); |
| 412 | /* tw_add_comm_name (dp->d_name); */ |
| 413 | } |
| 414 | (void) closedir(dirp); |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | void |
| 419 | dounhash() |
| 420 | { |
| 421 | havhash = 0; |
| 422 | } |
| 423 | |
| 424 | void |
| 425 | hashstat() |
| 426 | { |
| 427 | if (hits + misses) |
| 428 | xprintf("%d hits, %d misses, %d%%\n", |
| 429 | hits, misses, 100 * hits / (hits + misses)); |
| 430 | } |
| 431 | |
| 432 | /* |
| 433 | * Hash a command name. |
| 434 | */ |
| 435 | static int |
| 436 | hashname(cp) |
| 437 | register Char *cp; |
| 438 | { |
| 439 | register long h = 0; |
| 440 | |
| 441 | while (*cp) |
| 442 | h = hash(h, *cp++); |
| 443 | return ((int) h); |
| 444 | } |