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,
