From: Evan Hunt Date: Sun, 28 Jan 2007 23:00:19 +0000 (+0000) Subject: Change "execute" from numeric expression to executable statement, so X-Git-Tag: v4_0_0a1~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=253c8b6ad1f0696cf631331e2ce3a6da341c240e;p=thirdparty%2Fdhcp.git Change "execute" from numeric expression to executable statement, so it will not be necessary to use eval(execute(...)) [rt16620] --- diff --git a/common/dhcp-eval.5 b/common/dhcp-eval.5 index 4aeb88376..f2b30f3bd 100644 --- a/common/dhcp-eval.5 +++ b/common/dhcp-eval.5 @@ -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 +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. @@ -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 diff --git a/common/execute.c b/common/execute.c index a5fc03a48..efe31ff02 100644 --- a/common/execute.c +++ b/common/execute.c @@ -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; } } diff --git a/common/parse.c b/common/parse.c index 5c7bf71f2..486d0cd2c 100644 --- a/common/parse.c +++ b/common/parse.c @@ -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); diff --git a/common/print.c b/common/print.c index 4438fbe54..f068ecdfc 100644 --- a/common/print.c +++ b/common/print.c @@ -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 " diff --git a/common/tree.c b/common/tree.c index 9965bf44a..f3d8d3e3d 100644 --- a/common/tree.c +++ b/common/tree.c @@ -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: diff --git a/includes/statement.h b/includes/statement.h index 44fe8a848..27c758f47 100644 --- a/includes/statement.h +++ b/includes/statement.h @@ -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; }; diff --git a/includes/tree.h b/includes/tree.h index 1a3988228..e70a61230 100644 --- a/includes/tree.h +++ b/includes/tree.h @@ -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;