2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-09-03 15:56:00 +00:00

Change "execute" from numeric expression to executable statement, so

it will not be necessary to use eval(execute(...)) [rt16620]
This commit is contained in:
Evan Hunt
2007-01-28 23:00:19 +00:00
parent 85edef5cfe
commit 253c8b6ad1
7 changed files with 217 additions and 279 deletions

View File

@@ -1,4 +1,4 @@
.\" $Id: dhcp-eval.5,v 1.23 2006/07/31 22:19:51 dhankins Exp $
.\" $Id: dhcp-eval.5,v 1.24 2007/01/28 23:00:19 each Exp $
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1996-2003 by Internet Software Consortium
@@ -432,34 +432,31 @@ Rebind - DHCP client is in the REBINDING state - it has an IP address,
and is trying to contact any server to renew it. The next message to
be sent will be a DHCPREQUEST, which will be broadcast.
.RE
.SH REFERENCE: ACTION EXPRESSIONS
.PP
.B execute(\fIcommand-path\fB, \fIdata-expr1\fB ... \fIdata-exprN\fB);\fR
.B log (\fIpriority\fB, \fIdata-expr\fB)\fR
.RS 0.25i
.PP
External command execution is made possible through \fBexecute();\fR
expressions. These expressions take a variable number of arguments, where
the first is the command name (full path or only the name of the executable)
and is followed by zero or more are data-expressions whose values will be
evaluated and passed as external arguments (assumed to be text strings
suitable for use as a command-line argument). It returns the numeric return
code of the external command, or one of the following special values:
.TP 2
.I \(bu
125: Invalid arguments.
.TP
.I \(bu
126: fork() failure
.TP
.I \(bu
127: execvp() failure
.TP
.I \(bu
-SIGNAL: Should the child exit due to a signal, rather than exiting normally
with an exit status, the signal number multiplied by negative 1 will be
returned.
Logging statements may be used to send information to the standard logging
channels. A logging statement includes an optional priority (\fBfatal\fR,
\fBerror\fR, \fBinfo\fR, or \fBdebug\fR), and a data expression.
.PP
Execute is synchronous, and the program will block until the external
command being run has finished. Please note that lengthy program
Logging statements take only a single data expression argument, so if you
want to output multiple data values, you will need to use the \fBconcat\fR
operator to concatenate them.
.RE
.PP
.B execute (\fIcommand-path\fB [, \fIdata-expr1\fB, ... \fIdata-exprN\fB]);\fR
.RS 0.25i
.PP
The \fBexecute\fR statement runs an external command. The first argument
is a string literal containing the name or path of the command to run.
The other arguments, if present, are either string literals or data-
expressions which evaluate to text strings, to be passed as command-line
arguments to the command.
.PP
\fBexecute\fR is synchronous; the program will block until the external
command being run has finished. Please note that lengthy program
execution (for example, in an "on commit" in dhcpd.conf) may result in
bad performance and timeouts. Only external applications with very short
execution times are suitable for use.
@@ -470,21 +467,10 @@ Non-printable ASCII characters will be converted into dhcpd.conf language
octal escapes ("\777"), make sure your external command handles them as
such.
.PP
It is possible to use the execute expression in any context, not only
It is possible to use the execute statement in any context, not only
on events. If you put it in a regular scope in the configuration file
you will execute that command every time a scope is evaluated.
.RE
.SH REFERENCE: LOGGING
Logging statements may be used to send information to the standard logging
channels. A logging statement includes an optional priority (\fBfatal\fR,
\fBerror\fR, \fBinfo\fR, or \fBdebug\fR), and a data expression.
.PP
.B log (\fIpriority\fB, \fIdata-expr\fB)\fR
.PP
Logging statements take only a single data expression argument, so if you
want to output multiple data values, you will need to use the \fBconcat\fR
operator to concatenate them.
.RE
.SH REFERENCE: DYNAMIC DNS UPDATES
.PP
The DHCP client and server have the ability to dynamically update the

View File

@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
"$Id: execute.c,v 1.48 2006/02/24 23:16:28 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
"$Id: execute.c,v 1.49 2007/01/28 23:00:19 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -188,6 +188,86 @@ int execute_statements (result, packet, lease, client_state,
#endif
break;
case execute_statement: {
#ifdef ENABLE_EXECUTE
struct expression *expr;
char **argv;
int i, argc = r->data.execute.argc;
pid_t p;
/* save room for the command and the NULL terminator */
argv = dmalloc((argc + 2) * sizeof(*argv), MDL);
if (!argv)
break;
argv[0] = dmalloc(strlen(r->data.execute.command) + 1,
MDL);
if (argv[0]) {
strcpy(argv[0], r->data.execute.command);
} else {
goto execute_out;
}
log_debug("execute_statement argv[0] = %s", argv[0]);
for (i = 1, expr = r->data.execute.arglist; expr;
expr = expr->data.arg.next, i++) {
memset (&ds, 0, sizeof(ds));
status = (evaluate_data_expression
(&ds, packet,
lease, client_state, in_options,
out_options, scope,
expr->data.arg.val, MDL));
if (status) {
argv[i] = dmalloc(ds.len + 1, MDL);
if (argv[i]) {
memcpy(argv[i], ds.data,
ds.len);
argv[i][ds.len] = 0;
log_debug("execute_statement argv[%d] = %s", i, argv[i]);
}
data_string_forget (&ds, MDL);
if (!argv[i]) {
log_debug("execute_statement failed argv[%d]", i);
goto execute_out;
}
} else {
log_debug("execute: bad arg %d", i);
goto execute_out;
}
}
argv[i] = NULL;
if ((p = fork()) > 0) {
int status;
waitpid(p, &status, 0);
if (status) {
log_error("execute: %s exit status %d",
argv[0], status);
}
} else if (p == 0) {
execvp(argv[0], argv);
log_error("Unable to execute %s: %m", argv[0]);
_exit(127);
} else {
log_error("execute: fork() failed");
}
execute_out:
for (i = 0; i <= argc; i++) {
if(argv[i])
dfree(argv[i], MDL);
}
dfree(argv, MDL);
#else /* !ENABLE_EXECUTE */
log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE "
"is not defined).", MDL);
#endif /* ENABLE_EXECUTE */
break;
}
case return_statement:
status = evaluate_expression
(result, packet,
@@ -624,6 +704,14 @@ int executable_statement_dereference (ptr, file, line)
dfree ((*ptr)->data.unset, file, line);
break;
case execute_statement:
if ((*ptr)->data.execute.command)
dfree ((*ptr)->data.execute.command, file, line);
if ((*ptr)->data.execute.arglist)
expression_dereference (&(*ptr) -> data.execute.arglist,
file, line);
break;
case supersede_option_statement:
case send_option_statement:
case default_option_statement:
@@ -650,6 +738,7 @@ void write_statements (file, statements, indent)
int indent;
{
struct executable_statement *r, *x;
struct expression *expr;
int result;
int status;
const char *s, *t, *dot;
@@ -882,6 +971,25 @@ void write_statements (file, statements, indent)
"", "", ");");
break;
case execute_statement:
#ifdef ENABLE_EXECUTE
indent_spaces (file, indent);
col = token_print_indent(file, col, indent + 4, "", "",
"execute");
col = token_print_indent(file, col, indent + 4, " ", "",
"(");
col = token_print_indent(file, col, indent + 4, "\"", "\"", r->data.execute.command);
for (expr = r->data.execute.arglist; expr; expr = expr->data.arg.next) {
col = token_print_indent(file, col, indent + 4, "", " ", ",");
col = write_expression (file, expr->data.arg.val, col, indent + 4, 0);
}
col = token_print_indent(file, col, indent + 4, "", "", ");");
#else /* !ENABLE_EXECUTE */
log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE "
"is not defined).", MDL);
#endif /* ENABLE_EXECUTE */
break;
default:
log_fatal ("bogus statement type %d\n", r -> op);
@@ -1047,6 +1155,7 @@ int executable_statement_foreach (struct executable_statement *stmt,
break;
case log_statement:
case return_statement:
case execute_statement:
break;
}
}

View File

@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
"$Id: parse.c,v 1.117 2006/08/04 10:59:33 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
"$Id: parse.c,v 1.118 2007/01/28 23:00:19 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -1806,10 +1806,12 @@ int parse_executable_statement (result, cfile, lose, case_context)
{
enum dhcp_token token;
const char *val;
unsigned len;
struct executable_statement base;
struct class *cta;
struct option *option=NULL;
struct option_cache *cache;
struct expression **ep;
int known;
int flag;
int i;
@@ -2190,6 +2192,77 @@ int parse_executable_statement (result, cfile, lose, case_context)
}
break;
case EXECUTE:
#ifdef ENABLE_EXECUTE
token = next_token(&val, NULL, cfile);
if (!executable_statement_allocate (result, MDL))
log_fatal ("no memory for execute statement.");
(*result)->op = execute_statement;
token = next_token(&val, NULL, cfile);
if (token != LPAREN) {
parse_warn(cfile, "left parenthesis expected.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
token = next_token(&val, &len, cfile);
if (token != STRING) {
parse_warn(cfile, "Expecting a quoted string.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
(*result)->data.execute.command = dmalloc(len + 1, MDL);
if ((*result)->data.execute.command == NULL)
log_fatal("can't allocate command name");
strcpy((*result)->data.execute.command, val);
ep = &(*result)->data.execute.arglist;
(*result)->data.execute.argc = 0;
while((token = next_token(&val, NULL, cfile)) == COMMA) {
if (!expression_allocate(ep, MDL))
log_fatal ("can't allocate expression");
if (!parse_data_expression (&(*ep) -> data.arg.val,
cfile, lose)) {
if (!*lose) {
parse_warn (cfile,
"expecting expression.");
*lose = 1;
}
skip_to_semi(cfile);
*lose = 1;
return 0;
}
ep = &(*ep)->data.arg.next;
(*result)->data.execute.argc++;
}
if (token != RPAREN) {
parse_warn(cfile, "right parenthesis expected.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
if (!parse_semi (cfile)) {
*lose = 1;
executable_statement_dereference (result, MDL);
}
#else /* ! ENABLE_EXECUTE */
parse_warn(cfile, "define ENABLE_EXECUTE in site.h to "
"enable execute(); expressions.");
skip_to_semi(cfile);
*lose = 1;
return 0;
#endif /* ENABLE_EXECUTE */
break;
case RETURN:
token = next_token (&val, (unsigned *)0, cfile);
@@ -3684,71 +3757,6 @@ int parse_non_binary (expr, cfile, lose, context)
goto norparen;
break;
#ifdef ENABLE_EXECUTE
case EXECUTE:
token = next_token(&val, NULL, cfile);
if (!expression_allocate(expr, MDL))
log_fatal("can't allocate expression.");
token = next_token(&val, NULL, cfile);
if (token != LPAREN) {
parse_warn(cfile, "left parenthesis expected.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
token = next_token(&val, NULL, cfile);
if (token != STRING) {
parse_warn(cfile, "Expecting a quoted string.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
(*expr)->data.execute.command = dmalloc(strlen(val) + 1, MDL);
if ((*expr)->data.execute.command == NULL)
log_fatal("can't allocate command name");
strcpy((*expr)->data.execute.command, val);
token = next_token(&val, NULL, cfile);
ep = &(*expr)->data.execute.arglist;
i = 0;
while (token == COMMA) {
if (!expression_allocate(ep, MDL))
log_fatal ("can't allocate expression");
if (!parse_data_expression(&(*ep)->data.arg.val,
cfile, lose)) {
skip_to_semi(cfile);
*lose = 1;
return 0;
}
ep = &(*ep)->data.arg.next;
token = next_token(&val, NULL, cfile);
i++;
}
(*expr)->data.execute.argc = i;
(*expr)->op = expr_execute;
if (token != RPAREN) {
parse_warn(cfile, "right parenthesis expected.");
skip_to_semi(cfile);
*lose = 1;
return 0;
}
break;
#else
case EXECUTE:
parse_warn(cfile, "define ENABLE_EXECUTE in site.h to "
"enable execute(); expressions.");
skip_to_semi(cfile);
*lose = 1;
return 0;
break;
#endif
/* NOT EXISTS is special cased above... */
not_exists:
token = peek_token (&val, (unsigned *)0, cfile);

View File

@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
"$Id: print.c,v 1.61 2006/07/31 22:19:51 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
"$Id: print.c,v 1.62 2007/01/28 23:00:19 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -1034,6 +1034,7 @@ static unsigned print_subexpression (expr, buf, len)
return rv;
}
break;
case expr_function:
rv = 9;
if (len > rv + 1) {
@@ -1051,37 +1052,6 @@ static unsigned print_subexpression (expr, buf, len)
buf [rv] = 0;
return rv;
}
case expr_execute:
#ifdef ENABLE_EXECUTE
rv = 11 + strlen(expr->data.execute.command);
if (len > rv + 2) {
sprintf(buf, "(execute \"%s\"",
expr->data.execute.command);
for(next_arg = expr->data.execute.arglist;
next_arg;
next_arg = next_arg->data.arg.next) {
if (len <= rv + 3)
return 0;
buf[rv++] = ' ';
rv += print_subexpression(next_arg->
data.arg.val,
buf + rv,
len - rv - 2);
}
if (len <= rv + 2)
return 0;
buf[rv++] = ')';
buf[rv] = 0;
return rv;
}
#else
log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE is not "
"defined.", MDL);
#endif
break;
default:
log_fatal("Impossible case at %s:%d (undefined expression "

View File

@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
"$Id: tree.c,v 1.110 2006/11/06 18:13:31 shane Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
"$Id: tree.c,v 1.111 2007/01/28 23:00:19 each Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -51,35 +51,6 @@ struct __res_state resolver_state;
int resolver_inited = 0;
#endif
#ifdef ENABLE_EXECUTE
static unsigned long
execute(char **args)
{
pid_t p;
if (args == NULL || args[0] == NULL)
return 125;
p = fork();
if (p > 0) {
int status;
waitpid(p, &status, 0);
if (WIFEXITED(status))
return WEXITSTATUS(status);
else
return -WTERMSIG(status);
} else if (p == 0) {
execvp(args[0], args);
log_error("Unable to execute %s: %m", args[0]);
_exit(127);
}
return 126;
}
static void
append_to_ary(char **ary_ptr, int *ary_size, int ary_capacity,
char *new_element)
@@ -135,66 +106,6 @@ data_string_to_char_string(struct data_string *d)
return str;
}
static int
evaluate_execute(unsigned long *result, struct packet *packet,
struct lease *lease, struct client_state *client_state,
struct option_state *in_options,
struct option_state *cfg_options,
struct binding_scope **scope, struct expression *expr)
{
int status;
int cmd_status;
int i;
struct data_string ds;
struct expression *next_arg;
char **arg_ary = NULL;
int arg_ary_size = 0;
int arg_ary_capacity = 0;
/* Need 1 bucket for the command, and 1 for the trailing NULL
* terminator.
*/
i = expr->data.execute.argc + 2;
arg_ary = dmalloc(i * sizeof(char *), MDL);
/* Leave one bucket free for the NULL terminator. */
arg_ary_capacity = i - 1;
if (arg_ary == NULL)
return 0;
append_to_ary(arg_ary, &arg_ary_size, arg_ary_capacity,
expr->data.execute.command);
for(next_arg = expr->data.execute.arglist;
next_arg;
next_arg = next_arg->data.arg.next) {
memset(&ds, 0, sizeof ds);
status = (evaluate_data_expression
(&ds, packet, lease, client_state, in_options,
cfg_options, scope, next_arg->data.arg.val, MDL));
if (!status) {
if (arg_ary) {
for (i=1; i < arg_ary_size; i++)
dfree(arg_ary[i], MDL);
dfree(arg_ary, MDL);
}
return 0;
}
append_to_ary(arg_ary, &arg_ary_size, arg_ary_capacity,
data_string_to_char_string(&ds));
data_string_forget(&ds, MDL);
}
# if defined (DEBUG_EXPRESSIONS)
log_debug("exec: execute");
# endif
*result = execute(arg_ary);
for (i=1; i < arg_ary_size; i++)
dfree(arg_ary[i], MDL);
dfree(arg_ary, MDL);
return 1;
}
#endif
pair cons (car, cdr)
caddr_t car;
pair cdr;
@@ -1008,7 +919,6 @@ int evaluate_dns_expression (result, packet, lease, client_state, in_options,
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_const_int:
case expr_lease_time:
case expr_dns_transaction:
@@ -1374,7 +1284,6 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_const_int:
case expr_lease_time:
case expr_dns_transaction:
@@ -2311,7 +2220,6 @@ int evaluate_data_expression (result, packet, lease, client_state,
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_const_int:
case expr_lease_time:
case expr_dns_transaction:
@@ -2823,20 +2731,6 @@ int evaluate_numeric_expression (result, packet, lease, client_state,
return 0;
}
case expr_execute:
#if defined (ENABLE_EXECUTE)
status = evaluate_execute(result, packet, lease,
client_state, in_options,
cfg_options, scope, expr);
# if defined (DEBUG_EXPRESSIONS)
log_debug("num: execute() -> %d", status);
# endif
return status;
#else
log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE "
"is not defined).", MDL);
#endif
break;
case expr_ns_add:
case expr_ns_delete:
case expr_ns_exists:
@@ -3266,7 +3160,6 @@ int is_numeric_expression (expr)
return (expr -> op == expr_extract_int8 ||
expr -> op == expr_extract_int16 ||
expr -> op == expr_extract_int32 ||
expr -> op == expr_execute ||
expr -> op == expr_const_int ||
expr -> op == expr_lease_time ||
expr -> op == expr_dns_transaction ||
@@ -3302,7 +3195,6 @@ int is_compound_expression (expr)
expr -> op == expr_extract_int8 ||
expr -> op == expr_extract_int16 ||
expr -> op == expr_extract_int32 ||
expr -> op == expr_execute ||
expr -> op == expr_dns_transaction);
}
@@ -3331,7 +3223,6 @@ static int op_val (op)
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:
@@ -3430,7 +3321,6 @@ enum expression_context op_context (op)
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:
@@ -3978,30 +3868,6 @@ int write_expression (file, expr, col, indent, firstp)
expr -> data.variable);
col = token_print_indent (file, col, indent, "", "", ")");
break;
case expr_execute:
#if defined(ENABLE_EXECUTE)
col = token_print_indent(file, col, indent, "", "",
"execute");
col = token_print_indent(file, col, indent, " ", "",
"(");
scol = col;
col = token_print_indent_concat(file, col, scol, "", "", "\"",
expr->data.execute.command,
"\"", NULL);
for(next_arg = expr->data.execute.arglist;
next_arg;
next_arg = next_arg->data.arg.next) {
col = token_print_indent(file, col, scol, "", " ",
",");
col = write_expression(file, next_arg->data.arg.val,
col, scol, 0);
}
col = token_print_indent(file, col, indent, "", "", ")");
#else
log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE is not "
"defined.", MDL);
#endif
break;
default:
log_fatal ("invalid expression type in print_expression: %d",
@@ -4227,7 +4093,6 @@ int data_subexpression_length (int *rv,
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_execute:
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:

View File

@@ -56,7 +56,8 @@ struct executable_statement {
let_statement,
define_statement,
log_statement,
return_statement
return_statement,
execute_statement
} op;
union {
struct {
@@ -99,6 +100,11 @@ struct executable_statement {
} priority;
struct expression *expr;
} log;
struct {
char *command;
struct expression *arglist;
int argc;
} execute;
} data;
};

View File

@@ -153,7 +153,6 @@ enum expr_op {
expr_extract_int8,
expr_extract_int16,
expr_extract_int32,
expr_execute,
expr_encode_int8,
expr_encode_int16,
expr_encode_int32,
@@ -275,11 +274,6 @@ struct expression {
char *name;
struct expression *arglist;
} funcall;
struct {
char *command;
struct expression *arglist;
int argc;
} execute;
struct fundef *func;
} data;
int flags;