File: | src/usr.sbin/ifstated/ifstated.c |
Warning: | line 133, column 2 Value stored to 'argv' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: ifstated.c,v 1.66 2021/07/12 15:09:21 beck Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2004 Marco Pfatschbacher <mpf@openbsd.org> |
5 | * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | /* |
21 | * ifstated listens to link_state transitions on interfaces |
22 | * and executes predefined commands. |
23 | */ |
24 | |
25 | #include <sys/types.h> |
26 | #include <sys/time.h> |
27 | #include <sys/socket.h> |
28 | #include <sys/wait.h> |
29 | |
30 | #include <net/if.h> |
31 | #include <net/route.h> |
32 | #include <netinet/in.h> |
33 | |
34 | #include <paths.h> |
35 | #include <stdio.h> |
36 | #include <stdlib.h> |
37 | #include <string.h> |
38 | #include <signal.h> |
39 | #include <stdint.h> |
40 | #include <syslog.h> |
41 | #include <errno(*__errno()).h> |
42 | #include <event.h> |
43 | #include <unistd.h> |
44 | #include <ifaddrs.h> |
45 | |
46 | #include "ifstated.h" |
47 | #include "log.h" |
48 | |
49 | struct ifsd_config *conf, *newconf; |
50 | |
51 | int opts; |
52 | int opt_inhibit; |
53 | char *configfile = "/etc/ifstated.conf"; |
54 | struct event rt_msg_ev, sighup_ev, startup_ev, sigchld_ev; |
55 | |
56 | void startup_handler(int, short, void *); |
57 | void sighup_handler(int, short, void *); |
58 | int load_config(void); |
59 | void sigchld_handler(int, short, void *); |
60 | void rt_msg_handler(int, short, void *); |
61 | void external_handler(int, short, void *); |
62 | void external_exec(struct ifsd_external *, int); |
63 | void check_external_status(struct ifsd_state *); |
64 | void check_ifdeparture(void); |
65 | void external_evtimer_setup(struct ifsd_state *, int); |
66 | void scan_ifstate(const char *, int, int); |
67 | int scan_ifstate_single(const char *, int, struct ifsd_state *); |
68 | void fetch_ifstate(int); |
69 | __dead__attribute__((__noreturn__)) void usage(void); |
70 | void adjust_expressions(struct ifsd_expression_list *, int); |
71 | void adjust_external_expressions(struct ifsd_state *); |
72 | void eval_state(struct ifsd_state *); |
73 | int state_change(void); |
74 | void do_action(struct ifsd_action *); |
75 | void remove_action(struct ifsd_action *, struct ifsd_state *); |
76 | void remove_expression(struct ifsd_expression *, |
77 | struct ifsd_state *); |
78 | |
79 | __dead__attribute__((__noreturn__)) void |
80 | usage(void) |
81 | { |
82 | extern char *__progname; |
83 | |
84 | fprintf(stderr(&__sF[2]), "usage: %s [-dhinv] [-D macro=value] [-f file]\n", |
85 | __progname); |
86 | exit(1); |
87 | } |
88 | |
89 | int |
90 | main(int argc, char *argv[]) |
91 | { |
92 | struct timeval tv; |
93 | int ch, rt_fd; |
94 | int debug = 0; |
95 | unsigned int rtfilter; |
96 | |
97 | log_init(1, LOG_DAEMON(3<<3)); /* log to stderr until daemonized */ |
98 | log_setverbose(1); |
99 | |
100 | while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) { |
101 | switch (ch) { |
102 | case 'd': |
103 | debug = 1; |
104 | break; |
105 | case 'D': |
106 | if (cmdline_symset(optarg) < 0) |
107 | fatalx("could not parse macro definition %s", |
108 | optarg); |
109 | break; |
110 | case 'f': |
111 | configfile = optarg; |
112 | break; |
113 | case 'h': |
114 | usage(); |
115 | break; |
116 | case 'n': |
117 | opts |= IFSD_OPT_NOACTION0x00000004; |
118 | break; |
119 | case 'i': |
120 | opt_inhibit = 1; |
121 | break; |
122 | case 'v': |
123 | if (opts & IFSD_OPT_VERBOSE0x00000001) |
124 | opts |= IFSD_OPT_VERBOSE20x00000002; |
125 | opts |= IFSD_OPT_VERBOSE0x00000001; |
126 | break; |
127 | default: |
128 | usage(); |
129 | } |
130 | } |
131 | |
132 | argc -= optind; |
133 | argv += optind; |
Value stored to 'argv' is never read | |
134 | if (argc > 0) |
135 | usage(); |
136 | |
137 | if (opts & IFSD_OPT_NOACTION0x00000004) { |
138 | if ((newconf = parse_config(configfile, opts)) == NULL((void *)0)) |
139 | exit(1); |
140 | fprintf(stderr(&__sF[2]), "configuration OK\n"); |
141 | exit(0); |
142 | } |
143 | |
144 | if (!debug) |
145 | daemon(1, 0); |
146 | |
147 | event_init(); |
148 | log_init(debug, LOG_DAEMON(3<<3)); |
149 | log_setverbose(opts & IFSD_OPT_VERBOSE0x00000001); |
150 | |
151 | if ((rt_fd = socket(AF_ROUTE17, SOCK_RAW3, 0)) == -1) |
152 | fatal("no routing socket"); |
153 | |
154 | rtfilter = ROUTE_FILTER(RTM_IFINFO)(1 << (0xe)) | ROUTE_FILTER(RTM_IFANNOUNCE)(1 << (0xf)); |
155 | if (setsockopt(rt_fd, AF_ROUTE17, ROUTE_MSGFILTER1, |
156 | &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ |
157 | log_warn("%s: setsockopt msgfilter", __func__); |
158 | |
159 | rtfilter = RTABLE_ANY0xffffffff; |
160 | if (setsockopt(rt_fd, AF_ROUTE17, ROUTE_TABLEFILTER2, |
161 | &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ |
162 | log_warn("%s: setsockopt tablefilter", __func__); |
163 | |
164 | if (unveil(configfile, "r") == -1) |
165 | fatal("unveil %s", configfile); |
166 | if (unveil(_PATH_BSHELL"/bin/sh", "x") == -1) |
167 | fatal("unveil %s", _PATH_BSHELL"/bin/sh"); |
168 | if (pledge("stdio rpath route proc exec", NULL((void *)0)) == -1) |
169 | fatal("pledge"); |
170 | |
171 | signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL)event_set(&sigchld_ev, 20, 0x08|0x10, sigchld_handler, (( void *)0)); |
172 | signal_add(&sigchld_ev, NULL)event_add(&sigchld_ev, ((void *)0)); |
173 | |
174 | /* Loading the config needs to happen in the event loop */ |
175 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
176 | evtimer_set(&startup_ev, startup_handler, (void *)(long)rt_fd)event_set(&startup_ev, -1, 0, startup_handler, (void *)(long )rt_fd); |
177 | evtimer_add(&startup_ev, &tv)event_add(&startup_ev, &tv); |
178 | |
179 | event_loop(0); |
180 | exit(0); |
181 | } |
182 | |
183 | void |
184 | startup_handler(int fd, short event, void *arg) |
185 | { |
186 | int rfd = (int)(long)arg; |
187 | |
188 | if (load_config() != 0) { |
189 | log_warnx("unable to load config"); |
190 | exit(1); |
191 | } |
192 | |
193 | event_set(&rt_msg_ev, rfd, EV_READ0x02|EV_PERSIST0x10, rt_msg_handler, NULL((void *)0)); |
194 | event_add(&rt_msg_ev, NULL((void *)0)); |
195 | |
196 | signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL)event_set(&sighup_ev, 1, 0x08|0x10, sighup_handler, ((void *)0)); |
197 | signal_add(&sighup_ev, NULL)event_add(&sighup_ev, ((void *)0)); |
198 | |
199 | log_info("started"); |
200 | } |
201 | |
202 | void |
203 | sighup_handler(int fd, short event, void *arg) |
204 | { |
205 | log_info("reloading config"); |
206 | if (load_config() != 0) |
207 | log_warnx("unable to reload config"); |
208 | } |
209 | |
210 | int |
211 | load_config(void) |
212 | { |
213 | if ((newconf = parse_config(configfile, opts)) == NULL((void *)0)) |
214 | return (-1); |
215 | if (conf != NULL((void *)0)) |
216 | clear_config(conf); |
217 | conf = newconf; |
218 | conf->initstate.entered = time(NULL((void *)0)); |
219 | fetch_ifstate(0); |
220 | external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_ADD); |
221 | adjust_external_expressions(&conf->initstate); |
222 | eval_state(&conf->initstate); |
223 | if (conf->curstate != NULL((void *)0)) { |
224 | log_info("initial state: %s", conf->curstate->name); |
225 | conf->curstate->entered = time(NULL((void *)0)); |
226 | conf->nextstate = conf->curstate; |
227 | conf->curstate = NULL((void *)0); |
228 | while (state_change()) { |
229 | do_action(conf->curstate->init); |
230 | do_action(conf->curstate->body); |
231 | } |
232 | } |
233 | return (0); |
234 | } |
235 | |
236 | void |
237 | rt_msg_handler(int fd, short event, void *arg) |
238 | { |
239 | char msg[2048]; |
240 | struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; |
241 | struct if_msghdr ifm; |
242 | struct if_announcemsghdr ifan; |
243 | char ifnamebuf[IFNAMSIZ16]; |
244 | char *ifname; |
245 | ssize_t len; |
246 | |
247 | if ((len = read(fd, msg, sizeof(msg))) == -1) { |
248 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4) |
249 | return; |
250 | fatal("%s: routing socket read error", __func__); |
251 | } |
252 | |
253 | if (len == 0) |
254 | fatal("%s: routing socket closed", __func__); |
255 | |
256 | if (rtm->rtm_version != RTM_VERSION5) |
257 | return; |
258 | |
259 | switch (rtm->rtm_type) { |
260 | case RTM_IFINFO0xe: |
261 | memcpy(&ifm, rtm, sizeof(ifm)); |
262 | ifname = if_indextoname(ifm.ifm_index, ifnamebuf); |
263 | /* ifname is NULL on interface departure */ |
264 | if (ifname != NULL((void *)0)) |
265 | scan_ifstate(ifname, ifm.ifm_data.ifi_link_state, 1); |
266 | break; |
267 | case RTM_IFANNOUNCE0xf: |
268 | memcpy(&ifan, rtm, sizeof(ifan)); |
269 | switch (ifan.ifan_what) { |
270 | case IFAN_DEPARTURE1: |
271 | log_warnx("interface %s departed", ifan.ifan_name); |
272 | check_ifdeparture(); |
273 | break; |
274 | case IFAN_ARRIVAL0: |
275 | log_warnx("interface %s arrived", ifan.ifan_name); |
276 | fetch_ifstate(1); |
277 | break; |
278 | } |
279 | break; |
280 | case RTM_DESYNC0x10: |
281 | /* we lost some routing messages so rescan interfaces */ |
282 | check_ifdeparture(); |
283 | fetch_ifstate(1); |
284 | break; |
285 | } |
286 | return; |
287 | } |
288 | |
289 | void |
290 | sigchld_handler(int fd, short event, void *arg) |
291 | { |
292 | check_external_status(&conf->initstate); |
293 | if (conf->curstate != NULL((void *)0)) |
294 | check_external_status(conf->curstate); |
295 | } |
296 | |
297 | void |
298 | external_handler(int fd, short event, void *arg) |
299 | { |
300 | struct ifsd_external *external = (struct ifsd_external *)arg; |
301 | struct timeval tv; |
302 | |
303 | /* re-schedule */ |
304 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
305 | tv.tv_sec = external->frequency; |
306 | evtimer_set(&external->ev, external_handler, external)event_set(&external->ev, -1, 0, external_handler, external ); |
307 | evtimer_add(&external->ev, &tv)event_add(&external->ev, &tv); |
308 | |
309 | /* execute */ |
310 | external_exec(external, 1); |
311 | } |
312 | |
313 | void |
314 | external_exec(struct ifsd_external *external, int async) |
315 | { |
316 | char *argp[] = {"sh", "-c", NULL((void *)0), NULL((void *)0)}; |
317 | pid_t pid; |
318 | int s; |
319 | |
320 | if (external->pid > 0) { |
321 | log_debug("previous command %s [%d] still running, killing it", |
322 | external->command, external->pid); |
323 | kill(external->pid, SIGKILL9); |
324 | waitpid(external->pid, &s, 0); |
325 | external->pid = 0; |
326 | } |
327 | |
328 | argp[2] = external->command; |
329 | log_debug("running %s", external->command); |
330 | pid = fork(); |
331 | if (pid == -1) { |
332 | log_warn("fork error"); |
333 | } else if (pid == 0) { |
334 | execv(_PATH_BSHELL"/bin/sh", argp); |
335 | _exit(1); |
336 | /* NOTREACHED */ |
337 | } else { |
338 | external->pid = pid; |
339 | } |
340 | if (!async) { |
341 | waitpid(external->pid, &s, 0); |
342 | external->pid = 0; |
343 | if (WIFEXITED(s)(((s) & 0177) == 0)) |
344 | external->prevstatus = WEXITSTATUS(s)(int)(((unsigned)(s) >> 8) & 0xff); |
345 | } |
346 | } |
347 | |
348 | void |
349 | adjust_external_expressions(struct ifsd_state *state) |
350 | { |
351 | struct ifsd_external *external; |
352 | struct ifsd_expression_list expressions; |
353 | |
354 | TAILQ_INIT(&expressions)do { (&expressions)->tqh_first = ((void *)0); (&expressions )->tqh_last = &(&expressions)->tqh_first; } while (0); |
355 | TAILQ_FOREACH(external, &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
356 | struct ifsd_expression *expression; |
357 | |
358 | if (external->prevstatus == -1) |
359 | continue; |
360 | |
361 | TAILQ_FOREACH(expression, &external->expressions, entries)for((expression) = ((&external->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) { |
362 | TAILQ_INSERT_TAIL(&expressions,do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0) |
363 | expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0); |
364 | expression->truth = !external->prevstatus; |
365 | } |
366 | adjust_expressions(&expressions, conf->maxdepth); |
367 | } |
368 | } |
369 | |
370 | void |
371 | check_external_status(struct ifsd_state *state) |
372 | { |
373 | struct ifsd_external *external, *end = NULL((void *)0); |
374 | int status, s, changed = 0; |
375 | |
376 | /* Do this manually; change ordering so the oldest is first */ |
377 | external = TAILQ_FIRST(&state->external_tests)((&state->external_tests)->tqh_first); |
378 | while (external != NULL((void *)0) && external != end) { |
379 | struct ifsd_external *newexternal; |
380 | |
381 | newexternal = TAILQ_NEXT(external, entries)((external)->entries.tqe_next); |
382 | |
383 | if (external->pid <= 0) |
384 | goto loop; |
385 | |
386 | if (wait4(external->pid, &s, WNOHANG1, NULL((void *)0)) == 0) |
387 | goto loop; |
388 | |
389 | external->pid = 0; |
390 | if (end == NULL((void *)0)) |
391 | end = external; |
392 | if (WIFEXITED(s)(((s) & 0177) == 0)) |
393 | status = WEXITSTATUS(s)(int)(((unsigned)(s) >> 8) & 0xff); |
394 | else { |
395 | log_warnx("%s exited abnormally", external->command); |
396 | goto loop; |
397 | } |
398 | |
399 | if (external->prevstatus != status && |
400 | (external->prevstatus != -1 || !opt_inhibit)) { |
401 | changed = 1; |
402 | external->prevstatus = status; |
403 | } |
404 | external->lastexec = time(NULL((void *)0)); |
405 | TAILQ_REMOVE(&state->external_tests, external, entries)do { if (((external)->entries.tqe_next) != ((void *)0)) (external )->entries.tqe_next->entries.tqe_prev = (external)-> entries.tqe_prev; else (&state->external_tests)->tqh_last = (external)->entries.tqe_prev; *(external)->entries.tqe_prev = (external)->entries.tqe_next; ; ; } while (0); |
406 | TAILQ_INSERT_TAIL(&state->external_tests, external, entries)do { (external)->entries.tqe_next = ((void *)0); (external )->entries.tqe_prev = (&state->external_tests)-> tqh_last; *(&state->external_tests)->tqh_last = (external ); (&state->external_tests)->tqh_last = &(external )->entries.tqe_next; } while (0); |
407 | loop: |
408 | external = newexternal; |
409 | } |
410 | |
411 | if (changed) { |
412 | adjust_external_expressions(state); |
413 | eval_state(state); |
414 | } |
415 | } |
416 | |
417 | void |
418 | external_evtimer_setup(struct ifsd_state *state, int action) |
419 | { |
420 | struct ifsd_external *external; |
421 | int s; |
422 | |
423 | if (state != NULL((void *)0)) { |
424 | switch (action) { |
425 | case IFSD_EVTIMER_ADD: |
426 | TAILQ_FOREACH(external,for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) |
427 | &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
428 | struct timeval tv; |
429 | |
430 | /* run it once right away */ |
431 | external_exec(external, 0); |
432 | |
433 | /* schedule it for later */ |
434 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; |
435 | tv.tv_sec = external->frequency; |
436 | evtimer_set(&external->ev, external_handler,event_set(&external->ev, -1, 0, external_handler, external ) |
437 | external)event_set(&external->ev, -1, 0, external_handler, external ); |
438 | evtimer_add(&external->ev, &tv)event_add(&external->ev, &tv); |
439 | } |
440 | break; |
441 | case IFSD_EVTIMER_DEL: |
442 | TAILQ_FOREACH(external,for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) |
443 | &state->external_tests, entries)for((external) = ((&state->external_tests)->tqh_first ); (external) != ((void *)0); (external) = ((external)->entries .tqe_next)) { |
444 | if (external->pid > 0) { |
445 | kill(external->pid, SIGKILL9); |
446 | waitpid(external->pid, &s, 0); |
447 | external->pid = 0; |
448 | } |
449 | evtimer_del(&external->ev)event_del(&external->ev); |
450 | } |
451 | break; |
452 | } |
453 | } |
454 | } |
455 | |
456 | #define LINK_STATE_IS_DOWN(_s)(!(((_s)) >= 4 || ((_s)) == 0)) (!LINK_STATE_IS_UP((_s))(((_s)) >= 4 || ((_s)) == 0)) |
457 | |
458 | int |
459 | scan_ifstate_single(const char *ifname, int s, struct ifsd_state *state) |
460 | { |
461 | struct ifsd_ifstate *ifstate; |
462 | struct ifsd_expression_list expressions; |
463 | int changed = 0; |
464 | |
465 | TAILQ_INIT(&expressions)do { (&expressions)->tqh_first = ((void *)0); (&expressions )->tqh_last = &(&expressions)->tqh_first; } while (0); |
466 | |
467 | TAILQ_FOREACH(ifstate, &state->interface_states, entries)for((ifstate) = ((&state->interface_states)->tqh_first ); (ifstate) != ((void *)0); (ifstate) = ((ifstate)->entries .tqe_next)) { |
468 | if (strcmp(ifstate->ifname, ifname) == 0) { |
469 | if (ifstate->prevstate != s && |
470 | (ifstate->prevstate != -1 || !opt_inhibit)) { |
471 | struct ifsd_expression *expression; |
472 | int truth; |
473 | |
474 | truth = |
475 | (ifstate->ifstate == IFSD_LINKUNKNOWN0 && |
476 | s == LINK_STATE_UNKNOWN0) || |
477 | (ifstate->ifstate == IFSD_LINKDOWN1 && |
478 | LINK_STATE_IS_DOWN(s)(!(((s)) >= 4 || ((s)) == 0))) || |
479 | (ifstate->ifstate == IFSD_LINKUP2 && |
480 | LINK_STATE_IS_UP(s)((s) >= 4 || (s) == 0)); |
481 | |
482 | TAILQ_FOREACH(expression,for((expression) = ((&ifstate->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) |
483 | &ifstate->expressions, entries)for((expression) = ((&ifstate->expressions)->tqh_first ); (expression) != ((void *)0); (expression) = ((expression)-> entries.tqe_next)) { |
484 | expression->truth = truth; |
485 | TAILQ_INSERT_TAIL(&expressions,do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0) |
486 | expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&expressions)->tqh_last; *(& expressions)->tqh_last = (expression); (&expressions)-> tqh_last = &(expression)->eval.tqe_next; } while (0); |
487 | changed = 1; |
488 | } |
489 | ifstate->prevstate = s; |
490 | } |
491 | } |
492 | } |
493 | |
494 | if (changed) |
495 | adjust_expressions(&expressions, conf->maxdepth); |
496 | return (changed); |
497 | } |
498 | |
499 | void |
500 | scan_ifstate(const char *ifname, int s, int do_eval) |
501 | { |
502 | struct ifsd_state *state; |
503 | int cur_eval = 0; |
504 | |
505 | if (scan_ifstate_single(ifname, s, &conf->initstate) && do_eval) |
506 | eval_state(&conf->initstate); |
507 | TAILQ_FOREACH(state, &conf->states, entries)for((state) = ((&conf->states)->tqh_first); (state) != ((void *)0); (state) = ((state)->entries.tqe_next)) { |
508 | if (scan_ifstate_single(ifname, s, state) && |
509 | (do_eval && state == conf->curstate)) |
510 | cur_eval = 1; |
511 | } |
512 | /* execute actions _after_ all expressions have been adjusted */ |
513 | if (cur_eval) |
514 | eval_state(conf->curstate); |
515 | } |
516 | |
517 | /* |
518 | * Do a bottom-up ajustment of the expression tree's truth value, |
519 | * level-by-level to ensure that each expression's subexpressions have been |
520 | * evaluated. |
521 | */ |
522 | void |
523 | adjust_expressions(struct ifsd_expression_list *expressions, int depth) |
524 | { |
525 | struct ifsd_expression_list nexpressions; |
526 | struct ifsd_expression *expression; |
527 | |
528 | TAILQ_INIT(&nexpressions)do { (&nexpressions)->tqh_first = ((void *)0); (&nexpressions )->tqh_last = &(&nexpressions)->tqh_first; } while (0); |
529 | while ((expression = TAILQ_FIRST(expressions)((expressions)->tqh_first)) != NULL((void *)0)) { |
530 | TAILQ_REMOVE(expressions, expression, eval)do { if (((expression)->eval.tqe_next) != ((void *)0)) (expression )->eval.tqe_next->eval.tqe_prev = (expression)->eval .tqe_prev; else (expressions)->tqh_last = (expression)-> eval.tqe_prev; *(expression)->eval.tqe_prev = (expression) ->eval.tqe_next; ; ; } while (0); |
531 | if (expression->depth == depth) { |
532 | struct ifsd_expression *te; |
533 | |
534 | switch (expression->type) { |
535 | case IFSD_OPER_AND1: |
536 | expression->truth = expression->left->truth && |
537 | expression->right->truth; |
538 | break; |
539 | case IFSD_OPER_OR2: |
540 | expression->truth = expression->left->truth || |
541 | expression->right->truth; |
542 | break; |
543 | case IFSD_OPER_NOT3: |
544 | expression->truth = !expression->right->truth; |
545 | break; |
546 | default: |
547 | break; |
548 | } |
549 | if (expression->parent != NULL((void *)0)) { |
550 | if (TAILQ_EMPTY(&nexpressions)(((&nexpressions)->tqh_first) == ((void *)0))) |
551 | te = NULL((void *)0); |
552 | TAILQ_FOREACH(te, &nexpressions, eval)for((te) = ((&nexpressions)->tqh_first); (te) != ((void *)0); (te) = ((te)->eval.tqe_next)) |
553 | if (expression->parent == te) |
554 | break; |
555 | if (te == NULL((void *)0)) |
556 | TAILQ_INSERT_TAIL(&nexpressions,do { (expression->parent)->eval.tqe_next = ((void *)0); (expression->parent)->eval.tqe_prev = (&nexpressions )->tqh_last; *(&nexpressions)->tqh_last = (expression ->parent); (&nexpressions)->tqh_last = &(expression ->parent)->eval.tqe_next; } while (0) |
557 | expression->parent, eval)do { (expression->parent)->eval.tqe_next = ((void *)0); (expression->parent)->eval.tqe_prev = (&nexpressions )->tqh_last; *(&nexpressions)->tqh_last = (expression ->parent); (&nexpressions)->tqh_last = &(expression ->parent)->eval.tqe_next; } while (0); |
558 | } |
559 | } else |
560 | TAILQ_INSERT_TAIL(&nexpressions, expression, eval)do { (expression)->eval.tqe_next = ((void *)0); (expression )->eval.tqe_prev = (&nexpressions)->tqh_last; *(& nexpressions)->tqh_last = (expression); (&nexpressions )->tqh_last = &(expression)->eval.tqe_next; } while (0); |
561 | } |
562 | if (depth > 0) |
563 | adjust_expressions(&nexpressions, depth - 1); |
564 | } |
565 | |
566 | void |
567 | eval_state(struct ifsd_state *state) |
568 | { |
569 | struct ifsd_external *external; |
570 | |
571 | external = TAILQ_FIRST(&state->external_tests)((&state->external_tests)->tqh_first); |
572 | if (external == NULL((void *)0) || external->lastexec >= state->entered || |
573 | external->lastexec == 0) { |
574 | do_action(state->body); |
575 | while (state_change()) { |
576 | do_action(conf->curstate->init); |
577 | do_action(conf->curstate->body); |
578 | } |
579 | } |
580 | } |
581 | |
582 | int |
583 | state_change(void) |
584 | { |
585 | if (conf->nextstate != NULL((void *)0) && conf->curstate != conf->nextstate) { |
586 | log_info("changing state to %s", conf->nextstate->name); |
587 | if (conf->curstate != NULL((void *)0)) { |
588 | evtimer_del(&conf->curstate->ev)event_del(&conf->curstate->ev); |
589 | external_evtimer_setup(conf->curstate, |
590 | IFSD_EVTIMER_DEL); |
591 | } |
592 | conf->curstate = conf->nextstate; |
593 | conf->nextstate = NULL((void *)0); |
594 | conf->curstate->entered = time(NULL((void *)0)); |
595 | external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD); |
596 | adjust_external_expressions(conf->curstate); |
597 | return (1); |
598 | } |
599 | return (0); |
600 | } |
601 | |
602 | /* |
603 | * Run recursively through the tree of actions. |
604 | */ |
605 | void |
606 | do_action(struct ifsd_action *action) |
607 | { |
608 | struct ifsd_action *subaction; |
609 | |
610 | switch (action->type) { |
611 | case IFSD_ACTION_COMMAND1: |
612 | log_debug("running %s", action->act.command); |
613 | system(action->act.command); |
614 | break; |
615 | case IFSD_ACTION_CHANGESTATE2: |
616 | conf->nextstate = action->act.nextstate; |
617 | break; |
618 | case IFSD_ACTION_CONDITION3: |
619 | if ((action->act.c.expression != NULL((void *)0) && |
620 | action->act.c.expression->truth) || |
621 | action->act.c.expression == NULL((void *)0)) { |
622 | TAILQ_FOREACH(subaction, &action->act.c.actions,for((subaction) = ((&action->act.c.actions)->tqh_first ); (subaction) != ((void *)0); (subaction) = ((subaction)-> entries.tqe_next)) |
623 | entries)for((subaction) = ((&action->act.c.actions)->tqh_first ); (subaction) != ((void *)0); (subaction) = ((subaction)-> entries.tqe_next)) |
624 | do_action(subaction); |
625 | } |
626 | break; |
627 | default: |
628 | log_debug("%s: unknown action %d", __func__, action->type); |
629 | break; |
630 | } |
631 | } |
632 | |
633 | /* |
634 | * Fetch the current link states. |
635 | */ |
636 | void |
637 | fetch_ifstate(int do_eval) |
638 | { |
639 | struct ifaddrs *ifap, *ifa; |
640 | |
641 | if (getifaddrs(&ifap) != 0) |
642 | fatal("getifaddrs"); |
643 | |
644 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { |
645 | if (ifa->ifa_addr != NULL((void *)0) && |
646 | ifa->ifa_addr->sa_family == AF_LINK18) { |
647 | struct if_data *ifdata = ifa->ifa_data; |
648 | scan_ifstate(ifa->ifa_name, ifdata->ifi_link_state, |
649 | do_eval); |
650 | } |
651 | } |
652 | |
653 | freeifaddrs(ifap); |
654 | } |
655 | |
656 | void |
657 | check_ifdeparture(void) |
658 | { |
659 | struct ifsd_state *state; |
660 | struct ifsd_ifstate *ifstate; |
661 | |
662 | TAILQ_FOREACH(state, &conf->states, entries)for((state) = ((&conf->states)->tqh_first); (state) != ((void *)0); (state) = ((state)->entries.tqe_next)) { |
663 | TAILQ_FOREACH(ifstate, &state->interface_states, entries)for((ifstate) = ((&state->interface_states)->tqh_first ); (ifstate) != ((void *)0); (ifstate) = ((ifstate)->entries .tqe_next)) { |
664 | if (if_nametoindex(ifstate->ifname) == 0) |
665 | scan_ifstate(ifstate->ifname, |
666 | LINK_STATE_DOWN2, 1); |
667 | } |
668 | } |
669 | } |
670 | |
671 | void |
672 | clear_config(struct ifsd_config *oconf) |
673 | { |
674 | struct ifsd_state *state; |
675 | |
676 | external_evtimer_setup(&conf->initstate, IFSD_EVTIMER_DEL); |
677 | if (conf != NULL((void *)0) && conf->curstate != NULL((void *)0)) |
678 | external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL); |
679 | while ((state = TAILQ_FIRST(&oconf->states)((&oconf->states)->tqh_first)) != NULL((void *)0)) { |
680 | TAILQ_REMOVE(&oconf->states, state, entries)do { if (((state)->entries.tqe_next) != ((void *)0)) (state )->entries.tqe_next->entries.tqe_prev = (state)->entries .tqe_prev; else (&oconf->states)->tqh_last = (state )->entries.tqe_prev; *(state)->entries.tqe_prev = (state )->entries.tqe_next; ; ; } while (0); |
681 | remove_action(state->init, state); |
682 | remove_action(state->body, state); |
683 | free(state->name); |
684 | free(state); |
685 | } |
686 | remove_action(oconf->initstate.init, &oconf->initstate); |
687 | remove_action(oconf->initstate.body, &oconf->initstate); |
688 | free(oconf); |
689 | } |
690 | |
691 | void |
692 | remove_action(struct ifsd_action *action, struct ifsd_state *state) |
693 | { |
694 | struct ifsd_action *subaction; |
695 | |
696 | if (action == NULL((void *)0) || state == NULL((void *)0)) |
697 | return; |
698 | |
699 | switch (action->type) { |
700 | case IFSD_ACTION_COMMAND1: |
701 | free(action->act.command); |
702 | break; |
703 | case IFSD_ACTION_CHANGESTATE2: |
704 | break; |
705 | case IFSD_ACTION_CONDITION3: |
706 | if (action->act.c.expression != NULL((void *)0)) |
707 | remove_expression(action->act.c.expression, state); |
708 | while ((subaction = |
709 | TAILQ_FIRST(&action->act.c.actions)((&action->act.c.actions)->tqh_first)) != NULL((void *)0)) { |
710 | TAILQ_REMOVE(&action->act.c.actions,do { if (((subaction)->entries.tqe_next) != ((void *)0)) ( subaction)->entries.tqe_next->entries.tqe_prev = (subaction )->entries.tqe_prev; else (&action->act.c.actions)-> tqh_last = (subaction)->entries.tqe_prev; *(subaction)-> entries.tqe_prev = (subaction)->entries.tqe_next; ; ; } while (0) |
711 | subaction, entries)do { if (((subaction)->entries.tqe_next) != ((void *)0)) ( subaction)->entries.tqe_next->entries.tqe_prev = (subaction )->entries.tqe_prev; else (&action->act.c.actions)-> tqh_last = (subaction)->entries.tqe_prev; *(subaction)-> entries.tqe_prev = (subaction)->entries.tqe_next; ; ; } while (0); |
712 | remove_action(subaction, state); |
713 | } |
714 | } |
715 | free(action); |
716 | } |
717 | |
718 | void |
719 | remove_expression(struct ifsd_expression *expression, |
720 | struct ifsd_state *state) |
721 | { |
722 | switch (expression->type) { |
723 | case IFSD_OPER_IFSTATE5: |
724 | TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.ifstate-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0) |
725 | entries)do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.ifstate-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0); |
726 | if (--expression->u.ifstate->refcount == 0) { |
727 | TAILQ_REMOVE(&state->interface_states,do { if (((expression->u.ifstate)->entries.tqe_next) != ((void *)0)) (expression->u.ifstate)->entries.tqe_next ->entries.tqe_prev = (expression->u.ifstate)->entries .tqe_prev; else (&state->interface_states)->tqh_last = (expression->u.ifstate)->entries.tqe_prev; *(expression ->u.ifstate)->entries.tqe_prev = (expression->u.ifstate )->entries.tqe_next; ; ; } while (0) |
728 | expression->u.ifstate, entries)do { if (((expression->u.ifstate)->entries.tqe_next) != ((void *)0)) (expression->u.ifstate)->entries.tqe_next ->entries.tqe_prev = (expression->u.ifstate)->entries .tqe_prev; else (&state->interface_states)->tqh_last = (expression->u.ifstate)->entries.tqe_prev; *(expression ->u.ifstate)->entries.tqe_prev = (expression->u.ifstate )->entries.tqe_next; ; ; } while (0); |
729 | free(expression->u.ifstate); |
730 | } |
731 | break; |
732 | case IFSD_OPER_EXTERNAL4: |
733 | TAILQ_REMOVE(&expression->u.external->expressions, expression,do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.external-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0) |
734 | entries)do { if (((expression)->entries.tqe_next) != ((void *)0)) ( expression)->entries.tqe_next->entries.tqe_prev = (expression )->entries.tqe_prev; else (&expression->u.external-> expressions)->tqh_last = (expression)->entries.tqe_prev ; *(expression)->entries.tqe_prev = (expression)->entries .tqe_next; ; ; } while (0); |
735 | if (--expression->u.external->refcount == 0) { |
736 | TAILQ_REMOVE(&state->external_tests,do { if (((expression->u.external)->entries.tqe_next) != ((void *)0)) (expression->u.external)->entries.tqe_next ->entries.tqe_prev = (expression->u.external)->entries .tqe_prev; else (&state->external_tests)->tqh_last = (expression->u.external)->entries.tqe_prev; *(expression ->u.external)->entries.tqe_prev = (expression->u.external )->entries.tqe_next; ; ; } while (0) |
737 | expression->u.external, entries)do { if (((expression->u.external)->entries.tqe_next) != ((void *)0)) (expression->u.external)->entries.tqe_next ->entries.tqe_prev = (expression->u.external)->entries .tqe_prev; else (&state->external_tests)->tqh_last = (expression->u.external)->entries.tqe_prev; *(expression ->u.external)->entries.tqe_prev = (expression->u.external )->entries.tqe_next; ; ; } while (0); |
738 | free(expression->u.external->command); |
739 | event_del(&expression->u.external->ev); |
740 | free(expression->u.external); |
741 | } |
742 | break; |
743 | default: |
744 | if (expression->left != NULL((void *)0)) |
745 | remove_expression(expression->left, state); |
746 | if (expression->right != NULL((void *)0)) |
747 | remove_expression(expression->right, state); |
748 | break; |
749 | } |
750 | free(expression); |
751 | } |