aboutsummaryrefslogtreecommitdiff
path: root/common/execute.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/execute.c')
-rw-r--r--common/execute.c1152
1 files changed, 1152 insertions, 0 deletions
diff --git a/common/execute.c b/common/execute.c
new file mode 100644
index 0000000..9d08207
--- /dev/null
+++ b/common/execute.c
@@ -0,0 +1,1152 @@
+/* execute.c
+
+ Support for executable statements. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int execute_statements (result, packet, lease, client_state,
+ in_options, out_options, scope, statements)
+ 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 executable_statement *statements;
+{
+ struct executable_statement *r, *e, *next;
+ int rc;
+ int status;
+ struct binding *binding;
+ struct data_string ds;
+ struct binding_scope *ns;
+
+ if (!statements)
+ return 1;
+
+ r = (struct executable_statement *)0;
+ next = (struct executable_statement *)0;
+ e = (struct executable_statement *)0;
+ executable_statement_reference (&r, statements, MDL);
+ while (r && !(result && *result)) {
+ if (r -> next)
+ executable_statement_reference (&next, r -> next, MDL);
+ switch (r -> op) {
+ case statements_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: statements");
+#endif
+ status = execute_statements (result, packet, lease,
+ client_state, in_options,
+ out_options, scope,
+ r -> data.statements);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: statements returns %d", status);
+#endif
+ if (!status)
+ return 0;
+ break;
+
+ case on_statement:
+ if (lease) {
+ if (r -> data.on.evtypes & ON_EXPIRY) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on expiry");
+#endif
+ if (lease -> on_expiry)
+ executable_statement_dereference
+ (&lease -> on_expiry, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_expiry,
+ r -> data.on.statements, MDL);
+ }
+ if (r -> data.on.evtypes & ON_RELEASE) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on release");
+#endif
+ if (lease -> on_release)
+ executable_statement_dereference
+ (&lease -> on_release, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_release,
+ r -> data.on.statements, MDL);
+ }
+ if (r -> data.on.evtypes & ON_COMMIT) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on commit");
+#endif
+ if (lease -> on_commit)
+ executable_statement_dereference
+ (&lease -> on_commit, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_commit,
+ r -> data.on.statements, MDL);
+ }
+ }
+ break;
+
+ case switch_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: switch");
+#endif
+ status = (find_matching_case
+ (&e, packet, lease, client_state,
+ in_options, out_options, scope,
+ r -> data.s_switch.expr,
+ r -> data.s_switch.statements));
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: switch: case %lx", (unsigned long)e);
+#endif
+ if (status) {
+ if (!(execute_statements
+ (result, packet, lease, client_state,
+ in_options, out_options, scope, e))) {
+ executable_statement_dereference
+ (&e, MDL);
+ return 0;
+ }
+ executable_statement_dereference (&e, MDL);
+ }
+ break;
+
+ /* These have no effect when executed. */
+ case case_statement:
+ case default_statement:
+ break;
+
+ case if_statement:
+ status = (evaluate_boolean_expression
+ (&rc, packet,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.ie.expr));
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: if %s", (status
+ ? (rc ? "true" : "false")
+ : "NULL"));
+#endif
+ /* XXX Treat NULL as false */
+ if (!status)
+ rc = 0;
+ if (!execute_statements
+ (result, packet, lease, client_state,
+ in_options, out_options, scope,
+ rc ? r -> data.ie.tc : r -> data.ie.fc))
+ return 0;
+ break;
+
+ case eval_statement:
+ status = evaluate_expression
+ ((struct binding_value **)0,
+ packet, lease, client_state, in_options,
+ out_options, scope, r -> data.eval, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: evaluate: %s",
+ (status ? "succeeded" : "failed"));
+#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,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.retval, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: return: %s",
+ (status ? "succeeded" : "failed"));
+#endif
+ break;
+
+ case add_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: add %s", (r -> data.add -> name
+ ? r -> data.add -> name
+ : "<unnamed class>"));
+#endif
+ classify (packet, r -> data.add);
+ break;
+
+ case break_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: break");
+#endif
+ return 1;
+
+ case supersede_option_statement:
+ case send_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: %s option %s.%s",
+ (r -> op == supersede_option_statement
+ ? "supersede" : "send"),
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case default_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: default option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case append_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: append option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case prepend_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: prepend option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ option_statement:
+#endif
+ set_option (r -> data.option -> option -> universe,
+ out_options, r -> data.option, r -> op);
+ break;
+
+ case set_statement:
+ case define_statement:
+ if (!scope) {
+ log_error ("set %s: no scope",
+ r -> data.set.name);
+ status = 0;
+ break;
+ }
+ if (!*scope) {
+ if (!binding_scope_allocate (scope, MDL)) {
+ log_error ("set %s: can't allocate scope",
+ r -> data.set.name);
+ status = 0;
+ break;
+ }
+ }
+ binding = find_binding (*scope, r -> data.set.name);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: set %s", r -> data.set.name);
+#endif
+ if (!binding) {
+ binding = dmalloc (sizeof *binding, MDL);
+ if (binding) {
+ memset (binding, 0, sizeof *binding);
+ binding -> name =
+ dmalloc (strlen
+ (r -> data.set.name) + 1,
+ MDL);
+ if (binding -> name) {
+ strcpy (binding -> name,
+ r -> data.set.name);
+ binding -> next = (*scope) -> bindings;
+ (*scope) -> bindings = binding;
+ } else {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ }
+ }
+ }
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ if (r -> op == set_statement) {
+ status = (evaluate_expression
+ (&binding -> value, packet,
+ lease, client_state,
+ in_options, out_options,
+ scope, r -> data.set.expr,
+ MDL));
+ } else {
+ if (!(binding_value_allocate
+ (&binding -> value, MDL))) {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ }
+ if (binding -> value) {
+ binding -> value -> type =
+ binding_function;
+ (fundef_reference
+ (&binding -> value -> value.fundef,
+ r -> data.set.expr -> data.func,
+ MDL));
+ }
+ }
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: set %s%s", r -> data.set.name,
+ (binding && status ? "" : " (failed)"));
+#endif
+ break;
+
+ case unset_statement:
+ if (!scope || !*scope) {
+ status = 0;
+ break;
+ }
+ binding = find_binding (*scope, r -> data.unset);
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ status = 1;
+ } else
+ status = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: unset %s: %s", r -> data.unset,
+ (status ? "found" : "not found"));
+#endif
+ break;
+
+ case let_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: let %s", r -> data.let.name);
+#endif
+ ns = (struct binding_scope *)0;
+ binding_scope_allocate (&ns, MDL);
+ e = r;
+
+ next_let:
+ if (ns) {
+ binding = dmalloc (sizeof *binding, MDL);
+ memset (binding, 0, sizeof *binding);
+ if (!binding) {
+ blb:
+ binding_scope_dereference (&ns, MDL);
+ } else {
+ binding -> name =
+ dmalloc (strlen
+ (e -> data.let.name + 1),
+ MDL);
+ if (binding -> name)
+ strcpy (binding -> name,
+ e -> data.let.name);
+ else {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ goto blb;
+ }
+ }
+ } else
+ binding = NULL;
+
+ if (ns && binding) {
+ status = (evaluate_expression
+ (&binding -> value, packet, lease,
+ client_state,
+ in_options, out_options,
+ scope, e -> data.set.expr, MDL));
+ binding -> next = ns -> bindings;
+ ns -> bindings = binding;
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: let %s%s", e -> data.let.name,
+ (binding && status ? "" : "failed"));
+#endif
+ if (!e -> data.let.statements) {
+ } else if (e -> data.let.statements -> op ==
+ let_statement) {
+ e = e -> data.let.statements;
+ goto next_let;
+ } else if (ns) {
+ if (scope && *scope)
+ binding_scope_reference (&ns -> outer,
+ *scope, MDL);
+ execute_statements
+ (result, packet, lease,
+ client_state,
+ in_options, out_options,
+ &ns, e -> data.let.statements);
+ }
+ if (ns)
+ binding_scope_dereference (&ns, MDL);
+ break;
+
+ case log_statement:
+ memset (&ds, 0, sizeof ds);
+ status = (evaluate_data_expression
+ (&ds, packet,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.log.expr,
+ MDL));
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: log");
+#endif
+
+ if (status) {
+ switch (r -> data.log.priority) {
+ case log_priority_fatal:
+ log_fatal ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_error:
+ log_error ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_debug:
+ log_debug ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_info:
+ log_info ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ }
+ data_string_forget (&ds, MDL);
+ }
+
+ break;
+
+ default:
+ log_error ("bogus statement type %d", r -> op);
+ break;
+ }
+ executable_statement_dereference (&r, MDL);
+ if (next) {
+ executable_statement_reference (&r, next, MDL);
+ executable_statement_dereference (&next, MDL);
+ }
+ }
+
+ return 1;
+}
+
+/* Execute all the statements in a particular scope, and all statements in
+ scopes outer from that scope, but if a particular limiting scope is
+ reached, do not execute statements in that scope or in scopes outer
+ from it. More specific scopes need to take precedence over less
+ specific scopes, so we recursively traverse the scope list, executing
+ the most outer scope first. */
+
+void execute_statements_in_scope (result, packet,
+ lease, client_state, in_options, out_options,
+ scope, group, limiting_group)
+ 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 group *group;
+ struct group *limiting_group;
+{
+ struct group *limit;
+
+ /* If we've recursed as far as we can, return. */
+ if (!group)
+ return;
+
+ /* As soon as we get to a scope that is outer than the limiting
+ scope, we are done. This is so that if somebody does something
+ like this, it does the expected thing:
+
+ domain-name "fugue.com";
+ shared-network FOO {
+ host bar {
+ domain-name "othello.fugue.com";
+ fixed-address 10.20.30.40;
+ }
+ subnet 10.20.30.0 netmask 255.255.255.0 {
+ domain-name "manhattan.fugue.com";
+ }
+ }
+
+ The problem with the above arrangement is that the host's
+ group nesting will be host -> shared-network -> top-level,
+ and the limiting scope when we evaluate the host's scope
+ will be the subnet -> shared-network -> top-level, so we need
+ to know when we evaluate the host's scope to stop before we
+ evaluate the shared-networks scope, because it's outer than
+ the limiting scope, which means we've already evaluated it. */
+
+ for (limit = limiting_group; limit; limit = limit -> next) {
+ if (group == limit)
+ return;
+ }
+
+ if (group -> next)
+ execute_statements_in_scope (result, packet,
+ lease, client_state,
+ in_options, out_options, scope,
+ group -> next, limiting_group);
+ execute_statements (result, packet, lease, client_state, in_options,
+ out_options, scope, group -> statements);
+}
+
+/* Dereference or free any subexpressions of a statement being freed. */
+
+int executable_statement_dereference (ptr, file, line)
+ struct executable_statement **ptr;
+ const char *file;
+ int line;
+{
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ (*ptr) -> refcnt--;
+ rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
+ if ((*ptr) -> refcnt > 0) {
+ *ptr = (struct executable_statement *)0;
+ return 1;
+ }
+
+ if ((*ptr) -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*ptr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if ((*ptr) -> next)
+ executable_statement_dereference (&(*ptr) -> next, file, line);
+
+ switch ((*ptr) -> op) {
+ case statements_statement:
+ if ((*ptr) -> data.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.statements, file, line);
+ break;
+
+ case on_statement:
+ if ((*ptr) -> data.on.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.on.statements, file, line);
+ break;
+
+ case switch_statement:
+ if ((*ptr) -> data.s_switch.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.on.statements, file, line);
+ if ((*ptr) -> data.s_switch.expr)
+ expression_dereference (&(*ptr) -> data.s_switch.expr,
+ file, line);
+ break;
+
+ case case_statement:
+ if ((*ptr) -> data.s_switch.expr)
+ expression_dereference (&(*ptr) -> data.c_case,
+ file, line);
+ break;
+
+ case if_statement:
+ if ((*ptr) -> data.ie.expr)
+ expression_dereference (&(*ptr) -> data.ie.expr,
+ file, line);
+ if ((*ptr) -> data.ie.tc)
+ executable_statement_dereference
+ (&(*ptr) -> data.ie.tc, file, line);
+ if ((*ptr) -> data.ie.fc)
+ executable_statement_dereference
+ (&(*ptr) -> data.ie.fc, file, line);
+ break;
+
+ case eval_statement:
+ if ((*ptr) -> data.eval)
+ expression_dereference (&(*ptr) -> data.eval,
+ file, line);
+ break;
+
+ case return_statement:
+ if ((*ptr) -> data.eval)
+ expression_dereference (&(*ptr) -> data.eval,
+ file, line);
+ break;
+
+ case set_statement:
+ if ((*ptr)->data.set.name)
+ dfree ((*ptr)->data.set.name, file, line);
+ if ((*ptr)->data.set.expr)
+ expression_dereference (&(*ptr) -> data.set.expr,
+ file, line);
+ break;
+
+ case unset_statement:
+ if ((*ptr)->data.unset)
+ 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:
+ case append_option_statement:
+ case prepend_option_statement:
+ if ((*ptr) -> data.option)
+ option_cache_dereference (&(*ptr) -> data.option,
+ file, line);
+ break;
+
+ default:
+ /* Nothing to do. */
+ break;
+ }
+
+ dfree ((*ptr), file, line);
+ *ptr = (struct executable_statement *)0;
+ return 1;
+}
+
+void write_statements (file, statements, indent)
+ FILE *file;
+ struct executable_statement *statements;
+ int indent;
+{
+#if defined ENABLE_EXECUTE
+ struct expression *expr;
+#endif
+ struct executable_statement *r, *x;
+ const char *s, *t, *dot;
+ int col;
+
+ if (!statements)
+ return;
+
+ for (r = statements; r; r = r -> next) {
+ switch (r -> op) {
+ case statements_statement:
+ write_statements (file, r -> data.statements, indent);
+ break;
+
+ case on_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "on ");
+ s = "";
+ if (r -> data.on.evtypes & ON_EXPIRY) {
+ fprintf (file, "%sexpiry", s);
+ s = " or ";
+ }
+ if (r -> data.on.evtypes & ON_COMMIT) {
+ fprintf (file, "%scommit", s);
+ s = "or";
+ }
+ if (r -> data.on.evtypes & ON_RELEASE) {
+ fprintf (file, "%srelease", s);
+ s = "or";
+ }
+ if (r -> data.on.statements) {
+ fprintf (file, " {");
+ write_statements (file,
+ r -> data.on.statements,
+ indent + 2);
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ } else {
+ fprintf (file, ";");
+ }
+ break;
+
+ case switch_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "switch (");
+ col = write_expression (file,
+ r -> data.s_switch.expr,
+ indent + 7, indent + 7, 1);
+ col = token_print_indent (file, col, indent + 7,
+ "", "", ")");
+ token_print_indent (file,
+ col, indent, " ", "", "{");
+ write_statements (file, r -> data.s_switch.statements,
+ indent + 2);
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ break;
+
+ case case_statement:
+ indent_spaces (file, indent - 1);
+ fprintf (file, "case ");
+ col = write_expression (file,
+ r -> data.s_switch.expr,
+ indent + 5, indent + 5, 1);
+ token_print_indent (file, col, indent + 5,
+ "", "", ":");
+ break;
+
+ case default_statement:
+ indent_spaces (file, indent - 1);
+ fprintf (file, "default: ");
+ break;
+
+ case if_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "if ");
+ x = r;
+ col = write_expression (file,
+ x -> data.ie.expr,
+ indent + 3, indent + 3, 1);
+ else_if:
+ token_print_indent (file, col, indent, " ", "", "{");
+ write_statements (file, x -> data.ie.tc, indent + 2);
+ if (x -> data.ie.fc &&
+ x -> data.ie.fc -> op == if_statement &&
+ !x -> data.ie.fc -> next) {
+ indent_spaces (file, indent);
+ fprintf (file, "} elsif ");
+ x = x -> data.ie.fc;
+ col = write_expression (file,
+ x -> data.ie.expr,
+ indent + 6,
+ indent + 6, 1);
+ goto else_if;
+ }
+ if (x -> data.ie.fc) {
+ indent_spaces (file, indent);
+ fprintf (file, "} else {");
+ write_statements (file, x -> data.ie.fc,
+ indent + 2);
+ }
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ break;
+
+ case eval_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "eval ");
+ col = write_expression (file, r -> data.eval,
+ indent + 5, indent + 5, 1);
+ fprintf (file, ";");
+ break;
+
+ case return_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "return;");
+ break;
+
+ case add_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "add \"%s\"", r -> data.add -> name);
+ break;
+
+ case break_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "break;");
+ break;
+
+ case supersede_option_statement:
+ case send_option_statement:
+ s = "supersede";
+ goto option_statement;
+
+ case default_option_statement:
+ s = "default";
+ goto option_statement;
+
+ case append_option_statement:
+ s = "append";
+ goto option_statement;
+
+ case prepend_option_statement:
+ s = "prepend";
+ option_statement:
+ /* Note: the reason we don't try to pretty print
+ the option here is that the format of the option
+ may change in dhcpd.conf, and then when this
+ statement was read back, it would cause a syntax
+ error. */
+ if (r -> data.option -> option -> universe ==
+ &dhcp_universe) {
+ t = "";
+ dot = "";
+ } else {
+ t = (r -> data.option -> option ->
+ universe -> name);
+ dot = ".";
+ }
+ indent_spaces (file, indent);
+ fprintf (file, "%s %s%s%s = ", s, t, dot,
+ r -> data.option -> option -> name);
+ col = (indent + strlen (s) + strlen (t) +
+ strlen (dot) + strlen (r -> data.option ->
+ option -> name) + 4);
+ if (r -> data.option -> expression)
+ write_expression
+ (file,
+ r -> data.option -> expression,
+ col, indent + 8, 1);
+ else
+ token_indent_data_string
+ (file, col, indent + 8, "", "",
+ &r -> data.option -> data);
+
+ fprintf (file, ";"); /* XXX */
+ break;
+
+ case set_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "set ");
+ col = token_print_indent (file, indent + 4, indent + 4,
+ "", "", r -> data.set.name);
+ col = token_print_indent (file, col, indent + 4,
+ " ", " ", "=");
+ col = write_expression (file, r -> data.set.expr,
+ indent + 3, indent + 3, 0);
+ col = token_print_indent (file, col, indent + 4,
+ " ", "", ";");
+ break;
+
+ case unset_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "unset ");
+ col = token_print_indent (file, indent + 6, indent + 6,
+ "", "", r -> data.set.name);
+ col = token_print_indent (file, col, indent + 6,
+ " ", "", ";");
+ break;
+
+ case log_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "log ");
+ col = token_print_indent (file, indent + 4, indent + 4,
+ "", "", "(");
+ switch (r -> data.log.priority) {
+ case log_priority_fatal:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "fatal,");
+ break;
+ case log_priority_error:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "error,");
+ break;
+ case log_priority_debug:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "debug,");
+ break;
+ case log_priority_info:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "info,");
+ break;
+ }
+ col = write_expression (file, r -> data.log.expr,
+ indent + 4, indent + 4, 0);
+ col = token_print_indent (file, col, indent + 4,
+ "", "", ");");
+
+ break;
+
+ case execute_statement:
+#ifdef ENABLE_EXECUTE
+ indent_spaces (file, indent);
+ col = token_print_indent(file, indent + 4, 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);
+ }
+ }
+}
+
+/* Find a case statement in the sequence of executable statements that
+ matches the expression, and if found, return the following statement.
+ If no case statement matches, try to find a default statement and
+ return that (the default statement can precede all the case statements).
+ Otherwise, return the null statement. */
+
+int find_matching_case (struct executable_statement **ep,
+ 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,
+ struct executable_statement *stmt)
+{
+ int status, sub;
+ struct executable_statement *s;
+
+ if (is_data_expression (expr)) {
+ struct data_string cd, ds;
+ memset (&ds, 0, sizeof ds);
+ memset (&cd, 0, sizeof cd);
+
+ status = (evaluate_data_expression (&ds, packet, lease,
+ client_state, in_options,
+ out_options, scope, expr,
+ MDL));
+ if (status) {
+ for (s = stmt; s; s = s -> next) {
+ if (s -> op == case_statement) {
+ sub = (evaluate_data_expression
+ (&cd, packet, lease, client_state,
+ in_options, out_options,
+ scope, s -> data.c_case, MDL));
+ if (sub && cd.len == ds.len &&
+ !memcmp (cd.data, ds.data, cd.len))
+ {
+ data_string_forget (&cd, MDL);
+ data_string_forget (&ds, MDL);
+ executable_statement_reference
+ (ep, s -> next, MDL);
+ return 1;
+ }
+ data_string_forget (&cd, MDL);
+ }
+ }
+ data_string_forget (&ds, MDL);
+ }
+ } else {
+ unsigned long n, c;
+ status = evaluate_numeric_expression (&n, packet, lease,
+ client_state,
+ in_options, out_options,
+ scope, expr);
+
+ if (status) {
+ for (s = stmt; s; s = s -> next) {
+ if (s -> op == case_statement) {
+ sub = (evaluate_numeric_expression
+ (&c, packet, lease, client_state,
+ in_options, out_options,
+ scope, s -> data.c_case));
+ if (sub && n == c) {
+ executable_statement_reference
+ (ep, s -> next, MDL);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ /* If we didn't find a matching case statement, look for a default
+ statement and return the statement following it. */
+ for (s = stmt; s; s = s -> next)
+ if (s -> op == default_statement)
+ break;
+ if (s) {
+ executable_statement_reference (ep, s -> next, MDL);
+ return 1;
+ }
+ return 0;
+}
+
+int executable_statement_foreach (struct executable_statement *stmt,
+ int (*callback) (struct
+ executable_statement *,
+ void *, int),
+ void *vp, int condp)
+{
+ struct executable_statement *foo;
+ int ok = 0;
+
+ for (foo = stmt; foo; foo = foo -> next) {
+ if ((*callback) (foo, vp, condp) != 0)
+ ok = 1;
+ switch (foo -> op) {
+ case null_statement:
+ break;
+ case if_statement:
+ if (executable_statement_foreach (foo -> data.ie.tc,
+ callback, vp, 1))
+ ok = 1;
+ if (executable_statement_foreach (foo -> data.ie.fc,
+ callback, vp, 1))
+ ok = 1;
+ break;
+ case add_statement:
+ break;
+ case eval_statement:
+ break;
+ case break_statement:
+ break;
+ case default_option_statement:
+ break;
+ case supersede_option_statement:
+ break;
+ case append_option_statement:
+ break;
+ case prepend_option_statement:
+ break;
+ case send_option_statement:
+ break;
+ case statements_statement:
+ if ((executable_statement_foreach
+ (foo -> data.statements, callback, vp, condp)))
+ ok = 1;
+ break;
+ case on_statement:
+ if ((executable_statement_foreach
+ (foo -> data.on.statements, callback, vp, 1)))
+ ok = 1;
+ break;
+ case switch_statement:
+ if ((executable_statement_foreach
+ (foo -> data.s_switch.statements, callback, vp, 1)))
+ ok = 1;
+ break;
+ case case_statement:
+ break;
+ case default_statement:
+ break;
+ case set_statement:
+ break;
+ case unset_statement:
+ break;
+ case let_statement:
+ if ((executable_statement_foreach
+ (foo -> data.let.statements, callback, vp, 0)))
+ ok = 1;
+ break;
+ case define_statement:
+ break;
+ case log_statement:
+ case return_statement:
+ case execute_statement:
+ break;
+ }
+ }
+ return ok;
+}