diff -Naur dhcp-4.1.0rc1-orig/common/execute.c dhcp-4.1.0rc1/common/execute.c --- dhcp-4.1.0rc1-orig/common/execute.c 2008-02-28 18:21:55.000000000 -0300 +++ dhcp-4.1.0rc1/common/execute.c 2008-12-02 01:16:10.000000000 -0300 @@ -36,6 +36,7 @@ #include #include #include +#include int execute_statements (result, packet, lease, client_state, in_options, out_options, scope, statements) @@ -233,15 +234,34 @@ } argv[i] = NULL; + int pfds[2]; + char retbuffer[5]; + int retval,n; + pipe(pfds); + for(n=0; n<5; n++) { + retbuffer[n]=0; + } + for(n=0; n<2; n++) { + int f = fcntl(pfds[n], F_GETFL, 0); + f |= O_NONBLOCK; + fcntl(pfds[n], F_SETFL, f); + } if ((p = fork()) > 0) { int status; waitpid(p, &status, 0); - + read(pfds[0], retbuffer, 5); + close(pfds[0]); + close(pfds[1]); + if (sscanf(retbuffer, "%d", &retval) == 0 || retval > 65535 || retval < 0) { retval = 0; } + log_debug("execute_statement returned value %d", retval); if (status) { log_error("execute: %s exit status %d", argv[0], status); } } else if (p == 0) { + close(1); /* close normal stdout */ + dup(pfds[1]); /* make stdout same as pfds[1] */ + close(pfds[0]); /* we don't need this */ execvp(argv[0], argv); log_error("Unable to execute %s: %m", argv[0]); _exit(127); diff -Naur dhcp-4.1.0rc1-orig/common/parse.c dhcp-4.1.0rc1/common/parse.c --- dhcp-4.1.0rc1-orig/common/parse.c 2008-10-08 13:26:42.000000000 -0300 +++ dhcp-4.1.0rc1/common/parse.c 2008-12-06 21:16:28.000000000 -0300 @@ -3969,6 +3969,77 @@ goto norparen; break; + case EXECUTE: +#ifdef ENABLE_EXECUTE + token = next_token(&val, NULL, cfile); + + if (!expression_allocate (expr, MDL)) + log_fatal ("no memory for execute statement."); + (*expr)->op = expr_execute; + + 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; + } + + (*expr)->data.execute.command = dmalloc(len + 1, MDL); + if ((*expr)->data.execute.command == NULL) + log_fatal("can't allocate command name"); + strcpy((*expr)->data.execute.command, val); + + ep = &(*expr)->data.execute.arglist; + (*expr)->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; + (*expr)->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; + expression_dereference (expr, 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; + /* NOT EXISTS is special cased above... */ not_exists: token = peek_token (&val, (unsigned *)0, cfile); diff -Naur dhcp-4.1.0rc1-orig/common/print.c dhcp-4.1.0rc1/common/print.c --- dhcp-4.1.0rc1-orig/common/print.c 2007-10-01 11:47:35.000000000 -0300 +++ dhcp-4.1.0rc1/common/print.c 2008-12-05 01:20:06.000000000 -0300 @@ -456,6 +456,7 @@ { unsigned rv, left; const char *s; + struct expression *next_arg; switch (expr -> op) { case expr_none: @@ -1062,6 +1063,38 @@ 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 " "%d).", MDL, expr->op); diff -Naur dhcp-4.1.0rc1-orig/common/tree.c dhcp-4.1.0rc1/common/tree.c --- dhcp-4.1.0rc1-orig/common/tree.c 2008-08-29 14:48:57.000000000 -0300 +++ dhcp-4.1.0rc1/common/tree.c 2008-12-06 21:35:46.000000000 -0300 @@ -53,6 +53,114 @@ #define DS_SPRINTF_SIZE 128 + +int execute_evaluate (result, packet, lease, client_state, + in_options, out_options, scope, expr) + struct binding_value **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *out_options; + struct binding_scope **scope; + struct expression *expr; +{ + + int status; + struct data_string ds; + struct expression *next_expr; + + char **argv; + int i, argc = expr->data.execute.argc; + pid_t p; + + /* save room for the command and the NULL terminator */ + argv = dmalloc((argc + 2) * sizeof(*argv), MDL); + if (!argv) + return 0; + + argv[0] = dmalloc(strlen(expr->data.execute.command) + 1, + MDL); + if (argv[0]) { + strcpy(argv[0], expr->data.execute.command); + } else { + goto execute_out; + } + + log_debug("execute_statement argv[0] = %s", argv[0]); + + for (i = 1, next_expr = expr->data.execute.arglist; next_expr; + next_expr = next_expr->data.arg.next, i++) { + memset (&ds, 0, sizeof(ds)); + status = (evaluate_data_expression + (&ds, packet, + lease, client_state, in_options, + out_options, scope, + next_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; + + int pfds[2]; + char retbuffer[5]; + int retval,n; + pipe(pfds); + for(n=0; n<5; n++) { + retbuffer[n]=0; + } + for(n=0; n<2; n++) { + int f = fcntl(pfds[n], F_GETFL, 0); + f |= O_NONBLOCK; + fcntl(pfds[n], F_SETFL, f); + } + if ((p = fork()) > 0) { + int status; + waitpid(p, &status, 0); + read(pfds[0], retbuffer, 5); + close(pfds[0]); + close(pfds[1]); + if (sscanf(retbuffer, "%d", &retval) == 0 || retval > 65535 || retval < 0) { retval = 0; } + if (status) { + log_error("execute: %s exit status %d", + argv[0], status); + } + } else if (p == 0) { + close(1); /* close normal stdout */ + dup(pfds[1]); /* make stdout same as pfds[1] */ + close(pfds[0]); /* we don't need this */ + 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); + return (retval); + } /* * If we are using a data_string structure to hold a NUL-terminated * ASCII string, this function can be used to append a printf-formatted @@ -958,6 +1066,7 @@ 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: @@ -1383,6 +1492,7 @@ 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: @@ -2320,6 +2430,7 @@ 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: @@ -2832,6 +2943,21 @@ #endif return 0; } + case expr_execute: +#if defined (ENABLE_EXECUTE) + status = execute_evaluate(result, packet, lease, + client_state, in_options, + cfg_options, scope, expr); +# if defined (DEBUG_EXPRESSIONS) + log_debug("num: execute() -> %d", status); +# endif + *result = status; + return 1; +#else + log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE " + "is not defined).", MDL); +#endif + return 0; case expr_ns_add: case expr_ns_delete: @@ -3190,6 +3316,14 @@ fundef_dereference (&expr -> data.func, file, line); break; + case expr_execute: + if (expr->data.execute.command) + dfree (expr->data.execute.command, file, line); + if (expr->data.execute.arglist) + expression_dereference (&expr-> data.execute.arglist, + file, line); + break; + /* No subexpressions. */ case expr_leased_address: case expr_lease_time: @@ -3270,6 +3404,7 @@ 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 || @@ -3305,6 +3440,7 @@ expr -> op == expr_extract_int8 || expr -> op == expr_extract_int16 || expr -> op == expr_extract_int32 || + expr -> op == expr_execute || expr -> op == expr_dns_transaction); } @@ -3333,6 +3469,7 @@ 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: @@ -3431,6 +3568,7 @@ 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: @@ -3497,6 +3635,7 @@ char obuf [65]; int scol; int width; + struct expression *next_arg; /* If this promises to be a fat expression, start a new line. */ if (!firstp && is_compound_expression (expr)) { @@ -3988,6 +4127,30 @@ 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", expr -> op); @@ -4213,6 +4376,7 @@ 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 -Naur dhcp-4.1.0rc1-orig/includes/tree.h dhcp-4.1.0rc1/includes/tree.h --- dhcp-4.1.0rc1-orig/includes/tree.h 2008-01-23 23:43:05.000000000 -0300 +++ dhcp-4.1.0rc1/includes/tree.h 2008-12-06 21:31:13.000000000 -0300 @@ -156,6 +156,7 @@ expr_extract_int8, expr_extract_int16, expr_extract_int32, + expr_execute, expr_encode_int8, expr_encode_int16, expr_encode_int32, @@ -279,6 +280,11 @@ char *name; struct expression *arglist; } funcall; + struct { + char *command; + struct expression *arglist; + int argc; + } execute; struct fundef *func; } data; int flags;