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