]>
Commit | Line | Data |
---|---|---|
055fa312 TL |
1 | /* execute.c |
2 | ||
3 | Support for executable statements. */ | |
4 | ||
5 | /* | |
98311e4b DH |
6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
7 | * Copyright (c) 1998-2003 by Internet Software Consortium | |
055fa312 | 8 | * |
98311e4b DH |
9 | * Permission to use, copy, modify, and distribute this software for any |
10 | * purpose with or without fee is hereby granted, provided that the above | |
11 | * copyright notice and this permission notice appear in all copies. | |
055fa312 | 12 | * |
98311e4b DH |
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
055fa312 | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
25 | * http://www.isc.org/ | |
49733f31 | 26 | * |
98311e4b | 27 | * This software has been written for Internet Systems Consortium |
49733f31 | 28 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. |
98311e4b | 29 | * To learn more about Internet Systems Consortium, see |
49733f31 TL |
30 | * ``http://www.isc.org/''. To learn more about Vixie Enterprises, |
31 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see | |
32 | * ``http://www.nominum.com''. | |
055fa312 TL |
33 | */ |
34 | ||
35 | #ifndef lint | |
36 | static char copyright[] = | |
98311e4b | 37 | "$Id: execute.c,v 1.47 2005/03/17 20:14:58 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n"; |
055fa312 TL |
38 | #endif /* not lint */ |
39 | ||
40 | #include "dhcpd.h" | |
4bd8800e | 41 | #include <omapip/omapip_p.h> |
055fa312 | 42 | |
63b4fbf9 TL |
43 | int execute_statements (result, packet, lease, client_state, |
44 | in_options, out_options, scope, statements) | |
1b234d44 | 45 | struct binding_value **result; |
055fa312 | 46 | struct packet *packet; |
da38df14 | 47 | struct lease *lease; |
63b4fbf9 | 48 | struct client_state *client_state; |
a1933e2d TL |
49 | struct option_state *in_options; |
50 | struct option_state *out_options; | |
6ceb9118 | 51 | struct binding_scope **scope; |
055fa312 TL |
52 | struct executable_statement *statements; |
53 | { | |
a75826c6 | 54 | struct executable_statement *r, *e, *next; |
1b234d44 | 55 | int rc; |
a1933e2d | 56 | int status; |
f6a30e8d TL |
57 | unsigned long num; |
58 | struct binding_scope *outer; | |
91c82d9f TL |
59 | struct binding *binding; |
60 | struct data_string ds; | |
f6a30e8d | 61 | struct binding_scope *ns; |
055fa312 TL |
62 | |
63 | if (!statements) | |
64 | return 1; | |
65 | ||
a75826c6 TL |
66 | r = (struct executable_statement *)0; |
67 | next = (struct executable_statement *)0; | |
68 | e = (struct executable_statement *)0; | |
69 | executable_statement_reference (&r, statements, MDL); | |
1b234d44 | 70 | while (r && !(result && *result)) { |
a75826c6 TL |
71 | if (r -> next) |
72 | executable_statement_reference (&next, r -> next, MDL); | |
055fa312 | 73 | switch (r -> op) { |
79a65726 | 74 | case statements_statement: |
8ad81818 | 75 | #if defined (DEBUG_EXPRESSIONS) |
e92653f1 | 76 | log_debug ("exec: statements"); |
8ad81818 | 77 | #endif |
1b234d44 | 78 | status = execute_statements (result, packet, lease, |
63b4fbf9 TL |
79 | client_state, in_options, |
80 | out_options, scope, | |
8ad81818 TL |
81 | r -> data.statements); |
82 | #if defined (DEBUG_EXPRESSIONS) | |
e92653f1 | 83 | log_debug ("exec: statements returns %d", status); |
8ad81818 TL |
84 | #endif |
85 | if (!status) | |
79a65726 TL |
86 | return 0; |
87 | break; | |
88 | ||
89 | case on_statement: | |
90 | if (lease) { | |
91c82d9f | 91 | if (r -> data.on.evtypes & ON_EXPIRY) { |
9615e0e6 TL |
92 | #if defined (DEBUG_EXPRESSIONS) |
93 | log_debug ("exec: on expiry"); | |
94 | #endif | |
91c82d9f | 95 | if (lease -> on_expiry) |
79a65726 | 96 | executable_statement_dereference |
4bd8800e | 97 | (&lease -> on_expiry, MDL); |
f6a30e8d TL |
98 | if (r -> data.on.statements) |
99 | executable_statement_reference | |
100 | (&lease -> on_expiry, | |
4bd8800e | 101 | r -> data.on.statements, MDL); |
91c82d9f TL |
102 | } |
103 | if (r -> data.on.evtypes & ON_RELEASE) { | |
9615e0e6 TL |
104 | #if defined (DEBUG_EXPRESSIONS) |
105 | log_debug ("exec: on release"); | |
106 | #endif | |
91c82d9f TL |
107 | if (lease -> on_release) |
108 | executable_statement_dereference | |
4bd8800e | 109 | (&lease -> on_release, MDL); |
f6a30e8d TL |
110 | if (r -> data.on.statements) |
111 | executable_statement_reference | |
112 | (&lease -> on_release, | |
4bd8800e | 113 | r -> data.on.statements, MDL); |
91c82d9f TL |
114 | } |
115 | if (r -> data.on.evtypes & ON_COMMIT) { | |
9615e0e6 TL |
116 | #if defined (DEBUG_EXPRESSIONS) |
117 | log_debug ("exec: on commit"); | |
118 | #endif | |
91c82d9f TL |
119 | if (lease -> on_commit) |
120 | executable_statement_dereference | |
4bd8800e | 121 | (&lease -> on_commit, MDL); |
f6a30e8d TL |
122 | if (r -> data.on.statements) |
123 | executable_statement_reference | |
124 | (&lease -> on_commit, | |
4bd8800e | 125 | r -> data.on.statements, MDL); |
91c82d9f | 126 | } |
79a65726 TL |
127 | } |
128 | break; | |
129 | ||
91c82d9f | 130 | case switch_statement: |
9615e0e6 TL |
131 | #if defined (DEBUG_EXPRESSIONS) |
132 | log_debug ("exec: switch"); | |
133 | #endif | |
a75826c6 | 134 | status = (find_matching_case |
63b4fbf9 | 135 | (&e, packet, lease, client_state, |
a75826c6 TL |
136 | in_options, out_options, scope, |
137 | r -> data.s_switch.expr, | |
138 | r -> data.s_switch.statements)); | |
9615e0e6 TL |
139 | #if defined (DEBUG_EXPRESSIONS) |
140 | log_debug ("exec: switch: case %lx", (unsigned long)e); | |
141 | #endif | |
a75826c6 TL |
142 | if (status) { |
143 | if (!(execute_statements | |
63b4fbf9 | 144 | (result, packet, lease, client_state, |
a75826c6 TL |
145 | in_options, out_options, scope, e))) { |
146 | executable_statement_dereference | |
147 | (&e, MDL); | |
148 | return 0; | |
149 | } | |
150 | executable_statement_dereference (&e, MDL); | |
151 | } | |
9615e0e6 | 152 | break; |
91c82d9f TL |
153 | |
154 | /* These have no effect when executed. */ | |
155 | case case_statement: | |
156 | case default_statement: | |
157 | break; | |
158 | ||
055fa312 | 159 | case if_statement: |
6ceb9118 | 160 | status = (evaluate_boolean_expression |
63b4fbf9 TL |
161 | (&rc, packet, |
162 | lease, client_state, in_options, | |
6ceb9118 | 163 | out_options, scope, r -> data.ie.expr)); |
a1933e2d TL |
164 | |
165 | #if defined (DEBUG_EXPRESSIONS) | |
27b3a58f | 166 | log_debug ("exec: if %s", (status |
1b234d44 | 167 | ? (rc ? "true" : "false") |
a1933e2d TL |
168 | : "NULL")); |
169 | #endif | |
170 | /* XXX Treat NULL as false */ | |
171 | if (!status) | |
1b234d44 | 172 | rc = 0; |
055fa312 | 173 | if (!execute_statements |
63b4fbf9 | 174 | (result, packet, lease, client_state, |
1b234d44 | 175 | in_options, out_options, scope, |
d758ad8c | 176 | rc ? r -> data.ie.tc : r -> data.ie.fc)) |
055fa312 TL |
177 | return 0; |
178 | break; | |
179 | ||
180 | case eval_statement: | |
08514fb8 TL |
181 | status = evaluate_expression |
182 | ((struct binding_value **)0, | |
63b4fbf9 | 183 | packet, lease, client_state, in_options, |
d758ad8c | 184 | out_options, scope, r -> data.eval, MDL); |
a1933e2d | 185 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 186 | log_debug ("exec: evaluate: %s", |
9615e0e6 | 187 | (status ? "succeeded" : "failed")); |
a1933e2d | 188 | #endif |
055fa312 TL |
189 | break; |
190 | ||
1b234d44 DN |
191 | case return_statement: |
192 | status = evaluate_expression | |
63b4fbf9 TL |
193 | (result, packet, |
194 | lease, client_state, in_options, | |
d758ad8c | 195 | out_options, scope, r -> data.retval, MDL); |
1b234d44 DN |
196 | #if defined (DEBUG_EXPRESSIONS) |
197 | log_debug ("exec: return: %s", | |
198 | (status ? "succeeded" : "failed")); | |
199 | #endif | |
200 | break; | |
201 | ||
055fa312 | 202 | case add_statement: |
a1933e2d | 203 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 204 | log_debug ("exec: add %s", (r -> data.add -> name |
a1933e2d TL |
205 | ? r -> data.add -> name |
206 | : "<unnamed class>")); | |
207 | #endif | |
055fa312 TL |
208 | classify (packet, r -> data.add); |
209 | break; | |
210 | ||
211 | case break_statement: | |
a1933e2d | 212 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 213 | log_debug ("exec: break"); |
a1933e2d | 214 | #endif |
9615e0e6 | 215 | return 1; |
055fa312 TL |
216 | |
217 | case supersede_option_statement: | |
33454222 | 218 | case send_option_statement: |
a1933e2d | 219 | #if defined (DEBUG_EXPRESSIONS) |
33454222 TL |
220 | log_debug ("exec: %s option %s.%s", |
221 | (r -> op == supersede_option_statement | |
222 | ? "supersede" : "send"), | |
a1933e2d TL |
223 | r -> data.option -> option -> universe -> name, |
224 | r -> data.option -> option -> name); | |
225 | goto option_statement; | |
226 | #endif | |
055fa312 | 227 | case default_option_statement: |
a1933e2d | 228 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 229 | log_debug ("exec: default option %s.%s", |
a1933e2d TL |
230 | r -> data.option -> option -> universe -> name, |
231 | r -> data.option -> option -> name); | |
232 | goto option_statement; | |
233 | #endif | |
055fa312 | 234 | case append_option_statement: |
a1933e2d | 235 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 236 | log_debug ("exec: append option %s.%s", |
a1933e2d TL |
237 | r -> data.option -> option -> universe -> name, |
238 | r -> data.option -> option -> name); | |
239 | goto option_statement; | |
240 | #endif | |
055fa312 | 241 | case prepend_option_statement: |
a1933e2d | 242 | #if defined (DEBUG_EXPRESSIONS) |
27b3a58f | 243 | log_debug ("exec: prepend option %s.%s", |
a1933e2d TL |
244 | r -> data.option -> option -> universe -> name, |
245 | r -> data.option -> option -> name); | |
246 | option_statement: | |
247 | #endif | |
85c14386 TL |
248 | set_option (r -> data.option -> option -> universe, |
249 | out_options, r -> data.option, r -> op); | |
055fa312 TL |
250 | break; |
251 | ||
91c82d9f | 252 | case set_statement: |
1b234d44 | 253 | case define_statement: |
6ceb9118 TL |
254 | if (!scope) { |
255 | log_error ("set %s: no scope", | |
256 | r -> data.set.name); | |
257 | status = 0; | |
258 | break; | |
259 | } | |
260 | if (!*scope) { | |
261 | if (!binding_scope_allocate (scope, MDL)) { | |
262 | log_error ("set %s: can't allocate scope", | |
263 | r -> data.set.name); | |
264 | status = 0; | |
265 | break; | |
266 | } | |
267 | } | |
268 | binding = find_binding (*scope, r -> data.set.name); | |
9615e0e6 TL |
269 | #if defined (DEBUG_EXPRESSIONS) |
270 | log_debug ("exec: set %s", r -> data.set.name); | |
271 | #endif | |
7530a0ab | 272 | if (!binding) { |
4bd8800e | 273 | binding = dmalloc (sizeof *binding, MDL); |
91c82d9f | 274 | if (binding) { |
55321a21 | 275 | memset (binding, 0, sizeof *binding); |
91c82d9f TL |
276 | binding -> name = |
277 | dmalloc (strlen | |
3dc96064 | 278 | (r -> data.set.name) + 1, |
4bd8800e | 279 | MDL); |
f6a30e8d | 280 | if (binding -> name) { |
91c82d9f TL |
281 | strcpy (binding -> name, |
282 | r -> data.set.name); | |
349c525e DN |
283 | binding -> next = (*scope) -> bindings; |
284 | (*scope) -> bindings = binding; | |
f6a30e8d TL |
285 | } else { |
286 | badalloc: | |
4bd8800e | 287 | dfree (binding, MDL); |
f6a30e8d TL |
288 | binding = (struct binding *)0; |
289 | } | |
290 | } | |
91c82d9f TL |
291 | } |
292 | if (binding) { | |
08514fb8 TL |
293 | if (binding -> value) |
294 | binding_value_dereference | |
295 | (&binding -> value, MDL); | |
1b234d44 DN |
296 | if (r -> op == set_statement) { |
297 | status = (evaluate_expression | |
298 | (&binding -> value, packet, | |
63b4fbf9 TL |
299 | lease, client_state, |
300 | in_options, out_options, | |
d758ad8c TL |
301 | scope, r -> data.set.expr, |
302 | MDL)); | |
1b234d44 DN |
303 | } else { |
304 | if (!(binding_value_allocate | |
305 | (&binding -> value, MDL))) { | |
306 | dfree (binding, MDL); | |
307 | binding = (struct binding *)0; | |
308 | } | |
309 | if (binding -> value) { | |
310 | binding -> value -> type = | |
311 | binding_function; | |
312 | (fundef_reference | |
313 | (&binding -> value -> value.fundef, | |
314 | r -> data.set.expr -> data.func, | |
315 | MDL)); | |
316 | } | |
317 | } | |
91c82d9f | 318 | } |
91c82d9f | 319 | #if defined (DEBUG_EXPRESSIONS) |
08514fb8 TL |
320 | log_debug ("exec: set %s%s", r -> data.set.name, |
321 | (binding && status ? "" : " (failed)")); | |
91c82d9f TL |
322 | #endif |
323 | break; | |
324 | ||
f6a30e8d | 325 | case unset_statement: |
6ceb9118 TL |
326 | if (!scope || !*scope) { |
327 | status = 0; | |
328 | break; | |
329 | } | |
330 | binding = find_binding (*scope, r -> data.unset); | |
f6a30e8d | 331 | if (binding) { |
08514fb8 TL |
332 | if (binding -> value) |
333 | binding_value_dereference | |
334 | (&binding -> value, MDL); | |
f6a30e8d TL |
335 | status = 1; |
336 | } else | |
337 | status = 0; | |
338 | #if defined (DEBUG_EXPRESSIONS) | |
339 | log_debug ("exec: unset %s: %s", r -> data.unset, | |
08514fb8 | 340 | (status ? "found" : "not found")); |
f6a30e8d TL |
341 | #endif |
342 | break; | |
343 | ||
344 | case let_statement: | |
9615e0e6 TL |
345 | #if defined (DEBUG_EXPRESSIONS) |
346 | log_debug ("exec: let %s", r -> data.let.name); | |
347 | #endif | |
f6a30e8d | 348 | ns = (struct binding_scope *)0; |
4bd8800e | 349 | binding_scope_allocate (&ns, MDL); |
08514fb8 | 350 | e = r; |
f6a30e8d TL |
351 | |
352 | next_let: | |
353 | if (ns) { | |
4bd8800e | 354 | binding = dmalloc (sizeof *binding, MDL); |
55321a21 | 355 | memset (binding, 0, sizeof *binding); |
f6a30e8d TL |
356 | if (!binding) { |
357 | blb: | |
4bd8800e | 358 | binding_scope_dereference (&ns, MDL); |
f6a30e8d TL |
359 | } else { |
360 | binding -> name = | |
361 | dmalloc (strlen | |
08514fb8 | 362 | (e -> data.let.name + 1), |
4bd8800e | 363 | MDL); |
f6a30e8d TL |
364 | if (binding -> name) |
365 | strcpy (binding -> name, | |
08514fb8 | 366 | e -> data.let.name); |
f6a30e8d | 367 | else { |
4bd8800e | 368 | dfree (binding, MDL); |
f6a30e8d TL |
369 | binding = (struct binding *)0; |
370 | goto blb; | |
371 | } | |
372 | } | |
98311e4b DH |
373 | } else |
374 | binding = NULL; | |
375 | ||
08514fb8 TL |
376 | if (ns && binding) { |
377 | status = (evaluate_expression | |
378 | (&binding -> value, packet, lease, | |
63b4fbf9 | 379 | client_state, |
08514fb8 | 380 | in_options, out_options, |
d758ad8c | 381 | scope, e -> data.set.expr, MDL)); |
f6a30e8d TL |
382 | binding -> next = ns -> bindings; |
383 | ns -> bindings = binding; | |
384 | } | |
385 | ||
f6a30e8d | 386 | #if defined (DEBUG_EXPRESSIONS) |
08514fb8 TL |
387 | log_debug ("exec: let %s%s", e -> data.let.name, |
388 | (binding && status ? "" : "failed")); | |
f6a30e8d | 389 | #endif |
08514fb8 TL |
390 | if (!e -> data.let.statements) { |
391 | } else if (e -> data.let.statements -> op == | |
f6a30e8d | 392 | let_statement) { |
08514fb8 | 393 | e = e -> data.let.statements; |
f6a30e8d TL |
394 | goto next_let; |
395 | } else if (ns) { | |
6ceb9118 TL |
396 | if (scope && *scope) |
397 | binding_scope_reference (&ns -> outer, | |
398 | *scope, MDL); | |
f6a30e8d | 399 | execute_statements |
1b234d44 | 400 | (result, packet, lease, |
63b4fbf9 | 401 | client_state, |
1b234d44 | 402 | in_options, out_options, |
6ceb9118 | 403 | &ns, e -> data.let.statements); |
f6a30e8d TL |
404 | } |
405 | if (ns) | |
4bd8800e | 406 | binding_scope_dereference (&ns, MDL); |
f6a30e8d TL |
407 | break; |
408 | ||
e7a9c293 DN |
409 | case log_statement: |
410 | memset (&ds, 0, sizeof ds); | |
411 | status = (evaluate_data_expression | |
63b4fbf9 TL |
412 | (&ds, packet, |
413 | lease, client_state, in_options, | |
d758ad8c TL |
414 | out_options, scope, r -> data.log.expr, |
415 | MDL)); | |
e7a9c293 DN |
416 | |
417 | #if defined (DEBUG_EXPRESSIONS) | |
418 | log_debug ("exec: log"); | |
419 | #endif | |
420 | ||
421 | if (status) { | |
422 | switch (r -> data.log.priority) { | |
423 | case log_priority_fatal: | |
424 | log_fatal ("%.*s", (int)ds.len, | |
98311e4b | 425 | ds.data); |
e7a9c293 DN |
426 | break; |
427 | case log_priority_error: | |
428 | log_error ("%.*s", (int)ds.len, | |
98311e4b | 429 | ds.data); |
e7a9c293 DN |
430 | break; |
431 | case log_priority_debug: | |
432 | log_debug ("%.*s", (int)ds.len, | |
98311e4b | 433 | ds.data); |
e7a9c293 DN |
434 | break; |
435 | case log_priority_info: | |
436 | log_info ("%.*s", (int)ds.len, | |
98311e4b | 437 | ds.data); |
e7a9c293 DN |
438 | break; |
439 | } | |
440 | data_string_forget (&ds, MDL); | |
441 | } | |
442 | ||
443 | break; | |
444 | ||
055fa312 | 445 | default: |
2bf47722 TL |
446 | log_error ("bogus statement type %d", r -> op); |
447 | break; | |
055fa312 | 448 | } |
a75826c6 TL |
449 | executable_statement_dereference (&r, MDL); |
450 | if (next) { | |
451 | executable_statement_reference (&r, next, MDL); | |
452 | executable_statement_dereference (&next, MDL); | |
453 | } | |
055fa312 TL |
454 | } |
455 | ||
456 | return 1; | |
457 | } | |
458 | ||
459 | /* Execute all the statements in a particular scope, and all statements in | |
460 | scopes outer from that scope, but if a particular limiting scope is | |
461 | reached, do not execute statements in that scope or in scopes outer | |
8e7a5c42 TL |
462 | from it. More specific scopes need to take precedence over less |
463 | specific scopes, so we recursively traverse the scope list, executing | |
464 | the most outer scope first. */ | |
055fa312 | 465 | |
63b4fbf9 TL |
466 | void execute_statements_in_scope (result, packet, |
467 | lease, client_state, in_options, out_options, | |
468 | scope, group, limiting_group) | |
1b234d44 | 469 | struct binding_value **result; |
055fa312 | 470 | struct packet *packet; |
da38df14 | 471 | struct lease *lease; |
63b4fbf9 | 472 | struct client_state *client_state; |
8e7a5c42 TL |
473 | struct option_state *in_options; |
474 | struct option_state *out_options; | |
6ceb9118 | 475 | struct binding_scope **scope; |
055fa312 TL |
476 | struct group *group; |
477 | struct group *limiting_group; | |
478 | { | |
74f45f96 TL |
479 | struct group *limit; |
480 | ||
195ce2f8 TL |
481 | /* If we've recursed as far as we can, return. */ |
482 | if (!group) | |
483 | return; | |
484 | ||
74f45f96 TL |
485 | /* As soon as we get to a scope that is outer than the limiting |
486 | scope, we are done. This is so that if somebody does something | |
487 | like this, it does the expected thing: | |
488 | ||
489 | domain-name "fugue.com"; | |
490 | shared-network FOO { | |
491 | host bar { | |
492 | domain-name "othello.fugue.com"; | |
493 | fixed-address 10.20.30.40; | |
494 | } | |
495 | subnet 10.20.30.0 netmask 255.255.255.0 { | |
496 | domain-name "manhattan.fugue.com"; | |
497 | } | |
498 | } | |
499 | ||
500 | The problem with the above arrangement is that the host's | |
501 | group nesting will be host -> shared-network -> top-level, | |
502 | and the limiting scope when we evaluate the host's scope | |
503 | will be the subnet -> shared-network -> top-level, so we need | |
504 | to know when we evaluate the host's scope to stop before we | |
505 | evaluate the shared-networks scope, because it's outer than | |
506 | the limiting scope, which means we've already evaluated it. */ | |
507 | ||
508 | for (limit = limiting_group; limit; limit = limit -> next) { | |
509 | if (group == limit) | |
510 | return; | |
511 | } | |
055fa312 | 512 | |
8e7a5c42 | 513 | if (group -> next) |
63b4fbf9 TL |
514 | execute_statements_in_scope (result, packet, |
515 | lease, client_state, | |
f6a30e8d | 516 | in_options, out_options, scope, |
8e7a5c42 | 517 | group -> next, limiting_group); |
63b4fbf9 TL |
518 | execute_statements (result, packet, lease, client_state, in_options, |
519 | out_options, scope, group -> statements); | |
055fa312 | 520 | } |
d938be7b TL |
521 | |
522 | /* Dereference or free any subexpressions of a statement being freed. */ | |
523 | ||
4bd8800e | 524 | int executable_statement_dereference (ptr, file, line) |
d938be7b | 525 | struct executable_statement **ptr; |
4bd8800e TL |
526 | const char *file; |
527 | int line; | |
d938be7b TL |
528 | { |
529 | struct executable_statement *bp; | |
530 | ||
531 | if (!ptr || !*ptr) { | |
4bd8800e | 532 | log_error ("%s(%d): null pointer", file, line); |
d938be7b TL |
533 | #if defined (POINTER_DEBUG) |
534 | abort (); | |
535 | #else | |
536 | return 0; | |
537 | #endif | |
538 | } | |
539 | ||
540 | (*ptr) -> refcnt--; | |
98311e4b | 541 | rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC); |
f6a30e8d | 542 | if ((*ptr) -> refcnt > 0) { |
d938be7b TL |
543 | *ptr = (struct executable_statement *)0; |
544 | return 1; | |
545 | } | |
546 | ||
f6a30e8d | 547 | if ((*ptr) -> refcnt < 0) { |
4bd8800e | 548 | log_error ("%s(%d): negative refcnt!", file, line); |
f6a30e8d | 549 | #if defined (DEBUG_RC_HISTORY) |
d758ad8c | 550 | dump_rc_history (*ptr); |
f6a30e8d TL |
551 | #endif |
552 | #if defined (POINTER_DEBUG) | |
553 | abort (); | |
554 | #else | |
555 | return 0; | |
556 | #endif | |
557 | } | |
558 | ||
d938be7b | 559 | if ((*ptr) -> next) |
4bd8800e | 560 | executable_statement_dereference (&(*ptr) -> next, file, line); |
d938be7b TL |
561 | |
562 | switch ((*ptr) -> op) { | |
563 | case statements_statement: | |
ba6cb47a TL |
564 | if ((*ptr) -> data.statements) |
565 | executable_statement_dereference | |
4bd8800e | 566 | (&(*ptr) -> data.statements, file, line); |
d938be7b TL |
567 | break; |
568 | ||
569 | case on_statement: | |
ba6cb47a TL |
570 | if ((*ptr) -> data.on.statements) |
571 | executable_statement_dereference | |
4bd8800e | 572 | (&(*ptr) -> data.on.statements, file, line); |
d938be7b TL |
573 | break; |
574 | ||
91c82d9f TL |
575 | case switch_statement: |
576 | if ((*ptr) -> data.s_switch.statements) | |
577 | executable_statement_dereference | |
4bd8800e | 578 | (&(*ptr) -> data.on.statements, file, line); |
91c82d9f TL |
579 | if ((*ptr) -> data.s_switch.expr) |
580 | expression_dereference (&(*ptr) -> data.s_switch.expr, | |
4bd8800e | 581 | file, line); |
91c82d9f TL |
582 | break; |
583 | ||
584 | case case_statement: | |
585 | if ((*ptr) -> data.s_switch.expr) | |
586 | expression_dereference (&(*ptr) -> data.c_case, | |
4bd8800e | 587 | file, line); |
91c82d9f TL |
588 | break; |
589 | ||
d938be7b | 590 | case if_statement: |
ba6cb47a | 591 | if ((*ptr) -> data.ie.expr) |
4bd8800e TL |
592 | expression_dereference (&(*ptr) -> data.ie.expr, |
593 | file, line); | |
d758ad8c | 594 | if ((*ptr) -> data.ie.tc) |
ba6cb47a | 595 | executable_statement_dereference |
d758ad8c TL |
596 | (&(*ptr) -> data.ie.tc, file, line); |
597 | if ((*ptr) -> data.ie.fc) | |
d938be7b | 598 | executable_statement_dereference |
d758ad8c | 599 | (&(*ptr) -> data.ie.fc, file, line); |
d938be7b TL |
600 | break; |
601 | ||
602 | case eval_statement: | |
ba6cb47a | 603 | if ((*ptr) -> data.eval) |
4bd8800e TL |
604 | expression_dereference (&(*ptr) -> data.eval, |
605 | file, line); | |
d938be7b TL |
606 | break; |
607 | ||
1b234d44 DN |
608 | case return_statement: |
609 | if ((*ptr) -> data.eval) | |
610 | expression_dereference (&(*ptr) -> data.eval, | |
611 | file, line); | |
612 | break; | |
613 | ||
91c82d9f TL |
614 | case set_statement: |
615 | if ((*ptr)->data.set.name) | |
4bd8800e | 616 | dfree ((*ptr)->data.set.name, file, line); |
91c82d9f TL |
617 | if ((*ptr)->data.set.expr) |
618 | expression_dereference (&(*ptr) -> data.set.expr, | |
4bd8800e | 619 | file, line); |
91c82d9f TL |
620 | break; |
621 | ||
f6a30e8d TL |
622 | case unset_statement: |
623 | if ((*ptr)->data.unset) | |
4bd8800e | 624 | dfree ((*ptr)->data.unset, file, line); |
f6a30e8d TL |
625 | break; |
626 | ||
d938be7b | 627 | case supersede_option_statement: |
33454222 | 628 | case send_option_statement: |
d938be7b TL |
629 | case default_option_statement: |
630 | case append_option_statement: | |
631 | case prepend_option_statement: | |
ba6cb47a TL |
632 | if ((*ptr) -> data.option) |
633 | option_cache_dereference (&(*ptr) -> data.option, | |
4bd8800e | 634 | file, line); |
d938be7b TL |
635 | break; |
636 | ||
637 | default: | |
638 | /* Nothing to do. */ | |
8a6dfe4c | 639 | break; |
d938be7b TL |
640 | } |
641 | ||
4bd8800e | 642 | dfree ((*ptr), file, line); |
d938be7b TL |
643 | *ptr = (struct executable_statement *)0; |
644 | return 1; | |
645 | } | |
fa098be8 TL |
646 | |
647 | void write_statements (file, statements, indent) | |
648 | FILE *file; | |
649 | struct executable_statement *statements; | |
650 | int indent; | |
651 | { | |
652 | struct executable_statement *r, *x; | |
653 | int result; | |
654 | int status; | |
b1b7b521 | 655 | const char *s, *t, *dot; |
fa098be8 TL |
656 | int col; |
657 | ||
658 | if (!statements) | |
659 | return; | |
660 | ||
661 | for (r = statements; r; r = r -> next) { | |
662 | switch (r -> op) { | |
663 | case statements_statement: | |
664 | write_statements (file, r -> data.statements, indent); | |
665 | break; | |
666 | ||
667 | case on_statement: | |
fa098be8 | 668 | indent_spaces (file, indent); |
91c82d9f TL |
669 | fprintf (file, "on "); |
670 | s = ""; | |
671 | if (r -> data.on.evtypes & ON_EXPIRY) { | |
f6a30e8d TL |
672 | fprintf (file, "%sexpiry", s); |
673 | s = " or "; | |
91c82d9f TL |
674 | } |
675 | if (r -> data.on.evtypes & ON_COMMIT) { | |
f6a30e8d | 676 | fprintf (file, "%scommit", s); |
91c82d9f TL |
677 | s = "or"; |
678 | } | |
679 | if (r -> data.on.evtypes & ON_RELEASE) { | |
f6a30e8d | 680 | fprintf (file, "%srelease", s); |
91c82d9f TL |
681 | s = "or"; |
682 | } | |
f6a30e8d TL |
683 | if (r -> data.on.statements) { |
684 | fprintf (file, " {"); | |
685 | write_statements (file, | |
686 | r -> data.on.statements, | |
687 | indent + 2); | |
688 | indent_spaces (file, indent); | |
689 | fprintf (file, "}"); | |
690 | } else { | |
691 | fprintf (file, ";"); | |
692 | } | |
fa098be8 TL |
693 | break; |
694 | ||
91c82d9f TL |
695 | case switch_statement: |
696 | indent_spaces (file, indent); | |
697 | fprintf (file, "switch ("); | |
698 | col = write_expression (file, | |
699 | r -> data.s_switch.expr, | |
700 | indent + 7, indent + 7, 1); | |
701 | col = token_print_indent (file, col, indent + 7, | |
702 | "", "", ")"); | |
703 | token_print_indent (file, | |
704 | col, indent, " ", "", "{"); | |
705 | write_statements (file, r -> data.s_switch.statements, | |
706 | indent + 2); | |
707 | indent_spaces (file, indent); | |
708 | fprintf (file, "}"); | |
709 | break; | |
710 | ||
711 | case case_statement: | |
712 | indent_spaces (file, indent - 1); | |
713 | fprintf (file, "case "); | |
714 | col = write_expression (file, | |
715 | r -> data.s_switch.expr, | |
716 | indent + 5, indent + 5, 1); | |
717 | token_print_indent (file, col, indent + 5, | |
718 | "", "", ":"); | |
719 | break; | |
720 | ||
721 | case default_statement: | |
722 | indent_spaces (file, indent - 1); | |
723 | fprintf (file, "default: "); | |
724 | break; | |
725 | ||
fa098be8 TL |
726 | case if_statement: |
727 | indent_spaces (file, indent); | |
728 | fprintf (file, "if "); | |
729 | x = r; | |
730 | col = write_expression (file, | |
731 | x -> data.ie.expr, | |
91c82d9f | 732 | indent + 3, indent + 3, 1); |
fa098be8 TL |
733 | else_if: |
734 | token_print_indent (file, col, indent, " ", "", "{"); | |
d758ad8c TL |
735 | write_statements (file, x -> data.ie.tc, indent + 2); |
736 | if (x -> data.ie.fc && | |
737 | x -> data.ie.fc -> op == if_statement && | |
738 | !x -> data.ie.fc -> next) { | |
fa098be8 TL |
739 | indent_spaces (file, indent); |
740 | fprintf (file, "} elsif "); | |
d758ad8c | 741 | x = x -> data.ie.fc; |
fa098be8 TL |
742 | col = write_expression (file, |
743 | x -> data.ie.expr, | |
744 | indent + 6, | |
91c82d9f | 745 | indent + 6, 1); |
fa098be8 TL |
746 | goto else_if; |
747 | } | |
d758ad8c | 748 | if (x -> data.ie.fc) { |
fa098be8 TL |
749 | indent_spaces (file, indent); |
750 | fprintf (file, "} else {"); | |
d758ad8c | 751 | write_statements (file, x -> data.ie.fc, |
fa098be8 TL |
752 | indent + 2); |
753 | } | |
754 | indent_spaces (file, indent); | |
755 | fprintf (file, "}"); | |
756 | break; | |
757 | ||
758 | case eval_statement: | |
759 | indent_spaces (file, indent); | |
760 | fprintf (file, "eval "); | |
761 | col = write_expression (file, r -> data.eval, | |
91c82d9f | 762 | indent + 5, indent + 5, 1); |
fa098be8 TL |
763 | fprintf (file, ";"); |
764 | break; | |
765 | ||
1b234d44 DN |
766 | case return_statement: |
767 | indent_spaces (file, indent); | |
768 | fprintf (file, "return;"); | |
769 | break; | |
770 | ||
fa098be8 TL |
771 | case add_statement: |
772 | indent_spaces (file, indent); | |
773 | fprintf (file, "add \"%s\"", r -> data.add -> name); | |
774 | break; | |
775 | ||
776 | case break_statement: | |
777 | indent_spaces (file, indent); | |
778 | fprintf (file, "break;"); | |
779 | break; | |
780 | ||
781 | case supersede_option_statement: | |
33454222 | 782 | case send_option_statement: |
fa098be8 TL |
783 | s = "supersede"; |
784 | goto option_statement; | |
785 | ||
786 | case default_option_statement: | |
787 | s = "default"; | |
788 | goto option_statement; | |
789 | ||
790 | case append_option_statement: | |
791 | s = "append"; | |
792 | goto option_statement; | |
793 | ||
794 | case prepend_option_statement: | |
795 | s = "prepend"; | |
796 | option_statement: | |
797 | /* Note: the reason we don't try to pretty print | |
798 | the option here is that the format of the option | |
799 | may change in dhcpd.conf, and then when this | |
800 | statement was read back, it would cause a syntax | |
801 | error. */ | |
802 | if (r -> data.option -> option -> universe == | |
803 | &dhcp_universe) { | |
c9b01dcf | 804 | t = ""; |
fa098be8 TL |
805 | dot = ""; |
806 | } else { | |
807 | t = (r -> data.option -> option -> | |
808 | universe -> name); | |
809 | dot = "."; | |
810 | } | |
811 | indent_spaces (file, indent); | |
812 | fprintf (file, "%s %s%s%s = ", s, t, dot, | |
813 | r -> data.option -> option -> name); | |
814 | col = (indent + strlen (s) + strlen (t) + | |
815 | strlen (dot) + strlen (r -> data.option -> | |
816 | option -> name) + 4); | |
817 | if (r -> data.option -> expression) | |
818 | write_expression | |
819 | (file, | |
820 | r -> data.option -> expression, | |
91c82d9f | 821 | col, indent + 8, 1); |
fa098be8 TL |
822 | else |
823 | token_indent_data_string | |
824 | (file, col, indent + 8, "", "", | |
825 | &r -> data.option -> data); | |
826 | ||
827 | fprintf (file, ";"); /* XXX */ | |
828 | break; | |
829 | ||
91c82d9f TL |
830 | case set_statement: |
831 | indent_spaces (file, indent); | |
832 | fprintf (file, "set "); | |
833 | col = token_print_indent (file, indent + 4, indent + 4, | |
834 | "", "", r -> data.set.name); | |
835 | col = token_print_indent (file, col, indent + 4, | |
836 | " ", " ", "="); | |
837 | col = write_expression (file, r -> data.set.expr, | |
838 | indent + 3, indent + 3, 0); | |
839 | col = token_print_indent (file, col, indent + 4, | |
840 | " ", "", ";"); | |
841 | break; | |
842 | ||
f6a30e8d TL |
843 | case unset_statement: |
844 | indent_spaces (file, indent); | |
845 | fprintf (file, "unset "); | |
846 | col = token_print_indent (file, indent + 6, indent + 6, | |
847 | "", "", r -> data.set.name); | |
848 | col = token_print_indent (file, col, indent + 6, | |
849 | " ", "", ";"); | |
850 | break; | |
81beacfb DN |
851 | |
852 | case log_statement: | |
853 | indent_spaces (file, indent); | |
854 | fprintf (file, "log "); | |
98311e4b | 855 | col = token_print_indent (file, indent + 4, indent + 4, |
81beacfb DN |
856 | "", "", "("); |
857 | switch (r -> data.log.priority) { | |
858 | case log_priority_fatal: | |
859 | col = token_print_indent | |
860 | (file, col, indent + 4, "", | |
861 | " ", "fatal,"); | |
862 | break; | |
863 | case log_priority_error: | |
864 | col = token_print_indent | |
865 | (file, col, indent + 4, "", | |
866 | " ", "error,"); | |
867 | break; | |
868 | case log_priority_debug: | |
869 | col = token_print_indent | |
870 | (file, col, indent + 4, "", | |
871 | " ", "debug,"); | |
872 | break; | |
873 | case log_priority_info: | |
874 | col = token_print_indent | |
875 | (file, col, indent + 4, "", | |
876 | " ", "info,"); | |
877 | break; | |
878 | } | |
879 | col = write_expression (file, r -> data.log.expr, | |
880 | indent + 4, indent + 4, 0); | |
881 | col = token_print_indent (file, col, indent + 4, | |
882 | "", "", ");"); | |
883 | ||
884 | break; | |
f6a30e8d | 885 | |
fa098be8 TL |
886 | default: |
887 | log_fatal ("bogus statement type %d\n", r -> op); | |
888 | } | |
889 | } | |
890 | } | |
91c82d9f TL |
891 | |
892 | /* Find a case statement in the sequence of executable statements that | |
893 | matches the expression, and if found, return the following statement. | |
894 | If no case statement matches, try to find a default statement and | |
895 | return that (the default statement can precede all the case statements). | |
896 | Otherwise, return the null statement. */ | |
897 | ||
a75826c6 TL |
898 | int find_matching_case (struct executable_statement **ep, |
899 | struct packet *packet, struct lease *lease, | |
63b4fbf9 | 900 | struct client_state *client_state, |
a75826c6 TL |
901 | struct option_state *in_options, |
902 | struct option_state *out_options, | |
6ceb9118 | 903 | struct binding_scope **scope, |
a75826c6 TL |
904 | struct expression *expr, |
905 | struct executable_statement *stmt) | |
91c82d9f TL |
906 | { |
907 | int status, sub; | |
908 | struct executable_statement *s; | |
909 | unsigned long foo; | |
910 | ||
911 | if (is_data_expression (expr)) { | |
912 | struct executable_statement *e; | |
913 | struct data_string cd, ds; | |
914 | memset (&ds, 0, sizeof ds); | |
915 | memset (&cd, 0, sizeof cd); | |
916 | ||
f6a30e8d | 917 | status = (evaluate_data_expression (&ds, packet, lease, |
63b4fbf9 | 918 | client_state, in_options, |
d758ad8c TL |
919 | out_options, scope, expr, |
920 | MDL)); | |
91c82d9f TL |
921 | if (status) { |
922 | for (s = stmt; s; s = s -> next) { | |
923 | if (s -> op == case_statement) { | |
924 | sub = (evaluate_data_expression | |
63b4fbf9 TL |
925 | (&cd, packet, lease, client_state, |
926 | in_options, out_options, | |
d758ad8c | 927 | scope, s -> data.c_case, MDL)); |
91c82d9f TL |
928 | if (sub && cd.len == ds.len && |
929 | !memcmp (cd.data, ds.data, cd.len)) | |
930 | { | |
4bd8800e TL |
931 | data_string_forget (&cd, MDL); |
932 | data_string_forget (&ds, MDL); | |
a75826c6 TL |
933 | executable_statement_reference |
934 | (ep, s -> next, MDL); | |
935 | return 1; | |
91c82d9f | 936 | } |
4bd8800e | 937 | data_string_forget (&cd, MDL); |
91c82d9f TL |
938 | } |
939 | } | |
4bd8800e | 940 | data_string_forget (&ds, MDL); |
91c82d9f TL |
941 | } |
942 | } else { | |
943 | unsigned long n, c; | |
f6a30e8d | 944 | status = evaluate_numeric_expression (&n, packet, lease, |
63b4fbf9 | 945 | client_state, |
f6a30e8d TL |
946 | in_options, out_options, |
947 | scope, expr); | |
91c82d9f TL |
948 | |
949 | if (status) { | |
950 | for (s = stmt; s; s = s -> next) { | |
951 | if (s -> op == case_statement) { | |
952 | sub = (evaluate_numeric_expression | |
63b4fbf9 TL |
953 | (&c, packet, lease, client_state, |
954 | in_options, out_options, | |
955 | scope, s -> data.c_case)); | |
a75826c6 TL |
956 | if (sub && n == c) { |
957 | executable_statement_reference | |
958 | (ep, s -> next, MDL); | |
959 | return 1; | |
960 | } | |
91c82d9f TL |
961 | } |
962 | } | |
963 | } | |
964 | } | |
965 | ||
966 | /* If we didn't find a matching case statement, look for a default | |
967 | statement and return the statement following it. */ | |
968 | for (s = stmt; s; s = s -> next) | |
969 | if (s -> op == default_statement) | |
970 | break; | |
a75826c6 TL |
971 | if (s) { |
972 | executable_statement_reference (ep, s -> next, MDL); | |
973 | return 1; | |
974 | } | |
975 | return 0; | |
91c82d9f | 976 | } |
d758ad8c TL |
977 | |
978 | int executable_statement_foreach (struct executable_statement *stmt, | |
979 | int (*callback) (struct | |
980 | executable_statement *, | |
981 | void *, int), | |
982 | void *vp, int condp) | |
983 | { | |
984 | struct executable_statement *foo; | |
985 | int ok = 0; | |
986 | int result; | |
987 | ||
988 | for (foo = stmt; foo; foo = foo -> next) { | |
989 | if ((*callback) (foo, vp, condp) != 0) | |
990 | ok = 1; | |
991 | switch (foo -> op) { | |
992 | case null_statement: | |
993 | break; | |
994 | case if_statement: | |
a609e69b | 995 | if (executable_statement_foreach (foo -> data.ie.tc, |
d758ad8c TL |
996 | callback, vp, 1)) |
997 | ok = 1; | |
a609e69b | 998 | if (executable_statement_foreach (foo -> data.ie.fc, |
d758ad8c TL |
999 | callback, vp, 1)) |
1000 | ok = 1; | |
1001 | break; | |
1002 | case add_statement: | |
1003 | break; | |
1004 | case eval_statement: | |
1005 | break; | |
1006 | case break_statement: | |
1007 | break; | |
1008 | case default_option_statement: | |
1009 | break; | |
1010 | case supersede_option_statement: | |
1011 | break; | |
1012 | case append_option_statement: | |
1013 | break; | |
1014 | case prepend_option_statement: | |
1015 | break; | |
1016 | case send_option_statement: | |
1017 | break; | |
1018 | case statements_statement: | |
1019 | if ((executable_statement_foreach | |
a609e69b | 1020 | (foo -> data.statements, callback, vp, condp))) |
d758ad8c TL |
1021 | ok = 1; |
1022 | break; | |
1023 | case on_statement: | |
1024 | if ((executable_statement_foreach | |
a609e69b | 1025 | (foo -> data.on.statements, callback, vp, 1))) |
d758ad8c TL |
1026 | ok = 1; |
1027 | break; | |
1028 | case switch_statement: | |
1029 | if ((executable_statement_foreach | |
a609e69b | 1030 | (foo -> data.s_switch.statements, callback, vp, 1))) |
d758ad8c TL |
1031 | ok = 1; |
1032 | break; | |
1033 | case case_statement: | |
1034 | break; | |
1035 | case default_statement: | |
1036 | break; | |
1037 | case set_statement: | |
1038 | break; | |
1039 | case unset_statement: | |
1040 | break; | |
1041 | case let_statement: | |
1042 | if ((executable_statement_foreach | |
a609e69b | 1043 | (foo -> data.let.statements, callback, vp, 0))) |
d758ad8c TL |
1044 | ok = 1; |
1045 | break; | |
1046 | case define_statement: | |
1047 | break; | |
1048 | case log_statement: | |
1049 | case return_statement: | |
1050 | break; | |
1051 | } | |
1052 | } | |
1053 | return ok; | |
1054 | } |