mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
sh: Improve the command builtin:
* avoid unnecessary fork * allow executing builtins via command * executing a special builtin via command removes its special properties Obtained from: NetBSD (parts)
This commit is contained in:
parent
44d68185d1
commit
c848bc18e8
7 changed files with 244 additions and 43 deletions
|
|
@ -597,6 +597,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||
char *lastarg;
|
||||
int realstatus;
|
||||
int do_clearcmdentry;
|
||||
char *path = pathval();
|
||||
|
||||
/* First expand the arguments. */
|
||||
TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags));
|
||||
|
|
@ -682,7 +683,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||
cmdentry.special = 1;
|
||||
} else {
|
||||
static const char PATH[] = "PATH=";
|
||||
char *path = pathval();
|
||||
int cmd_flags = 0, bltinonly = 0;
|
||||
|
||||
/*
|
||||
* Modify the command lookup path, if a PATH= assignment
|
||||
|
|
@ -713,24 +714,68 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||
do_clearcmdentry = 1;
|
||||
}
|
||||
|
||||
find_command(argv[0], &cmdentry, 0, path);
|
||||
/* implement the bltin builtin here */
|
||||
if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
|
||||
for (;;) {
|
||||
argv++;
|
||||
if (--argc == 0)
|
||||
break;
|
||||
if ((cmdentry.u.index = find_builtin(*argv,
|
||||
&cmdentry.special)) < 0) {
|
||||
for (;;) {
|
||||
if (bltinonly) {
|
||||
cmdentry.u.index = find_builtin(*argv, &cmdentry.special);
|
||||
if (cmdentry.u.index < 0) {
|
||||
cmdentry.u.index = BLTINCMD;
|
||||
argv--;
|
||||
argc++;
|
||||
break;
|
||||
}
|
||||
if (cmdentry.u.index != BLTINCMD)
|
||||
} else
|
||||
find_command(argv[0], &cmdentry, cmd_flags, path);
|
||||
/* implement the bltin and command builtins here */
|
||||
if (cmdentry.cmdtype != CMDBUILTIN)
|
||||
break;
|
||||
if (cmdentry.u.index == BLTINCMD) {
|
||||
if (argc == 1)
|
||||
break;
|
||||
}
|
||||
argv++;
|
||||
argc--;
|
||||
bltinonly = 1;
|
||||
} else if (cmdentry.u.index == COMMANDCMD) {
|
||||
if (argc == 1)
|
||||
break;
|
||||
if (!strcmp(argv[1], "-p")) {
|
||||
if (argc == 2)
|
||||
break;
|
||||
if (argv[2][0] == '-') {
|
||||
if (strcmp(argv[2], "--"))
|
||||
break;
|
||||
if (argc == 3)
|
||||
break;
|
||||
argv += 3;
|
||||
argc -= 3;
|
||||
} else {
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
}
|
||||
path = _PATH_STDPATH;
|
||||
clearcmdentry(0);
|
||||
do_clearcmdentry = 1;
|
||||
} else if (!strcmp(argv[1], "--")) {
|
||||
if (argc == 2)
|
||||
break;
|
||||
argv += 2;
|
||||
argc -= 2;
|
||||
} else if (argv[1][0] == '-')
|
||||
break;
|
||||
else {
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
cmd_flags |= DO_NOFUNC;
|
||||
bltinonly = 0;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Special builtins lose their special properties when
|
||||
* called via 'command'.
|
||||
*/
|
||||
if (cmd_flags & DO_NOFUNC)
|
||||
cmdentry.special = 0;
|
||||
}
|
||||
|
||||
/* Fork off a child process if necessary. */
|
||||
|
|
@ -741,9 +786,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
|||
&& (cmdentry.cmdtype != CMDBUILTIN
|
||||
|| cmdentry.u.index == CDCMD
|
||||
|| cmdentry.u.index == DOTCMD
|
||||
|| cmdentry.u.index == EVALCMD))
|
||||
|| (cmdentry.cmdtype == CMDBUILTIN &&
|
||||
cmdentry.u.index == COMMANDCMD)) {
|
||||
|| cmdentry.u.index == EVALCMD))) {
|
||||
jp = makejob(cmd, 1);
|
||||
mode = cmd->ncmd.backgnd;
|
||||
if (flags & EV_BACKCMD) {
|
||||
|
|
@ -889,7 +932,7 @@ cmddone:
|
|||
for (sp = varlist.list ; sp ; sp = sp->next)
|
||||
setvareq(sp->text, VEXPORT|VSTACK);
|
||||
envp = environment();
|
||||
shellexec(argv, envp, pathval(), cmdentry.u.index);
|
||||
shellexec(argv, envp, path, cmdentry.u.index);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
goto out;
|
||||
|
|
@ -996,15 +1039,11 @@ int
|
|||
commandcmd(int argc, char **argv)
|
||||
{
|
||||
static char stdpath[] = _PATH_STDPATH;
|
||||
struct jmploc loc, *old;
|
||||
struct strlist *sp;
|
||||
char *path;
|
||||
int ch;
|
||||
int cmd = -1;
|
||||
|
||||
for (sp = cmdenviron; sp ; sp = sp->next)
|
||||
setvareq(sp->text, VEXPORT|VSTACK);
|
||||
path = pathval();
|
||||
path = bltinlookup("PATH", 1);
|
||||
|
||||
optind = optreset = 1;
|
||||
opterr = 0;
|
||||
|
|
@ -1032,22 +1071,14 @@ commandcmd(int argc, char **argv)
|
|||
error("wrong number of arguments");
|
||||
return typecmd_impl(2, argv - 1, cmd, path);
|
||||
}
|
||||
if (argc != 0) {
|
||||
old = handler;
|
||||
handler = &loc;
|
||||
if (setjmp(handler->loc) == 0)
|
||||
shellexec(argv, environment(), path, 0);
|
||||
handler = old;
|
||||
if (exception == EXEXEC)
|
||||
exit(exerrno);
|
||||
exraise(exception);
|
||||
}
|
||||
if (argc != 0)
|
||||
error("commandcmd() called while it should not be");
|
||||
|
||||
/*
|
||||
* Do nothing successfully if no command was specified;
|
||||
* ksh also does this.
|
||||
*/
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ hashcmd(int argc __unused, char **argv __unused)
|
|||
&& (cmdp->cmdtype == CMDNORMAL
|
||||
|| (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
|
||||
delete_cmd_entry();
|
||||
find_command(name, &entry, 1, pathval());
|
||||
find_command(name, &entry, DO_ERR, pathval());
|
||||
if (verbose) {
|
||||
if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */
|
||||
cmdp = cmdlookup(name, 0);
|
||||
|
|
@ -310,10 +310,10 @@ printentry(struct tblentry *cmdp, int verbose)
|
|||
*/
|
||||
|
||||
void
|
||||
find_command(const char *name, struct cmdentry *entry, int printerr,
|
||||
find_command(const char *name, struct cmdentry *entry, int act,
|
||||
const char *path)
|
||||
{
|
||||
struct tblentry *cmdp;
|
||||
struct tblentry *cmdp, loc_cmd;
|
||||
int idx;
|
||||
int prev;
|
||||
char *fullname;
|
||||
|
|
@ -330,13 +330,19 @@ find_command(const char *name, struct cmdentry *entry, int printerr,
|
|||
}
|
||||
|
||||
/* If name is in the table, and not invalidated by cd, we're done */
|
||||
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
|
||||
goto success;
|
||||
if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
|
||||
if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC)
|
||||
cmdp = NULL;
|
||||
else
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* If %builtin not in path, check for builtin next */
|
||||
if (builtinloc < 0 && (i = find_builtin(name, &spec)) >= 0) {
|
||||
INTOFF;
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION)
|
||||
cmdp = &loc_cmd;
|
||||
cmdp->cmdtype = CMDBUILTIN;
|
||||
cmdp->param.index = i;
|
||||
cmdp->special = spec;
|
||||
|
|
@ -365,6 +371,8 @@ loop:
|
|||
goto loop;
|
||||
INTOFF;
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION)
|
||||
cmdp = &loc_cmd;
|
||||
cmdp->cmdtype = CMDBUILTIN;
|
||||
cmdp->param.index = i;
|
||||
cmdp->special = spec;
|
||||
|
|
@ -414,6 +422,8 @@ loop:
|
|||
TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
|
||||
INTOFF;
|
||||
cmdp = cmdlookup(name, 1);
|
||||
if (cmdp->cmdtype == CMDFUNCTION)
|
||||
cmdp = &loc_cmd;
|
||||
cmdp->cmdtype = CMDNORMAL;
|
||||
cmdp->param.index = idx;
|
||||
INTON;
|
||||
|
|
@ -421,9 +431,9 @@ loop:
|
|||
}
|
||||
|
||||
/* We failed. If there was an entry for this command, delete it */
|
||||
if (cmdp)
|
||||
if (cmdp && cmdp->cmdtype != CMDFUNCTION)
|
||||
delete_cmd_entry();
|
||||
if (printerr) {
|
||||
if (act & DO_ERR) {
|
||||
if (e == ENOENT || e == ENOTDIR)
|
||||
outfmt(out2, "%s: not found\n", name);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ struct cmdentry {
|
|||
};
|
||||
|
||||
|
||||
/* action to find_command() */
|
||||
#define DO_ERR 0x01 /* prints errors */
|
||||
#define DO_NOFUNC 0x02 /* don't return shell functions, for command */
|
||||
|
||||
extern const char *pathopt; /* set by padvance */
|
||||
extern int exerrno; /* last exec error */
|
||||
|
||||
|
|
|
|||
10
bin/sh/sh.1
10
bin/sh/sh.1
|
|
@ -32,7 +32,7 @@
|
|||
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 31, 2009
|
||||
.Dd March 6, 2010
|
||||
.Dt SH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
|
@ -1571,10 +1571,12 @@ built-in command.
|
|||
.It Ic command Oo Fl p Oc Op Ar utility Op Ar argument ...
|
||||
.It Ic command Oo Fl v | V Oc Op Ar utility
|
||||
The first form of invocation executes the specified
|
||||
.Ar utility ,
|
||||
ignoring shell functions in the search.
|
||||
If
|
||||
.Ar utility
|
||||
as a simple command (see the
|
||||
.Sx Simple Commands
|
||||
section).
|
||||
is a special builtin,
|
||||
it is executed as if it were a regular builtin.
|
||||
.Pp
|
||||
If the
|
||||
.Fl p
|
||||
|
|
|
|||
45
tools/regression/bin/sh/builtins/command8.0
Normal file
45
tools/regression/bin/sh/builtins/command8.0
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# $FreeBSD$
|
||||
IFS=,
|
||||
|
||||
SPECIAL="break,\
|
||||
:,\
|
||||
continue,\
|
||||
. /dev/null,\
|
||||
eval,\
|
||||
exec,\
|
||||
export -p,\
|
||||
readonly -p,\
|
||||
set,\
|
||||
shift 0,\
|
||||
times,\
|
||||
trap,\
|
||||
unset foo"
|
||||
|
||||
set -e
|
||||
|
||||
# Check that special builtins can be executed via "command".
|
||||
|
||||
set -- ${SPECIAL}
|
||||
for cmd in "$@"
|
||||
do
|
||||
sh -c "v=:; while \$v; do v=false; command ${cmd}; done" >/dev/null
|
||||
done
|
||||
|
||||
while :; do
|
||||
command break
|
||||
echo Error on line $LINENO
|
||||
done
|
||||
|
||||
set p q r
|
||||
command shift 2
|
||||
if [ $# -ne 1 ]; then
|
||||
echo Error on line $LINENO
|
||||
fi
|
||||
|
||||
(
|
||||
command exec >/dev/null
|
||||
echo Error on line $LINENO
|
||||
)
|
||||
|
||||
set +e
|
||||
! command shift 2 2>/dev/null
|
||||
55
tools/regression/bin/sh/builtins/var-assign2.0
Normal file
55
tools/regression/bin/sh/builtins/var-assign2.0
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# $FreeBSD$
|
||||
IFS=,
|
||||
|
||||
SPECIAL="break,\
|
||||
:,\
|
||||
continue,\
|
||||
. /dev/null,\
|
||||
eval,\
|
||||
exec,\
|
||||
export -p,\
|
||||
readonly -p,\
|
||||
set,\
|
||||
shift 0,\
|
||||
times,\
|
||||
trap,\
|
||||
unset foo"
|
||||
|
||||
UTILS="alias,\
|
||||
bg,\
|
||||
bind,\
|
||||
cd,\
|
||||
command echo,\
|
||||
echo,\
|
||||
false,\
|
||||
fc -l,\
|
||||
fg,\
|
||||
getopts a var,\
|
||||
hash,\
|
||||
jobs,\
|
||||
printf a,\
|
||||
pwd,\
|
||||
read var < /dev/null,\
|
||||
test,\
|
||||
true,\
|
||||
type ls,\
|
||||
ulimit,\
|
||||
umask,\
|
||||
unalias -a,\
|
||||
wait"
|
||||
|
||||
set -e
|
||||
|
||||
# With 'command', variable assignments affect the shell environment.
|
||||
|
||||
set -- ${SPECIAL}
|
||||
for cmd in "$@"
|
||||
do
|
||||
sh -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
set -- ${UTILS}
|
||||
for cmd in "$@"
|
||||
do
|
||||
sh -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1
|
||||
done
|
||||
54
tools/regression/bin/sh/errors/redirection-error3.0
Normal file
54
tools/regression/bin/sh/errors/redirection-error3.0
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# $FreeBSD$
|
||||
IFS=,
|
||||
|
||||
SPECIAL="break,\
|
||||
:,\
|
||||
continue,\
|
||||
. /dev/null,\
|
||||
eval,\
|
||||
exec,\
|
||||
export -p,\
|
||||
readonly -p,\
|
||||
set,\
|
||||
shift,\
|
||||
times,\
|
||||
trap,\
|
||||
unset foo"
|
||||
|
||||
UTILS="alias,\
|
||||
bg,\
|
||||
bind,\
|
||||
cd,\
|
||||
command echo,\
|
||||
echo,\
|
||||
false,\
|
||||
fc -l,\
|
||||
fg,\
|
||||
getopts a -a,\
|
||||
hash,\
|
||||
jobs,\
|
||||
printf a,\
|
||||
pwd,\
|
||||
read var < /dev/null,\
|
||||
test,\
|
||||
true,\
|
||||
type ls,\
|
||||
ulimit,\
|
||||
umask,\
|
||||
unalias -a,\
|
||||
wait"
|
||||
|
||||
# When used with 'command', neither special built-in utilities nor other
|
||||
# utilities must abort on a redirection error.
|
||||
|
||||
set -- ${SPECIAL}
|
||||
for cmd in "$@"
|
||||
do
|
||||
sh -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
|
||||
done
|
||||
|
||||
set -- ${UTILS}
|
||||
for cmd in "$@"
|
||||
do
|
||||
sh -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1
|
||||
done
|
||||
Loading…
Reference in a new issue