sh: Remove special code for shell scripts without magic number.

These are called "shell procedures" in the source.

If execve() failed with [ENOEXEC], the shell would reinitialize itself
and execute the program as a script. This requires a fair amount of code
which is not frequently used (most scripts have a #! magic number).
Therefore just execute a new instance of sh (_PATH_BSHELL) to run the
script.
This commit is contained in:
Jilles Tjoelker 2011-02-04 22:47:55 +00:00
parent 9f2e8bdff3
commit 3835f47c7e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=218306
17 changed files with 27 additions and 224 deletions

View File

@ -44,10 +44,6 @@ C source files for entries looking like:
back to the main command loop */ back to the main command loop */
} }
SHELLPROC {
x = 3; /* executed when the shell runs a shell procedure */
}
It pulls this code out into routines which are when particular It pulls this code out into routines which are when particular
events occur. The intent is to improve modularity by isolating events occur. The intent is to improve modularity by isolating
the information about which modules need to be explicitly the information about which modules need to be explicitly
@ -80,12 +76,7 @@ EXCEPTIONS: Code for dealing with exceptions appears in
exceptions.c. The C language doesn't include exception handling, exceptions.c. The C language doesn't include exception handling,
so I implement it using setjmp and longjmp. The global variable so I implement it using setjmp and longjmp. The global variable
exception contains the type of exception. EXERROR is raised by exception contains the type of exception. EXERROR is raised by
calling error. EXINT is an interrupt. EXSHELLPROC is an excep- calling error. EXINT is an interrupt.
tion which is raised when a shell procedure is invoked. The pur-
pose of EXSHELLPROC is to perform the cleanup actions associated
with other exceptions. After these cleanup actions, the shell
can interpret a shell procedure itself without exec'ing a new
copy of the shell.
INTERRUPTS: In an interactive shell, an interrupt will cause an INTERRUPTS: In an interactive shell, an interrupt will cause an
EXINT exception to return to the main command loop. (Exception: EXINT exception to return to the main command loop. (Exception:
@ -270,14 +261,6 @@ When a program is run, the code in eval.c sticks any environment
variables which precede the command (as in "PATH=xxx command") in variables which precede the command (as in "PATH=xxx command") in
the variable table as the simplest way to strip duplicates, and the variable table as the simplest way to strip duplicates, and
then calls "environment" to get the value of the environment. then calls "environment" to get the value of the environment.
There are two consequences of this. First, if an assignment to
PATH precedes the command, the value of PATH before the assign-
ment must be remembered and passed to shellexec. Second, if the
program turns out to be a shell procedure, the strings from the
environment variables which preceded the command must be pulled
out of the table and replaced with strings obtained from malloc,
since the former will automatically be freed when the stack (see
the entry on memalloc.c) is emptied.
BUILTIN COMMANDS: The procedures for handling these are scat- BUILTIN COMMANDS: The procedures for handling these are scat-
tered throughout the code, depending on which location appears tered throughout the code, depending on which location appears

View File

@ -145,15 +145,7 @@ unalias(const char *name)
return (1); return (1);
} }
#ifdef mkinit static void
MKINIT void rmaliases(void);
SHELLPROC {
rmaliases();
}
#endif
void
rmaliases(void) rmaliases(void)
{ {
struct alias *ap, *tmp; struct alias *ap, *tmp;

View File

@ -45,4 +45,3 @@ struct alias {
struct alias *lookupalias(const char *, int); struct alias *lookupalias(const char *, int);
int aliascmd(int, char **); int aliascmd(int, char **);
int unaliascmd(int, char **); int unaliascmd(int, char **);
void rmaliases(void);

View File

@ -56,8 +56,7 @@ extern volatile sig_atomic_t exception;
/* exceptions */ /* exceptions */
#define EXINT 0 /* SIGINT received */ #define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */ #define EXERROR 1 /* a generic error */
#define EXSHELLPROC 2 /* execute a shell procedure */ #define EXEXEC 2 /* command execution failed */
#define EXEXEC 3 /* command execution failed */
/* /*

View File

@ -111,10 +111,6 @@ RESET {
loopnest = 0; loopnest = 0;
funcnest = 0; funcnest = 0;
} }
SHELLPROC {
exitstatus = 0;
}
#endif #endif
@ -732,7 +728,9 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
argc = 0; argc = 0;
for (sp = arglist.list ; sp ; sp = sp->next) for (sp = arglist.list ; sp ; sp = sp->next)
argc++; argc++;
argv = stalloc(sizeof (char *) * (argc + 1)); /* Add one slot at the beginning for tryexec(). */
argv = stalloc(sizeof (char *) * (argc + 2));
argv++;
for (sp = arglist.list ; sp ; sp = sp->next) { for (sp = arglist.list ; sp ; sp = sp->next) {
TRACE(("evalcommand arg: %s\n", sp->text)); TRACE(("evalcommand arg: %s\n", sp->text));
@ -927,14 +925,10 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
reffunc(cmdentry.u.func); reffunc(cmdentry.u.func);
savehandler = handler; savehandler = handler;
if (setjmp(jmploc.loc)) { if (setjmp(jmploc.loc)) {
if (exception == EXSHELLPROC) freeparam(&shellparam);
freeparam(&saveparam); shellparam = saveparam;
else { if (exception == EXERROR || exception == EXEXEC)
freeparam(&shellparam); popredir();
shellparam = saveparam;
if (exception == EXERROR || exception == EXEXEC)
popredir();
}
unreffunc(cmdentry.u.func); unreffunc(cmdentry.u.func);
poplocalvars(); poplocalvars();
localvars = savelocalvars; localvars = savelocalvars;
@ -1016,11 +1010,9 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
out2 = &errout; out2 = &errout;
freestdout(); freestdout();
handler = savehandler; handler = savehandler;
if (e != EXSHELLPROC) { commandname = savecmdname;
commandname = savecmdname; if (jp)
if (jp) exitshell(exitstatus);
exitshell(exitstatus);
}
if (flags == EV_BACKCMD) { if (flags == EV_BACKCMD) {
backcmd->buf = memout.buf; backcmd->buf = memout.buf;
backcmd->nleft = memout.nextc - memout.buf; backcmd->nleft = memout.nextc - memout.buf;

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <paths.h>
#include <stdlib.h> #include <stdlib.h>
/* /*
@ -105,6 +106,8 @@ static void delete_cmd_entry(void);
/* /*
* Exec a program. Never returns. If you change this routine, you may * Exec a program. Never returns. If you change this routine, you may
* have to change the find_command routine as well. * have to change the find_command routine as well.
*
* The argv array may be changed and element argv[-1] should be writable.
*/ */
void void
@ -147,12 +150,9 @@ tryexec(char *cmd, char **argv, char **envp)
execve(cmd, argv, envp); execve(cmd, argv, envp);
e = errno; e = errno;
if (e == ENOEXEC) { if (e == ENOEXEC) {
initshellproc(); *argv = cmd;
setinputfile(cmd, 0); *--argv = _PATH_BSHELL;
commandname = arg0 = savestr(argv[0]); execve(_PATH_BSHELL, argv, envp);
setparam(argv + 1);
exraise(EXSHELLPROC);
/*NOTREACHED*/
} }
errno = e; errno = e;
} }
@ -536,43 +536,6 @@ clearcmdentry(int firstchange)
} }
/*
* Delete all functions.
*/
#ifdef mkinit
MKINIT void deletefuncs(void);
SHELLPROC {
deletefuncs();
}
#endif
void
deletefuncs(void)
{
struct tblentry **tblp;
struct tblentry **pp;
struct tblentry *cmdp;
INTOFF;
for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
pp = tblp;
while ((cmdp = *pp) != NULL) {
if (cmdp->cmdtype == CMDFUNCTION) {
*pp = cmdp->next;
unreffunc(cmdp->param.func);
ckfree(cmdp);
} else {
pp = &cmdp->next;
}
}
}
INTON;
}
/* /*
* Locate a command in the command hash table. If "add" is nonzero, * Locate a command in the command hash table. If "add" is nonzero,
* add the command to the table if it is not already present. The * add the command to the table if it is not already present. The

View File

@ -71,7 +71,6 @@ void find_command(const char *, struct cmdentry *, int, const char *);
int find_builtin(const char *, int *); int find_builtin(const char *, int *);
void hashcd(void); void hashcd(void);
void changepath(const char *); void changepath(const char *);
void deletefuncs(void);
void addcmdentry(const char *, struct cmdentry *); void addcmdentry(const char *, struct cmdentry *);
void defun(const char *, union node *); void defun(const char *, union node *);
int unsetfunc(const char *); int unsetfunc(const char *);

View File

@ -35,4 +35,3 @@
void init(void); void init(void);
void reset(void); void reset(void);
void initshellproc(void);

View File

@ -119,12 +119,7 @@ INIT {
RESET { RESET {
popallfiles(); popallfiles();
if (exception != EXSHELLPROC) parselleft = parsenleft = 0; /* clear input buffer */
parselleft = parsenleft = 0; /* clear input buffer */
}
SHELLPROC {
popallfiles();
} }
#endif #endif

View File

@ -177,22 +177,6 @@ out: out2fmt_flush("sh: can't access tty; job control turned off\n");
#endif #endif
#ifdef mkinit
INCLUDE <sys/types.h>
INCLUDE <stdlib.h>
SHELLPROC {
backgndpid = -1;
bgjob = NULL;
#if JOBS
jobctl = 0;
#endif
}
#endif
#if JOBS #if JOBS
int int
fgcmd(int argc __unused, char **argv) fgcmd(int argc __unused, char **argv)

View File

@ -98,19 +98,7 @@ main(int argc, char *argv[])
(void) setlocale(LC_ALL, ""); (void) setlocale(LC_ALL, "");
state = 0; state = 0;
if (setjmp(main_handler.loc)) { if (setjmp(main_handler.loc)) {
/*
* When a shell procedure is executed, we raise the
* exception EXSHELLPROC to clean up before executing
* the shell procedure.
*/
switch (exception) { switch (exception) {
case EXSHELLPROC:
rootpid = getpid();
rootshell = 1;
minusc = NULL;
state = 3;
break;
case EXEXEC: case EXEXEC:
exitstatus = exerrno; exitstatus = exerrno;
break; break;
@ -123,10 +111,8 @@ main(int argc, char *argv[])
break; break;
} }
if (exception != EXSHELLPROC) { if (state == 0 || iflag == 0 || ! rootshell)
if (state == 0 || iflag == 0 || ! rootshell) exitshell(exitstatus);
exitshell(exitstatus);
}
reset(); reset();
if (exception == EXINT) if (exception == EXINT)
out2fmt_flush("\n"); out2fmt_flush("\n");

View File

@ -126,16 +126,10 @@ char reset[] = "\
* interactive shell and control is returned to the main command loop.\n\ * interactive shell and control is returned to the main command loop.\n\
*/\n"; */\n";
char shellproc[] = "\
/*\n\
* This routine is called to initialize the shell to run a shell procedure.\n\
*/\n";
struct event event[] = { struct event event[] = {
{ "INIT", "init", init, { NULL, 0, NULL, NULL } }, { "INIT", "init", init, { NULL, 0, NULL, NULL } },
{ "RESET", "reset", reset, { NULL, 0, NULL, NULL } }, { "RESET", "reset", reset, { NULL, 0, NULL, NULL } },
{ "SHELLPROC", "initshellproc", shellproc, { NULL, 0, NULL, NULL } },
{ NULL, NULL, NULL, { NULL, 0, NULL, NULL } } { NULL, NULL, NULL, { NULL, 0, NULL, NULL } }
}; };

View File

@ -304,21 +304,6 @@ setoption(int flag, int val)
} }
#ifdef mkinit
INCLUDE "options.h"
SHELLPROC {
int i;
for (i = 0; i < NOPTS; i++)
optlist[i].val = 0;
optschanged();
}
#endif
/* /*
* Set the shell parameters. * Set the shell parameters.
*/ */

View File

@ -324,10 +324,6 @@ RESET {
popredir(); popredir();
} }
SHELLPROC {
clearredir();
}
#endif #endif
/* Return true if fd 0 has already been redirected at least once. */ /* Return true if fd 0 has already been redirected at least once. */

View File

@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 .\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd January 16, 2011 .Dd February 4, 2011
.Dt SH 1 .Dt SH 1
.Os .Os
.Sh NAME .Sh NAME
@ -647,15 +647,9 @@ resulting in an
.Er ENOEXEC .Er ENOEXEC
return value from return value from
.Xr execve 2 ) .Xr execve 2 )
the shell will interpret the program in a subshell. the shell will run a new instance of
The child shell will reinitialize itself in this case, .Nm
so that the effect will be to interpret it.
as if a new shell had been invoked to handle the ad-hoc shell script,
except that the location of hashed commands located in
the parent shell will be remembered by the child
(see the description of the
.Ic hash
built-in command below).
.Pp .Pp
Note that previous versions of this document Note that previous versions of this document
and the source code itself misleadingly and sporadically and the source code itself misleadingly and sporadically

View File

@ -367,22 +367,6 @@ ignoresig(int signo)
} }
#ifdef mkinit
INCLUDE <signal.h>
INCLUDE "trap.h"
SHELLPROC {
char *sm;
clear_traps();
for (sm = sigmode ; sm < sigmode + NSIG ; sm++) {
if (*sm == S_IGN)
*sm = S_HARD_IGN;
}
}
#endif
/* /*
* Signal handler. * Signal handler.
*/ */

View File

@ -161,7 +161,7 @@ INIT {
/* /*
* This routine initializes the builtin variables. It is called when the * This routine initializes the builtin variables. It is called when the
* shell is initialized and again when a shell procedure is spawned. * shell is initialized.
*/ */
void void
@ -542,47 +542,6 @@ environment(void)
} }
/*
* Called when a shell procedure is invoked to clear out nonexported
* variables. It is also necessary to reallocate variables of with
* VSTACK set since these are currently allocated on the stack.
*/
MKINIT void shprocvar(void);
#ifdef mkinit
SHELLPROC {
shprocvar();
}
#endif
void
shprocvar(void)
{
struct var **vpp;
struct var *vp, **prev;
for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
for (prev = vpp ; (vp = *prev) != NULL ; ) {
if ((vp->flags & VEXPORT) == 0) {
*prev = vp->next;
if ((vp->flags & VTEXTFIXED) == 0)
ckfree(vp->text);
if ((vp->flags & VSTRFIXED) == 0)
ckfree(vp);
} else {
if (vp->flags & VSTACK) {
vp->text = savestr(vp->text);
vp->flags &=~ VSTACK;
}
prev = &vp->next;
}
}
}
initvar();
}
static int static int
var_compare(const void *a, const void *b) var_compare(const void *a, const void *b)
{ {