-.\" $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
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
+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
-Execute is synchronous, and the program will block until the external
-command being run has finished. Please note that lengthy program
+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.
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
#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"
#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,
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:
int indent;
{
struct executable_statement *r, *x;
+ struct expression *expr;
int result;
int status;
const char *s, *t, *dot;
"", "", ");");
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);
break;
case log_statement:
case return_statement:
+ case execute_statement:
break;
}
}
#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"
{
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;
}
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);
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);
#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"
return rv;
}
break;
+
case expr_function:
rv = 9;
if (len > rv + 1) {
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 "
#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"
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)
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;
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:
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:
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:
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:
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 ||
expr -> op == expr_extract_int8 ||
expr -> op == expr_extract_int16 ||
expr -> op == expr_extract_int32 ||
- expr -> op == expr_execute ||
expr -> op == expr_dns_transaction);
}
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:
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:
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",
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:
let_statement,
define_statement,
log_statement,
- return_statement
+ return_statement,
+ execute_statement
} op;
union {
struct {
} priority;
struct expression *expr;
} log;
+ struct {
+ char *command;
+ struct expression *arglist;
+ int argc;
+ } execute;
} data;
};
expr_extract_int8,
expr_extract_int16,
expr_extract_int32,
- expr_execute,
expr_encode_int8,
expr_encode_int16,
expr_encode_int32,
char *name;
struct expression *arglist;
} funcall;
- struct {
- char *command;
- struct expression *arglist;
- int argc;
- } execute;
struct fundef *func;
} data;
int flags;