1998-06-25 03:57:00 +00:00
|
|
|
/* execute.c
|
|
|
|
|
|
|
|
Support for executable statements. */
|
|
|
|
|
|
|
|
/*
|
1999-03-16 05:50:46 +00:00
|
|
|
* Copyright (c) 1996-1999 Internet Software Consortium.
|
|
|
|
* Use is subject to license terms which appear in the file named
|
|
|
|
* ISC-LICENSE that should have accompanied this file when you
|
|
|
|
* received it. If a file named ISC-LICENSE did not accompany this
|
|
|
|
* file, or you are not sure the one you have is correct, you may
|
|
|
|
* obtain an applicable copy of the license at:
|
1998-06-25 03:57:00 +00:00
|
|
|
*
|
1999-03-16 05:50:46 +00:00
|
|
|
* http://www.isc.org/isc-license-1.0.html.
|
1998-06-25 03:57:00 +00:00
|
|
|
*
|
1999-03-16 05:50:46 +00:00
|
|
|
* This file is part of the ISC DHCP distribution. The documentation
|
|
|
|
* associated with this file is listed in the file DOCUMENTATION,
|
|
|
|
* included in the top-level directory of this release.
|
1998-06-25 03:57:00 +00:00
|
|
|
*
|
1999-03-16 05:50:46 +00:00
|
|
|
* Support and other services are available for ISC products - see
|
|
|
|
* http://www.isc.org for more information.
|
1998-06-25 03:57:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
static char copyright[] =
|
1999-09-22 01:45:57 +00:00
|
|
|
"$Id: execute.c,v 1.19 1999/09/22 01:45:49 mellon Exp $ Copyright (c) 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
|
1998-06-25 03:57:00 +00:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
#include "dhcpd.h"
|
|
|
|
|
1999-07-02 20:58:48 +00:00
|
|
|
int execute_statements (packet, lease, in_options, out_options, statements)
|
1998-06-25 03:57:00 +00:00
|
|
|
struct packet *packet;
|
1999-07-02 20:58:48 +00:00
|
|
|
struct lease *lease;
|
1998-11-05 18:40:40 +00:00
|
|
|
struct option_state *in_options;
|
|
|
|
struct option_state *out_options;
|
1998-06-25 03:57:00 +00:00
|
|
|
struct executable_statement *statements;
|
|
|
|
{
|
|
|
|
struct executable_statement *r;
|
1998-11-05 18:40:40 +00:00
|
|
|
int result;
|
|
|
|
int status;
|
1998-06-25 03:57:00 +00:00
|
|
|
|
|
|
|
if (!statements)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
for (r = statements; r; r = r -> next) {
|
|
|
|
switch (r -> op) {
|
1999-07-16 21:34:14 +00:00
|
|
|
case statements_statement:
|
1999-07-17 15:52:20 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-09-09 23:53:29 +00:00
|
|
|
log_debug ("exec: statements");
|
1999-07-17 15:52:20 +00:00
|
|
|
#endif
|
|
|
|
status = execute_statements (packet, lease,
|
|
|
|
in_options, out_options,
|
|
|
|
r -> data.statements);
|
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-09-09 23:53:29 +00:00
|
|
|
log_debug ("exec: statements returns %d", status);
|
1999-07-17 15:52:20 +00:00
|
|
|
#endif
|
|
|
|
if (!status)
|
1999-07-16 21:34:14 +00:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case on_statement:
|
|
|
|
if (lease) {
|
|
|
|
struct executable_statement **d;
|
|
|
|
switch (r -> data.on.evtype) {
|
|
|
|
case expiry:
|
|
|
|
d = &lease -> on_expiry;
|
|
|
|
break;
|
|
|
|
case commit:
|
|
|
|
d = &lease -> on_commit;
|
|
|
|
break;
|
|
|
|
case release:
|
|
|
|
d = &lease -> on_release;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_fatal ("unknown event type %d %s",
|
|
|
|
r -> data.on.evtype,
|
|
|
|
"in on statement.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*d)
|
|
|
|
executable_statement_dereference
|
|
|
|
(d, "execute_statements");
|
|
|
|
executable_statement_reference
|
|
|
|
(d, r -> data.on.statements,
|
|
|
|
"execute_statements");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1998-06-25 03:57:00 +00:00
|
|
|
case if_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
status = evaluate_boolean_expression
|
1999-07-31 17:56:09 +00:00
|
|
|
(&result, packet, lease,
|
|
|
|
in_options, out_options, r -> data.ie.expr);
|
1998-11-05 18:40:40 +00:00
|
|
|
|
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: if %s", (status
|
1998-11-05 18:40:40 +00:00
|
|
|
? (result ? "true" : "false")
|
|
|
|
: "NULL"));
|
|
|
|
#endif
|
|
|
|
/* XXX Treat NULL as false */
|
|
|
|
if (!status)
|
|
|
|
result = 0;
|
1998-06-25 03:57:00 +00:00
|
|
|
if (!execute_statements
|
1999-07-02 20:58:48 +00:00
|
|
|
(packet, lease, in_options, out_options,
|
1998-11-05 18:40:40 +00:00
|
|
|
result ? r -> data.ie.true : r -> data.ie.false))
|
1998-06-25 03:57:00 +00:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eval_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
status = evaluate_boolean_expression
|
1999-07-31 17:56:09 +00:00
|
|
|
(&result, packet, lease,
|
|
|
|
in_options, out_options, r -> data.eval);
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: evaluate: %s",
|
1998-11-05 18:40:40 +00:00
|
|
|
(status
|
|
|
|
? (result ? "true" : "false") : "NULL"));
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case add_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: add %s", (r -> data.add -> name
|
1998-11-05 18:40:40 +00:00
|
|
|
? r -> data.add -> name
|
|
|
|
: "<unnamed class>"));
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
classify (packet, r -> data.add);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case break_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: break");
|
1998-11-05 18:40:40 +00:00
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case supersede_option_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: supersede option %s.%s",
|
1998-11-05 18:40:40 +00:00
|
|
|
r -> data.option -> option -> universe -> name,
|
|
|
|
r -> data.option -> option -> name);
|
|
|
|
goto option_statement;
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
case default_option_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: default option %s.%s",
|
1998-11-05 18:40:40 +00:00
|
|
|
r -> data.option -> option -> universe -> name,
|
|
|
|
r -> data.option -> option -> name);
|
|
|
|
goto option_statement;
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
case append_option_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: append option %s.%s",
|
1998-11-05 18:40:40 +00:00
|
|
|
r -> data.option -> option -> universe -> name,
|
|
|
|
r -> data.option -> option -> name);
|
|
|
|
goto option_statement;
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
case prepend_option_statement:
|
1998-11-05 18:40:40 +00:00
|
|
|
#if defined (DEBUG_EXPRESSIONS)
|
1999-07-19 13:08:29 +00:00
|
|
|
log_debug ("exec: prepend option %s.%s",
|
1998-11-05 18:40:40 +00:00
|
|
|
r -> data.option -> option -> universe -> name,
|
|
|
|
r -> data.option -> option -> name);
|
|
|
|
option_statement:
|
|
|
|
#endif
|
1998-06-25 03:57:00 +00:00
|
|
|
if (r -> data.option -> option -> universe -> set_func)
|
1999-04-05 15:35:54 +00:00
|
|
|
((r -> data.option -> option ->
|
|
|
|
universe -> set_func)
|
|
|
|
(r -> data.option -> option -> universe,
|
|
|
|
out_options,
|
|
|
|
r -> data.option, r -> op));
|
1998-06-25 03:57:00 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
1999-02-24 17:56:53 +00:00
|
|
|
log_fatal ("bogus statement type %d\n", r -> op);
|
1998-06-25 03:57:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
1998-11-11 07:51:41 +00:00
|
|
|
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. */
|
1998-06-25 03:57:00 +00:00
|
|
|
|
1999-07-02 20:58:48 +00:00
|
|
|
void execute_statements_in_scope (packet, lease, in_options, out_options,
|
1998-11-11 07:51:41 +00:00
|
|
|
group, limiting_group)
|
1998-06-25 03:57:00 +00:00
|
|
|
struct packet *packet;
|
1999-07-02 20:58:48 +00:00
|
|
|
struct lease *lease;
|
1998-11-11 07:51:41 +00:00
|
|
|
struct option_state *in_options;
|
|
|
|
struct option_state *out_options;
|
1998-06-25 03:57:00 +00:00
|
|
|
struct group *group;
|
|
|
|
struct group *limiting_group;
|
|
|
|
{
|
|
|
|
struct group *scope;
|
1999-02-25 23:30:43 +00:00
|
|
|
struct group *limit;
|
|
|
|
|
1999-03-09 23:40:22 +00:00
|
|
|
/* If we've recursed as far as we can, return. */
|
|
|
|
if (!group)
|
|
|
|
return;
|
|
|
|
|
1999-02-25 23:30:43 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
1998-06-25 03:57:00 +00:00
|
|
|
|
1998-11-11 07:51:41 +00:00
|
|
|
if (group -> next)
|
1999-07-02 20:58:48 +00:00
|
|
|
execute_statements_in_scope (packet, lease,
|
|
|
|
in_options, out_options,
|
1998-11-11 07:51:41 +00:00
|
|
|
group -> next, limiting_group);
|
1999-07-02 20:58:48 +00:00
|
|
|
execute_statements (packet, lease,
|
1998-11-11 07:51:41 +00:00
|
|
|
in_options, out_options, group -> statements);
|
1998-06-25 03:57:00 +00:00
|
|
|
}
|
1999-07-17 17:59:24 +00:00
|
|
|
|
|
|
|
/* Dereference or free any subexpressions of a statement being freed. */
|
|
|
|
|
|
|
|
int executable_statement_dereference (ptr, name)
|
|
|
|
struct executable_statement **ptr;
|
|
|
|
char *name;
|
|
|
|
{
|
|
|
|
struct executable_statement *bp;
|
|
|
|
|
|
|
|
if (!ptr || !*ptr) {
|
|
|
|
log_error ("Null ptr in executable_statement_dereference: %s",
|
|
|
|
name);
|
|
|
|
#if defined (POINTER_DEBUG)
|
|
|
|
abort ();
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
(*ptr) -> refcnt--;
|
|
|
|
if ((*ptr) -> refcnt) {
|
|
|
|
*ptr = (struct executable_statement *)0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*ptr) -> next)
|
|
|
|
executable_statement_dereference
|
|
|
|
(&(*ptr) -> next, name);
|
|
|
|
|
|
|
|
switch ((*ptr) -> op) {
|
|
|
|
case statements_statement:
|
1999-07-19 15:34:33 +00:00
|
|
|
if ((*ptr) -> data.statements)
|
|
|
|
executable_statement_dereference
|
|
|
|
(&(*ptr) -> data.statements, name);
|
1999-07-17 17:59:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case on_statement:
|
1999-07-19 15:34:33 +00:00
|
|
|
if ((*ptr) -> data.on.statements)
|
|
|
|
executable_statement_dereference
|
|
|
|
(&(*ptr) -> data.on.statements, name);
|
1999-07-17 17:59:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case if_statement:
|
1999-07-19 15:34:33 +00:00
|
|
|
if ((*ptr) -> data.ie.expr)
|
|
|
|
expression_dereference (&(*ptr) -> data.ie.expr, name);
|
|
|
|
if ((*ptr) -> data.ie.true)
|
|
|
|
executable_statement_dereference
|
|
|
|
(&(*ptr) -> data.ie.true, name);
|
1999-07-17 17:59:24 +00:00
|
|
|
if ((*ptr) -> data.ie.false)
|
|
|
|
executable_statement_dereference
|
|
|
|
(&(*ptr) -> data.ie.false, name);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eval_statement:
|
1999-07-19 15:34:33 +00:00
|
|
|
if ((*ptr) -> data.eval)
|
|
|
|
expression_dereference (&(*ptr) -> data.eval, name);
|
1999-07-17 17:59:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case supersede_option_statement:
|
|
|
|
case default_option_statement:
|
|
|
|
case append_option_statement:
|
|
|
|
case prepend_option_statement:
|
1999-07-19 15:34:33 +00:00
|
|
|
if ((*ptr) -> data.option)
|
|
|
|
option_cache_dereference (&(*ptr) -> data.option,
|
|
|
|
name);
|
1999-07-17 17:59:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Nothing to do. */
|
1999-09-15 17:22:52 +00:00
|
|
|
break;
|
1999-07-17 17:59:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dfree ((*ptr), name);
|
|
|
|
*ptr = (struct executable_statement *)0;
|
|
|
|
return 1;
|
|
|
|
}
|
1999-09-22 01:45:57 +00:00
|
|
|
|
|
|
|
void write_statements (file, statements, indent)
|
|
|
|
FILE *file;
|
|
|
|
struct executable_statement *statements;
|
|
|
|
int indent;
|
|
|
|
{
|
|
|
|
struct executable_statement *r, *x;
|
|
|
|
int result;
|
|
|
|
int status;
|
|
|
|
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:
|
|
|
|
switch (r -> data.on.evtype) {
|
|
|
|
case expiry:
|
|
|
|
s = "expiry";
|
|
|
|
break;
|
|
|
|
case commit:
|
|
|
|
s = "commit";
|
|
|
|
break;
|
|
|
|
case release:
|
|
|
|
s = "release";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
log_fatal ("unknown event type %d %s",
|
|
|
|
r -> data.on.evtype,
|
|
|
|
"in on statement.");
|
|
|
|
}
|
|
|
|
indent_spaces (file, indent);
|
|
|
|
fprintf (file, "on %s {", s);
|
|
|
|
write_statements (file, r -> data.on.statements,
|
|
|
|
indent + 2);
|
|
|
|
indent_spaces (file, indent);
|
|
|
|
fprintf (file, "}");
|
|
|
|
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);
|
|
|
|
else_if:
|
|
|
|
token_print_indent (file, col, indent, " ", "", "{");
|
|
|
|
write_statements (file, x -> data.ie.true, indent + 2);
|
|
|
|
if (x -> data.ie.false &&
|
|
|
|
x -> data.ie.false -> op == if_statement &&
|
|
|
|
!x -> data.ie.false -> next) {
|
|
|
|
indent_spaces (file, indent);
|
|
|
|
fprintf (file, "} elsif ");
|
|
|
|
x = x -> data.ie.false;
|
|
|
|
col = write_expression (file,
|
|
|
|
x -> data.ie.expr,
|
|
|
|
indent + 6,
|
|
|
|
indent + 6);
|
|
|
|
goto else_if;
|
|
|
|
}
|
|
|
|
if (x -> data.ie.false) {
|
|
|
|
indent_spaces (file, indent);
|
|
|
|
fprintf (file, "} else {");
|
|
|
|
write_statements (file, x -> data.ie.false,
|
|
|
|
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);
|
|
|
|
fprintf (file, ";");
|
|
|
|
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:
|
|
|
|
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 = (char *)0;
|
|
|
|
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);
|
|
|
|
else
|
|
|
|
token_indent_data_string
|
|
|
|
(file, col, indent + 8, "", "",
|
|
|
|
&r -> data.option -> data);
|
|
|
|
|
|
|
|
fprintf (file, ";"); /* XXX */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
log_fatal ("bogus statement type %d\n", r -> op);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|