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