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