| 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<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 |
| +append_to_ary (&arg_ary, &arg_ary_size, &arg_ary_capacity, NULL); |
| +*result = execute (arg_ary); |
| +for (i=1; i<arg_ary_size-1; i++) |
| +dfree (arg_ary[i], MDL); |
| +dfree(arg_ary, MDL); |
| +return 1; |
| +} |
| +#endif |
| + |
| + |
| pair cons (car, cdr) |
| caddr_t car; |
| pair cdr; |
| @@ -861,6 +968,9 @@ |
| case expr_extract_int8: |
| case expr_extract_int16: |
| case expr_extract_int32: |
| + #ifdef ENABLE_EXECUTE |
| + case expr_execute: |
| + #endif |
| case expr_const_int: |
| case expr_lease_time: |
| case expr_dns_transaction: |
| @@ -1224,6 +1334,9 @@ |
| case expr_extract_int8: |
| case expr_extract_int16: |
| case expr_extract_int32: |
| + #ifdef ENABLE_EXECUTE |
| + case expr_execute: |
| + #endif |
| case expr_const_int: |
| case expr_lease_time: |
| case expr_dns_transaction: |
| @@ -2087,6 +2200,9 @@ |
| case expr_extract_int8: |
| case expr_extract_int16: |
| case expr_extract_int32: |
| + #ifdef ENABLE_EXECUTE |
| + case expr_execute: |
| + #endif |
| case expr_const_int: |
| case expr_lease_time: |
| case expr_dns_transaction: |
| @@ -2595,7 +2711,12 @@ |
| #endif |
| return 0; |
| } |
| - |
| +#ifdef ENABLE_EXECUTE |
| + case expr_execute: |
| + return evaluate_execute (result, packet, lease, |
| + client_state, in_options, |
| + cfg_options, scope, expr); |
| +#endif |
| case expr_ns_add: |
| case expr_ns_delete: |
| case expr_ns_exists: |
| @@ -3008,6 +3129,9 @@ |
| return (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_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 @@ |
| DOMAIN_NAME = 613, |
| DO_FORWARD_UPDATE = 614, |
| KNOWN_CLIENTS = 615, |
| - ATSFP = 616 |
| + ATSFP = 616, |
| + 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, |