1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley Software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include "sh.h" 18 #include <dirent.h> 19 #include <string.h> 20 #include "sh.tconst.h" 21 #include "sh_policy.h" 22 23 24 /* 25 * C shell 26 */ 27 28 /* 29 * System level search and execute of a command. 30 * We look in each directory for the specified command name. 31 * If the name contains a '/' then we execute only the full path name. 32 * If there is no search path then we execute only full path names. 33 */ 34 35 /* 36 * As we search for the command we note the first non-trivial error 37 * message for presentation to the user. This allows us often 38 * to show that a file has the wrong mode/no access when the file 39 * is not in the last component of the search path, so we must 40 * go on after first detecting the error. 41 */ 42 char *exerr; /* Execution error message */ 43 44 void pexerr(void); 45 void texec(struct command *, tchar *, tchar **); 46 void xechoit(tchar **); 47 void dohash(char []); 48 49 static void tconvert(struct command *, tchar *, tchar **); 50 51 52 extern DIR *opendir_(tchar *); 53 54 void 55 doexec(struct command *t) 56 { 57 tchar *sav; 58 tchar *dp, **pv, **av; 59 struct varent *v; 60 bool slash; 61 int hashval, hashval1, i; 62 tchar *blk[2]; 63 #ifdef TRACE 64 tprintf("TRACE- doexec()\n"); 65 #endif 66 67 /* 68 * Glob the command name. If this does anything, then we 69 * will execute the command only relative to ".". One special 70 * case: if there is no PATH, then we execute only commands 71 * which start with '/'. 72 */ 73 dp = globone(t->t_dcom[0]); 74 sav = t->t_dcom[0]; 75 exerr = 0; t->t_dcom[0] = dp; 76 setname(dp); 77 xfree(sav); 78 v = adrof(S_path /* "path" */); 79 if (v == 0 && dp[0] != '/') { 80 pexerr(); 81 } 82 slash = gflag; 83 84 /* 85 * Glob the argument list, if necessary. 86 * Otherwise trim off the quote bits. 87 */ 88 gflag = 0; av = &t->t_dcom[1]; 89 tglob(av); 90 if (gflag) { 91 av = glob(av); 92 if (av == 0) 93 error("No match"); 94 } 95 blk[0] = t->t_dcom[0]; 96 blk[1] = 0; 97 av = blkspl(blk, av); 98 #ifdef VFORK 99 Vav = av; 100 #endif 101 trim(av); 102 slash |= any('/', av[0]); 103 104 xechoit(av); /* Echo command if -x */ 105 /* 106 * Since all internal file descriptors are set to close on exec, 107 * we don't need to close them explicitly here. Just reorient 108 * ourselves for error messages. 109 */ 110 SHIN = 0; SHOUT = 1; SHDIAG = 2; OLDSTD = 0; 111 112 /* 113 * We must do this AFTER any possible forking (like `foo` 114 * in glob) so that this shell can still do subprocesses. 115 */ 116 (void) sigsetmask(0); 117 118 /* 119 * If no path, no words in path, or a / in the filename 120 * then restrict the command search. 121 */ 122 if (v == 0 || v->vec[0] == 0 || slash) 123 pv = justabs; 124 else 125 pv = v->vec; 126 sav = strspl(S_SLASH /* "/" */, *av); /* / command name for postpending */ 127 #ifdef VFORK 128 Vsav = sav; 129 #endif 130 if (havhash) 131 hashval = hashname(*av); 132 i = 0; 133 #ifdef VFORK 134 hits++; 135 #endif 136 do { 137 if (!slash && pv[0][0] == '/' && havhash) { 138 hashval1 = hash(hashval, i); 139 if (!bit(xhash, hashval1)) 140 goto cont; 141 } 142 143 if (pv[0][0] == 0 || eq(pv[0], S_DOT /* "." */)) { /* don't make ./xxx */ 144 texec(t, *av, av); 145 } else { 146 dp = strspl(*pv, sav); 147 #ifdef VFORK 148 Vdp = dp; 149 #endif 150 texec(t, dp, av); 151 #ifdef VFORK 152 Vdp = 0; 153 #endif 154 xfree(dp); 155 } 156 #ifdef VFORK 157 misses++; 158 #endif 159 cont: 160 pv++; 161 i++; 162 } while (*pv); 163 #ifdef VFORK 164 hits--; 165 #endif 166 #ifdef VFORK 167 Vsav = 0; 168 Vav = 0; 169 #endif 170 xfree(sav); 171 xfree((char *)av); 172 pexerr(); 173 } 174 175 void 176 pexerr(void) 177 { 178 179 #ifdef TRACE 180 tprintf("TRACE- pexerr()\n"); 181 #endif 182 /* Couldn't find the damn thing */ 183 if (exerr) 184 bferr(exerr); 185 bferr("Command not found"); 186 } 187 188 /* 189 * Execute command f, arg list t. 190 * Record error message if not found. 191 * Also do shell scripts here. 192 */ 193 void 194 texec(struct command *cmd, tchar *f, tchar **t) 195 { 196 int pfstatus = 0; 197 struct varent *v; 198 tchar **vp; 199 tchar *lastsh[2]; 200 201 #ifdef TRACE 202 tprintf("TRACE- texec()\n"); 203 #endif 204 /* convert cfname and cargs from tchar to char */ 205 tconvert(cmd, f, t); 206 207 if (pfcshflag == 1) { 208 pfstatus = secpolicy_pfexec((const char *)(cmd->cfname), 209 cmd->cargs, (const char **)NULL); 210 if (pfstatus != NOATTRS) { 211 errno = pfstatus; 212 } 213 } 214 if ((pfcshflag == 0) || (pfstatus == NOATTRS)) { 215 execv(cmd->cfname, cmd->cargs); 216 } 217 218 /* 219 * exec returned, free up allocations from above 220 * tconvert(), zero cfname and cargs to prevent 221 * duplicate free() in freesyn() 222 */ 223 xfree(cmd->cfname); 224 chr_blkfree(cmd->cargs); 225 cmd->cfname = (char *)0; 226 cmd->cargs = (char **)0; 227 228 switch (errno) { 229 case ENOEXEC: 230 /* check that this is not a binary file */ 231 { 232 int ff = open_(f, 0); 233 tchar ch[MB_LEN_MAX]; 234 235 if (ff != -1 && read_(ff, ch, 1) == 1 && 236 !isprint(ch[0]) && !isspace(ch[0])) { 237 printf("Cannot execute binary file.\n"); 238 Perror(f); 239 (void) close(ff); 240 unsetfd(ff); 241 return; 242 } 243 (void) close(ff); 244 unsetfd(ff); 245 } 246 /* 247 * If there is an alias for shell, then 248 * put the words of the alias in front of the 249 * argument list replacing the command name. 250 * Note no interpretation of the words at this point. 251 */ 252 v = adrof1(S_shell /* "shell" */, &aliases); 253 if (v == 0) { 254 #ifdef OTHERSH 255 int ff = open_(f, 0); 256 tchar ch[MB_LEN_MAX]; 257 #endif 258 259 vp = lastsh; 260 vp[0] = adrof(S_shell /* "shell" */) ? value(S_shell /* "shell" */) : S_SHELLPATH /* SHELLPATH */; 261 vp[1] = (tchar *) NULL; 262 #ifdef OTHERSH 263 if (ff != -1 && read_(ff, ch, 1) == 1 && ch[0] != '#') 264 vp[0] = S_OTHERSH /* OTHERSH */; 265 (void) close(ff); 266 unsetfd(ff); 267 #endif 268 } else 269 vp = v->vec; 270 t[0] = f; 271 t = blkspl(vp, t); /* Splice up the new arglst */ 272 f = *t; 273 274 tconvert(cmd, f, t); /* convert tchar to char */ 275 276 /* 277 * now done with tchar arg list t, 278 * free the space calloc'd by above blkspl() 279 */ 280 xfree((char *)t); 281 282 execv(cmd->cfname, cmd->cargs); /* exec the command */ 283 284 /* exec returned, same free'ing as above */ 285 xfree(cmd->cfname); 286 chr_blkfree(cmd->cargs); 287 cmd->cfname = (char *)0; 288 cmd->cargs = (char **)0; 289 290 /* The sky is falling, the sky is falling! */ 291 292 case ENOMEM: 293 Perror(f); 294 295 case ENOENT: 296 break; 297 298 default: 299 if (exerr == 0) { 300 exerr = strerror(errno); 301 setname(f); 302 } 303 } 304 } 305 306 307 static void 308 tconvert(struct command *cmd, tchar *fname, tchar **list) 309 { 310 char **rc; 311 int len; 312 313 cmd->cfname = tstostr(NULL, fname); 314 315 len = blklen(list); 316 rc = cmd->cargs = (char **) 317 xcalloc((uint_t)(len + 1), sizeof (char **)); 318 while (len--) 319 *rc++ = tstostr(NULL, *list++); 320 *rc = NULL; 321 } 322 323 324 /*ARGSUSED*/ 325 void 326 execash(tchar **t, struct command *kp) 327 { 328 #ifdef TRACE 329 tprintf("TRACE- execash()\n"); 330 #endif 331 332 rechist(); 333 (void) signal(SIGINT, parintr); 334 (void) signal(SIGQUIT, parintr); 335 (void) signal(SIGTERM, parterm); /* if doexec loses, screw */ 336 lshift(kp->t_dcom, 1); 337 exiterr++; 338 doexec(kp); 339 /*NOTREACHED*/ 340 } 341 342 void 343 xechoit(tchar **t) 344 { 345 #ifdef TRACE 346 tprintf("TRACE- xechoit()\n"); 347 #endif 348 349 if (adrof(S_echo /* "echo" */)) { 350 flush(); 351 haderr = 1; 352 blkpr(t), Putchar('\n'); 353 haderr = 0; 354 } 355 } 356 357 /* 358 * This routine called when user enters "rehash". 359 * Both the path and cdpath caching arrays will 360 * be rehashed, via calling dohash. If either 361 * variable is not set with a value, then dohash 362 * just exits. 363 */ 364 void 365 dorehash(void) 366 { 367 dohash(xhash); 368 dohash(xhash2); 369 } 370 371 /* 372 * Fill up caching arrays for path and cdpath 373 */ 374 void 375 dohash(char cachearray[]) 376 { 377 struct stat stb; 378 DIR *dirp; 379 struct dirent *dp; 380 int cnt; 381 int i = 0; 382 struct varent *v; 383 tchar **pv; 384 int hashval; 385 tchar curdir_[MAXNAMLEN+1]; 386 387 #ifdef TRACE 388 tprintf("TRACE- dohash()\n"); 389 #endif 390 /* Caching $path */ 391 if (cachearray == xhash) { 392 havhash = 1; 393 v = adrof(S_path /* "path" */); 394 } else { /* Caching $cdpath */ 395 havhash2 = 1; 396 v = adrof(S_cdpath /* "cdpath" */); 397 } 398 399 for (cnt = 0; cnt < (HSHSIZ / 8); cnt++) 400 cachearray[cnt] = 0; 401 if (v == 0) 402 { 403 return; 404 } 405 for (pv = v->vec; *pv; pv++, i++) { 406 if (pv[0][0] != '/') 407 continue; 408 dirp = opendir_(*pv); 409 if (dirp == NULL) 410 continue; 411 if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) { 412 unsetfd(dirp->dd_fd); 413 closedir_(dirp); 414 continue; 415 } 416 while ((dp = readdir(dirp)) != NULL) { 417 if (dp->d_ino == 0) 418 continue; 419 if (dp->d_name[0] == '.' && 420 (dp->d_name[1] == '\0' || 421 dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 422 continue; 423 hashval = hash(hashname(strtots(curdir_, dp->d_name)), i); 424 bis(cachearray, hashval); 425 } 426 unsetfd(dirp->dd_fd); 427 closedir_(dirp); 428 } 429 } 430 431 void 432 dounhash(void) 433 { 434 435 #ifdef TRACE 436 tprintf("TRACE- dounhash()\n"); 437 #endif 438 havhash = 0; 439 havhash2 = 0; 440 } 441 442 #ifdef VFORK 443 void 444 hashstat(void) 445 { 446 #ifdef TRACE 447 tprintf("TRACE- hashstat_()\n"); 448 #endif 449 450 if (hits+misses) 451 printf("%d hits, %d misses, %d%%\n", 452 hits, misses, 100 * hits / (hits + misses)); 453 } 454 #endif 455 456 /* 457 * Hash a command name. 458 */ 459 int 460 hashname(tchar *cp) 461 { 462 long h = 0; 463 464 #ifdef TRACE 465 tprintf("TRACE- hashname()\n"); 466 #endif 467 while (*cp) 468 h = hash(h, *cp++); 469 return ((int)h); 470 } 471