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 */
}
SHELLPROC {
x = 3; /* executed when the shell runs a shell procedure */
}
It pulls this code out into routines which are when particular
events occur. The intent is to improve modularity by isolating
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,
so I implement it using setjmp and longjmp. The global variable
exception contains the type of exception. EXERROR is raised by
calling error. EXINT is an interrupt. EXSHELLPROC is an excep-
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.
calling error. EXINT is an interrupt.
INTERRUPTS: In an interactive shell, an interrupt will cause an
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
the variable table as the simplest way to strip duplicates, and
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-
tered throughout the code, depending on which location appears

View File

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

View File

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

View File

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

View File

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

View File

@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <paths.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
* have to change the find_command routine as well.
*
* The argv array may be changed and element argv[-1] should be writable.
*/
void
@ -147,12 +150,9 @@ tryexec(char *cmd, char **argv, char **envp)
execve(cmd, argv, envp);
e = errno;
if (e == ENOEXEC) {
initshellproc();
setinputfile(cmd, 0);
commandname = arg0 = savestr(argv[0]);
setparam(argv + 1);
exraise(EXSHELLPROC);
/*NOTREACHED*/
*argv = cmd;
*--argv = _PATH_BSHELL;
execve(_PATH_BSHELL, argv, envp);
}
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,
* 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 *);
void hashcd(void);
void changepath(const char *);
void deletefuncs(void);
void addcmdentry(const char *, struct cmdentry *);
void defun(const char *, union node *);
int unsetfunc(const char *);

View File

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

View File

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

View File

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

View File

@ -98,19 +98,7 @@ main(int argc, char *argv[])
(void) setlocale(LC_ALL, "");
state = 0;
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) {
case EXSHELLPROC:
rootpid = getpid();
rootshell = 1;
minusc = NULL;
state = 3;
break;
case EXEXEC:
exitstatus = exerrno;
break;
@ -123,10 +111,8 @@ main(int argc, char *argv[])
break;
}
if (exception != EXSHELLPROC) {
if (state == 0 || iflag == 0 || ! rootshell)
exitshell(exitstatus);
}
if (state == 0 || iflag == 0 || ! rootshell)
exitshell(exitstatus);
reset();
if (exception == EXINT)
out2fmt_flush("\n");

View File

@ -126,16 +126,10 @@ char reset[] = "\
* interactive shell and control is returned to the main command loop.\n\
*/\n";
char shellproc[] = "\
/*\n\
* This routine is called to initialize the shell to run a shell procedure.\n\
*/\n";
struct event event[] = {
{ "INIT", "init", init, { 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 } }
};

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.
*/

View File

@ -324,10 +324,6 @@ RESET {
popredir();
}
SHELLPROC {
clearredir();
}
#endif
/* 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
.\" $FreeBSD$
.\"
.Dd January 16, 2011
.Dd February 4, 2011
.Dt SH 1
.Os
.Sh NAME
@ -647,15 +647,9 @@ resulting in an
.Er ENOEXEC
return value from
.Xr execve 2 )
the shell will interpret the program in a subshell.
The child shell will reinitialize itself in this case,
so that the effect will be
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).
the shell will run a new instance of
.Nm
to interpret it.
.Pp
Note that previous versions of this document
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.
*/

View File

@ -161,7 +161,7 @@ INIT {
/*
* 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
@ -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
var_compare(const void *a, const void *b)
{