diff -urN dhcp-3.0.2.orig/common/conflex.c dhcp-3.0.2/common/conflex.c --- dhcp-3.0.2.orig/common/conflex.c 2004-11-24 10:39:15.000000000 -0700 +++ dhcp-3.0.2/common/conflex.c 2005-02-24 12:32:12.000000000 -0700 @@ -676,6 +676,8 @@ return EVAL; if (!strcasecmp (atom + 1, "ncapsulate")) return ENCAPSULATE; + if (!strcasecmp (atom + 1, "xecute")) + return EXECUTE; break; case 'f': if (!strcasecmp (atom + 1, "atal")) diff -urN dhcp-3.0.2.orig/common/dhcp-eval.5 dhcp-3.0.2/common/dhcp-eval.5 --- dhcp-3.0.2.orig/common/dhcp-eval.5 2005-01-19 13:00:52.000000000 -0700 +++ dhcp-3.0.2/common/dhcp-eval.5 2005-02-24 12:32:12.000000000 -0700 @@ -409,7 +409,32 @@ 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 +.PP +.B execute (\fIcommand-path\fB, \fIdata-expr1\fB ... \fIdata-exprN\fB)\fR +.PP +External command execution is possibly through execute expressions. Execute +takes a variable number of arguments, where the first is the command +name (full path or only the name of the executable) and following zero +or more are data-expressions which values will be passed as external +arguments. It returns the return code of the external command. +.PP +Execute is synchronous, and 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 the dhcpd) may result in +bad performance and timed out clients. Try keeping the execution times +short. +.PP +Passing user-supplied data might be dangerous. Check input buffers +and make sure the external command handles all kinds of "unusual" +characters (shell special characters in sh-scripts etc) correctly. +.PP +It is possible to use the execute expression 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. +.PP +The execute expression is only available if you have defined ENABLE_EXECUTE +in site.h before compilation. +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, diff -urN dhcp-3.0.2.orig/common/parse.c dhcp-3.0.2/common/parse.c --- dhcp-3.0.2.orig/common/parse.c 2004-09-30 14:38:31.000000000 -0600 +++ dhcp-3.0.2/common/parse.c 2005-02-24 12:32:12.000000000 -0700 @@ -3639,7 +3639,56 @@ return 0; } break; - + #ifdef ENABLE_EXECUTE + case EXECUTE: + token = next_token (&val, (unsigned *)0, cfile); + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression."); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + (*expr) -> data.funcall.name = + dmalloc (strlen (val) + 1, MDL); + if (!(*expr)->data.funcall.name) + log_fatal ("can't allocate command name"); + strcpy ((*expr) -> data.funcall.name, val); + token = next_token (&val, (unsigned *)0, cfile); + ep = &((*expr) -> data.funcall.arglist); + 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, (unsigned *)0, cfile); + } + (*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 case ENCODE_INT: token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); diff -urN dhcp-3.0.2.orig/common/print.c dhcp-3.0.2/common/print.c --- dhcp-3.0.2.orig/common/print.c 2004-06-17 14:54:39.000000000 -0600 +++ dhcp-3.0.2/common/print.c 2005-02-24 12:32:12.000000000 -0700 @@ -459,6 +459,7 @@ { unsigned rv, left; const char *s; + struct expression* next_arg; switch (expr -> op) { case expr_none: @@ -483,7 +484,8 @@ return rv; } break; - + + case expr_equal: if (len > 6) { rv = 4; @@ -1024,6 +1026,29 @@ buf [rv++] = 0; return rv; } + #ifdef ENABLE_EXECUTE + case expr_execute: + rv = 11 + strlen (expr -> data.funcall.name); + if (len > rv + 2) { + sprintf (buf, + "(execute \"%s\"", + expr -> data.funcall.name); + for(next_arg = expr -> data.funcall.arglist; + next_arg; + next_arg = next_arg -> data.arg.next) { + if (len > rv + 3) + buf [rv++] = ' '; + rv += print_subexpression (next_arg -> + data.arg.val, + buf + rv, + len - rv - 2); + } + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + #endif } return 0; } diff -urN dhcp-3.0.2.orig/common/tree.c dhcp-3.0.2/common/tree.c --- dhcp-3.0.2.orig/common/tree.c 2004-11-24 10:39:16.000000000 -0700 +++ dhcp-3.0.2/common/tree.c 2005-02-24 12:32:12.000000000 -0700 @@ -50,6 +50,113 @@ int resolver_inited = 0; #endif +#ifdef ENABLE_EXECUTE +static unsigned long execute (char** args) +{ +pid_t p = fork(); +if (p > 0) { +int status; +waitpid (p, &status, 0); +return WEXITSTATUS(status); +} +else if(p == 0) { +execvp (args[0], args); +log_error ("Unable to execute %s: %s", args[0], +strerror(errno)); +_exit(127); +} else { +log_fatal ("unable to fork"); +} +return 1; /* never reached */ +} + +#define CAPACITY_INCREMENT 8 +static void append_to_ary (char*** ary_ptr, int* ary_size, int* ary_capacity, +char* new_element) +{ +(*ary_size)++; +if (*ary_size > *ary_capacity) { +char** new_ary; +int new_ary_capacity = *ary_capacity + CAPACITY_INCREMENT; +new_ary = dmalloc(new_ary_capacity*sizeof(char *), MDL); +if (!new_ary) +log_fatal ("no memory for array."); +if (*ary_ptr != NULL) { +memcpy (new_ary, *ary_ptr, +(*ary_capacity)*sizeof(char *)); +dfree (*ary_ptr, MDL); +} +*ary_ptr = new_ary; +*ary_capacity = new_ary_capacity; +} +(*ary_ptr)[*ary_size-1] = new_element; +} + +static char* data_string_to_char_string (struct data_string* d) +{ +char* str = dmalloc (d->len+1, MDL); +if (!str) +log_fatal ("no memory for string."); +/* FIXME: should one use d -> buffer -> data or d -> data? are +they equivalent? */ +strncpy (str, d -> data, d -> len); +str[d->len] = '\0'; +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; +append_to_ary (&arg_ary, &arg_ary_size, &arg_ary_capacity, + expr -> data.funcall.name); +for(next_arg = expr -> data.funcall.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 op == expr_extract_int8 || expr -> op == expr_extract_int16 || expr -> op == expr_extract_int32 || + #ifdef ENABLE_EXECUTE + expr -> op == expr_execute || + #endif expr -> op == expr_const_int || expr -> op == expr_lease_time || expr -> op == expr_dns_transaction || @@ -3043,6 +3167,9 @@ expr -> op == expr_extract_int8 || expr -> op == expr_extract_int16 || expr -> op == expr_extract_int32 || + #ifdef ENABLE_EXECUTE + expr -> op == expr_execute || + #endif expr -> op == expr_dns_transaction); } @@ -3069,6 +3196,9 @@ case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: + #ifdef ENABLE_EXECUTE + case expr_execute: + #endif case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: @@ -3165,6 +3295,9 @@ case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: + #ifdef ENABLE_EXECUTE + case expr_execute: + #endif case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: @@ -3225,6 +3358,8 @@ int firstp; { struct expression *e; + struct expression* next_arg; + const char *s; char obuf [65]; int scol; @@ -3696,7 +3831,27 @@ expr -> data.variable); col = token_print_indent (file, col, indent, "", "", ")"); break; - + #ifdef ENABLE_EXECUTE + case expr_execute: + col = token_print_indent (file, col, indent, "", "","execute"); + col = token_print_indent (file, col, indent, " ", "","("); + scol = col; + /* FIXME: use token_print_indent_concat() here? */ + col = token_print_indent (file, col, scol, "", "","\""); + col = token_print_indent (file, col, scol, "", "",expr -> data.funcall.name); + col = token_print_indent (file, col, scol, "", "","\""); + for(next_arg = expr -> data.funcall.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, "", "",")"); + + break; +#endif default: log_fatal ("invalid expression type in print_expression: %d", expr -> op); @@ -3915,6 +4070,9 @@ case expr_extract_int8: case expr_extract_int16: case expr_extract_int32: + #ifdef ENABLE_EXECUTE + case expr_execute: + #endif case expr_encode_int8: case expr_encode_int16: case expr_encode_int32: diff -urN dhcp-3.0.2.orig/includes/dhctoken.h dhcp-3.0.2/includes/dhctoken.h --- dhcp-3.0.2.orig/includes/dhctoken.h 2004-09-21 13:25:38.000000000 -0600 +++ dhcp-3.0.2/includes/dhctoken.h 2005-02-24 12:33:21.000000000 -0700 @@ -308,7 +308,8 @@ REFRESH = 612, DOMAIN_NAME = 613, DO_FORWARD_UPDATE = 614, - KNOWN_CLIENTS = 615 + KNOWN_CLIENTS = 615, + EXECUTE = 616 }; #define is_identifier(x) ((x) >= FIRST_TOKEN && \ diff -urN dhcp-3.0.2.orig/includes/site.h dhcp-3.0.2/includes/site.h --- dhcp-3.0.2.orig/includes/site.h 2002-03-12 11:33:39.000000000 -0700 +++ dhcp-3.0.2/includes/site.h 2005-02-24 12:32:12.000000000 -0700 @@ -167,6 +167,12 @@ /* #define DHCPD_LOG_FACILITY LOG_DAEMON */ +/* Define this if you want to be able to execute external commands + during conditional evaluation. */ + +/* #define ENABLE_EXECUTE */ + + /* Define this if you aren't debugging and you want to save memory (potentially a _lot_ of memory) by allocating leases in chunks rather than one at a time. */ diff -urN dhcp-3.0.2.orig/includes/tree.h dhcp-3.0.2/includes/tree.h --- dhcp-3.0.2.orig/includes/tree.h 2004-06-10 11:59:31.000000000 -0600 +++ dhcp-3.0.2/includes/tree.h 2005-02-24 12:32:12.000000000 -0700 @@ -150,6 +150,9 @@ expr_hardware, expr_packet, expr_const_data, + #ifdef ENABLE_EXECUTE + expr_execute, + #endif expr_extract_int8, expr_extract_int16, expr_extract_int32,