1 /* $NetBSD: exec.c,v 1.27 2007/12/15 19:44:37 perry Exp $ */
4 * Copyright (c) 1980, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)exec.c 8.3 (Berkeley) 5/23/95";
37 __RCSID("$NetBSD: exec.c,v 1.27 2007/12/15 19:44:37 perry Exp $");
41 #include <sys/param.h>
43 #include <sys/types.h>
57 * System level search and execute of a command. We look in each directory
58 * for the specified command name. If the name contains a '/' then we
59 * execute only the full path name. If there is no search path then we
60 * execute only full path names.
62 extern char **environ
;
65 * As we search for the command we note the first non-trivial error
66 * message for presentation to the user. This allows us often
67 * to show that a file has the wrong mode/no access when the file
68 * is not in the last component of the search path, so we must
69 * go on after first detecting the error.
71 static const char *exerr
; /* Execution error message */
72 static Char
*expath
; /* Path for exerr */
75 * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used
76 * to hash execs. If it is allocated (havhash true), then to tell
77 * whether ``name'' is (possibly) present in the i'th component
78 * of the variable path, you look at the bit in xhash indexed by
79 * hash(hashname("name"), i). This is setup automatically
80 * after .login is executed, and recomputed whenever ``path'' is
82 * The two part hash function is designed to let texec() call the
83 * more expensive hashname() only once and the simple hash() several
84 * times (once for each path component checked).
85 * Byte size is assumed to be 8.
87 #define HSHSIZ 8192 /* 1k bytes */
88 #define HSHMASK (HSHSIZ - 1)
90 static char xhash
[HSHSIZ
/ 8];
92 #define hash(a, b) (((a) * HSHMUL + (b)) & HSHMASK)
93 #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */
94 #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */
95 static int hits
, misses
;
97 /* Dummy search path for just absolute search when no path */
98 static Char
*justabs
[] = {STRNULL
, 0};
100 static void pexerr(void) __dead
;
101 static void texec(Char
*, Char
**);
102 static int hashname(Char
*);
103 static int tellmewhat(struct wordent
*, Char
*);
104 static int executable(Char
*, Char
*, int);
105 static int iscommand(Char
*);
109 doexec(Char
**v
, struct command
*t
)
111 struct varent
*pathv
;
112 Char
*blk
[2], **av
, *dp
, **pv
, *sav
;
113 int i
, hashval
, hashval1
;
119 * Glob the command name. We will search $path even if this does something,
120 * as in sh but not in csh. One special case: if there is no PATH, then we
121 * execute only commands which start with '/'.
123 blk
[0] = t
->t_dcom
[0];
125 gflag
= 0, tglob(blk
);
129 setname(vis_str(blk
[0]));
130 stderror(ERR_NAME
| ERR_NOMATCH
);
140 expath
= Strsave(pv
[0]);
143 pathv
= adrof(STRpath
);
144 if (pathv
== 0 && expath
[0] != '/') {
148 slash
= any(short2str(expath
), '/');
151 * Glob the argument list, if necessary. Otherwise trim off the quote bits.
160 setname(vis_str(expath
));
161 stderror(ERR_NAME
| ERR_NOMATCH
);
169 t
->t_dcom
= blkspl(pv
, av
);
175 if (*av
== NULL
|| **av
== '\0')
178 xechoit(av
); /* Echo command if -x */
180 * Since all internal file descriptors are set to close on exec, we don't
181 * need to close them explicitly here. Just reorient ourselves for error
189 * We must do this AFTER any possible forking (like `foo` in glob) so that
190 * this shell can still do subprocesses.
192 sigemptyset(&nsigset
);
193 (void)sigprocmask(SIG_SETMASK
, &nsigset
, NULL
);
195 * If no path, no words in path, or a / in the filename then restrict the
198 if (pathv
== 0 || pathv
->vec
[0] == 0 || slash
)
202 sav
= Strspl(STRslash
, *av
); /* / command name for postpending */
205 hashval
= hashname(*av
);
210 * Try to save time by looking at the hash table for where this command
211 * could be. If we are doing delayed hashing, then we put the names in
212 * one at a time, as the user enters them. This is kinda like Korn
213 * Shell's "tracked aliases".
215 if (!slash
&& pv
[0][0] == '/' && havhash
) {
216 hashval1
= hash(hashval
, i
);
217 if (!bit(xhash
, hashval1
))
220 if (pv
[0][0] == 0 || eq(pv
[0], STRdot
)) /* don't make ./xxx */
223 dp
= Strspl(*pv
, sav
);
244 /* Couldn't find the damn thing */
246 setname(vis_str(expath
));
248 xfree((ptr_t
)expath
);
254 stderror(ERR_NAME
| ERR_STRING
, exerr
);
256 stderror(ERR_NAME
| ERR_COMMAND
);
261 * Execute command f, arg list t.
262 * Record error message if not found.
263 * Also do shell scripts here.
266 texec(Char
*sf
, Char
**st
)
269 Char
*lastsh
[2], **vp
, *st0
, **ost
;
272 unsigned char c
= '\0';
274 /* The order for the conversions is significant */
278 errno
= 0; /* don't use a previous error */
279 (void)execve(f
, t
, environ
);
286 * From: casper@fwi.uva.nl (Casper H.S. Dik) If we could not execute
287 * it, don't feed it to the shell if it looks like a binary!
289 if ((fd
= open(f
, O_RDONLY
)) != -1) {
290 if (read(fd
, (char *)&c
, 1) == 1) {
291 if (!Isprint(c
) && (c
!= '\n' && c
!= '\t')) {
294 * We *know* what ENOEXEC means.
296 stderror(ERR_ARCH
, f
, strerror(errno
));
306 * If there is an alias for shell, then put the words of the alias in
307 * front of the argument list replacing the command name. Note no
308 * interpretation of the words at this point.
310 v
= adrof1(STRshell
, &aliases
);
313 vp
[0] = adrof(STRshell
) ? value(STRshell
) : STR_SHELLPATH
;
316 if (fd
!= -1 && c
!= '#')
325 st
= blkspl(vp
, st
); /* Splice up the new arglst */
328 /* The order for the conversions is significant */
333 (void)execve(f
, t
, environ
);
335 blkfree((Char
**) t
);
339 stderror(ERR_SYSTEM
, f
, strerror(errno
));
347 exerr
= strerror(errno
);
349 xfree((ptr_t
) expath
);
350 expath
= Strsave(sf
);
358 execash(Char
**t
, struct command
*kp
)
361 sig_t osigint
, osigquit
, osigterm
;
362 int my_reenter
, odidfds
, oOLDSTD
, oSHERR
, oSHIN
, oSHOUT
;
363 int saveDIAG
, saveIN
, saveOUT
, saveSTD
;
365 if (chkstop
== 0 && setintr
)
368 * Hmm, we don't really want to do that now because we might
369 * fail, but what is the choice
373 osigint
= signal(SIGINT
, parintr
);
374 osigquit
= signal(SIGQUIT
, parintr
);
375 osigterm
= signal(SIGTERM
, parterm
);
383 saveIN
= dcopy(SHIN
, -1);
384 saveOUT
= dcopy(SHOUT
, -1);
385 saveDIAG
= dcopy(SHERR
, -1);
386 saveSTD
= dcopy(OLDSTD
, -1);
388 lshift(kp
->t_dcom
, 1);
392 if ((my_reenter
= setexit()) == 0) {
394 SHOUT
= dcopy(1, -1);
395 SHERR
= dcopy(2, -1);
400 (void)signal(SIGINT
, osigint
);
401 (void)signal(SIGQUIT
, osigquit
);
402 (void)signal(SIGTERM
, osigterm
);
410 SHIN
= dmove(saveIN
, oSHIN
);
411 SHOUT
= dmove(saveOUT
, oSHOUT
);
412 SHERR
= dmove(saveDIAG
, oSHERR
);
413 OLDSTD
= dmove(saveSTD
, oOLDSTD
);
417 stderror(ERR_SILENT
);
423 if (adrof(STRecho
)) {
424 int odidfds
= didfds
;
425 (void)fflush(csherr
);
429 (void)fputc('\n', csherr
);
430 (void)fflush(csherr
);
437 dohash(Char
**v
, struct command
*t
)
440 struct varent
*pathv
;
448 pathv
= adrof(STRpath
);
450 for (cnt
= 0; cnt
< sizeof xhash
; cnt
++)
454 for (pv
= pathv
->vec
; *pv
; pv
++, i
++) {
457 dirp
= opendir(short2str(*pv
));
460 while ((dp
= readdir(dirp
)) != NULL
) {
463 if (dp
->d_name
[0] == '.' &&
464 (dp
->d_name
[1] == '\0' ||
465 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
467 hashval
= hash(hashname(str2short(dp
->d_name
)), i
);
469 /* tw_add_comm_name (dp->d_name); */
471 (void) closedir(dirp
);
477 dounhash(Char
**v
, struct command
*t
)
484 hashstat(Char
**v
, struct command
*t
)
487 (void)fprintf(cshout
, "%d hits, %d misses, %d%%\n",
488 hits
, misses
, 100 * hits
/ (hits
+ misses
));
492 * Hash a command name.
505 iscommand(Char
*name
)
509 int hashval
, hashval1
, i
;
513 slash
= any(short2str(name
), '/');
516 if (v
== 0 || v
->vec
[0] == 0 || slash
)
520 sav
= Strspl(STRslash
, name
); /* / command name for postpending */
522 hashval
= hashname(name
);
525 if (!slash
&& pv
[0][0] == '/' && havhash
) {
526 hashval1
= hash(hashval
, i
);
527 if (!bit(xhash
, hashval1
))
530 if (pv
[0][0] == 0 || eq(pv
[0], STRdot
)) { /* don't make ./xxx */
531 if (executable(NULL
, name
, 0)) {
537 if (executable(*pv
, sav
, 0)) {
551 * Andreas Luik <luik@isaak.isa.de>
552 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
556 * is the executable() routine below and changes to iscommand().
561 * executable() examines the pathname obtained by concatenating dir and name
562 * (dir may be NULL), and returns 1 either if it is executable by us, or
563 * if dir_ok is set and the pathname refers to a directory.
564 * This is a bit kludgy, but in the name of optimization...
567 executable(Char
*dir
, Char
*name
, int dir_ok
)
570 Char path
[MAXPATHLEN
+ 1], *dp
, *sp
;
574 for (dp
= path
, sp
= dir
; *sp
; *dp
++ = *sp
++)
575 if (dp
== &path
[MAXPATHLEN
+ 1]) {
579 for (sp
= name
; *sp
; *dp
++ = *sp
++)
580 if (dp
== &path
[MAXPATHLEN
+ 1]) {
585 strname
= short2str(path
);
588 strname
= short2str(name
);
589 return (stat(strname
, &stbuf
) != -1 && ((S_ISREG(stbuf
.st_mode
) &&
590 /* save time by not calling access() in the hopeless case */
591 (stbuf
.st_mode
& (S_IXOTH
| S_IXGRP
| S_IXUSR
)) &&
592 access(strname
, X_OK
) == 0) || (dir_ok
&& S_ISDIR(stbuf
.st_mode
))));
595 /* The dowhich() is by:
596 * Andreas Luik <luik@isaak.isa.de>
597 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung
605 dowhich(Char
**v
, struct command
*c
)
607 struct wordent lexw
[3];
610 lexw
[0].next
= &lexw
[1];
611 lexw
[1].next
= &lexw
[2];
612 lexw
[2].next
= &lexw
[0];
614 lexw
[0].prev
= &lexw
[2];
615 lexw
[1].prev
= &lexw
[0];
616 lexw
[2].prev
= &lexw
[1];
618 lexw
[0].word
= STRNULL
;
619 lexw
[2].word
= STRret
;
622 if ((vp
= adrof1(*v
, &aliases
)) != NULL
) {
623 (void)fprintf(cshout
, "%s: \t aliased to ", vis_str(*v
));
624 blkpr(cshout
, vp
->vec
);
625 (void)fputc('\n', cshout
);
626 set(STRstatus
, Strsave(STR0
));
630 set(STRstatus
, Strsave(tellmewhat(lexw
, NULL
) ? STR0
: STR1
));
636 tellmewhat(struct wordent
*lexp
, Char
*str
)
638 struct biltins
*bptr
;
640 Char
*cmd
, *s0
, *s1
, *s2
;
648 if (adrof1(sp
->word
, &aliases
)) {
654 s0
= sp
->word
; /* to get the memory freeing right... */
656 /* handle quoted alias hack */
657 if ((*(sp
->word
) & (QUOTE
| TRIM
)) == QUOTE
)
660 /* do quoting, if it hasn't been done */
667 while (*s2
&& *s2
!= qc
)
668 *s1
++ = *s2
++ | QUOTE
;
674 *s1
++ = *s2
++ | QUOTE
;
681 for (bptr
= bfunc
; bptr
< &bfunc
[nbfunc
]; bptr
++) {
682 if (eq(sp
->word
, str2short(bptr
->bname
))) {
686 (void)fprintf(cshout
, "%s: shell built-in command.\n",
690 (void)Strcpy(str
, sp
->word
);
691 sp
->word
= s0
; /* we save and then restore this */
696 sp
->word
= cmd
= globone(sp
->word
, G_IGNORE
);
698 if ((i
= iscommand(sp
->word
)) != 0) {
701 int slash
= any(short2str(sp
->word
), '/');
704 if (v
== 0 || v
->vec
[0] == 0 || slash
)
711 if (pv
[0][0] == 0 || eq(pv
[0], STRdot
)) {
713 sp
->word
= Strspl(STRdotsl
, sp
->word
);
715 xfree((ptr_t
) sp
->word
);
721 s1
= Strspl(*pv
, STRslash
);
722 sp
->word
= Strspl(s1
, sp
->word
);
727 (void)Strcpy(str
, sp
->word
);
728 xfree((ptr_t
) sp
->word
);
736 (void)fprintf(csherr
,
737 "%s: Command not found.\n", vis_str(sp
->word
));
740 (void)Strcpy(str
, sp
->word
);
743 sp
->word
= s0
; /* we save and then restore this */