xref: /openbsd/usr.sbin/ifstated/ifstated.c (revision 404b540a)
1 /*	$OpenBSD: ifstated.c,v 1.35 2009/06/25 17:14:57 sthen 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/ioctl.h>
28 #include <sys/socket.h>
29 #include <sys/wait.h>
30 
31 #include <net/if.h>
32 #include <net/route.h>
33 #include <netinet/in.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <err.h>
40 #include <event.h>
41 #include <unistd.h>
42 #include <ifaddrs.h>
43 
44 #include "ifstated.h"
45 
46 struct	 ifsd_config *conf = NULL, *newconf = NULL;
47 
48 int	 opts = 0;
49 int	 opt_inhibit = 0;
50 char	*configfile = "/etc/ifstated.conf";
51 struct event	rt_msg_ev, sighup_ev, startup_ev, sigchld_ev;
52 
53 void	startup_handler(int, short, void *);
54 void	sighup_handler(int, short, void *);
55 int	load_config(void);
56 void	sigchld_handler(int, short, void *);
57 void	rt_msg_handler(int, short, void *);
58 void	external_handler(int, short, void *);
59 void	external_exec(struct ifsd_external *, int);
60 void	check_external_status(struct ifsd_state *);
61 void	external_evtimer_setup(struct ifsd_state *, int);
62 void	scan_ifstate(int, int, int);
63 int	scan_ifstate_single(int, int, struct ifsd_state *);
64 void	fetch_state(void);
65 void	usage(void);
66 void	adjust_expressions(struct ifsd_expression_list *, int);
67 void	adjust_external_expressions(struct ifsd_state *);
68 void	eval_state(struct ifsd_state *);
69 int	state_change(void);
70 void	do_action(struct ifsd_action *);
71 void	remove_action(struct ifsd_action *, struct ifsd_state *);
72 void	remove_expression(struct ifsd_expression *, struct ifsd_state *);
73 
74 void
75 usage(void)
76 {
77 	extern char *__progname;
78 
79 	fprintf(stderr, "usage: %s [-dhinv] [-D macro=value] [-f file]\n",
80 	    __progname);
81 	exit(1);
82 }
83 
84 int
85 main(int argc, char *argv[])
86 {
87 	struct timeval tv;
88 	int ch;
89 	int debug = 0;
90 
91 	log_init(1);
92 
93 	while ((ch = getopt(argc, argv, "dD:f:hniv")) != -1) {
94 		switch (ch) {
95 		case 'd':
96 			debug = 1;
97 			break;
98 		case 'D':
99 			if (cmdline_symset(optarg) < 0)
100 				errx(1, "could not parse macro definition %s",
101 				    optarg);
102 			break;
103 		case 'f':
104 			configfile = optarg;
105 			break;
106 		case 'h':
107 			usage();
108 			break;
109 		case 'n':
110 			opts |= IFSD_OPT_NOACTION;
111 			break;
112 		case 'i':
113 			opt_inhibit = 1;
114 			break;
115 		case 'v':
116 			if (opts & IFSD_OPT_VERBOSE)
117 				opts |= IFSD_OPT_VERBOSE2;
118 			opts |= IFSD_OPT_VERBOSE;
119 			break;
120 		default:
121 			usage();
122 		}
123 	}
124 
125 	argc -= optind;
126 	argv += optind;
127 	if (argc > 0)
128 		usage();
129 
130 	if (opts & IFSD_OPT_NOACTION) {
131 		if ((newconf = parse_config(configfile, opts)) == NULL)
132 			exit(1);
133 		warnx("configuration OK");
134 		exit(0);
135 	}
136 
137 	if (!debug) {
138 		daemon(1, 0);
139 		setproctitle(NULL);
140 	}
141 
142 	event_init();
143 	log_init(debug);
144 
145 	signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
146 	signal_add(&sigchld_ev, NULL);
147 
148 	/* Loading the config needs to happen in the event loop */
149 	tv.tv_usec = 0;
150 	tv.tv_sec = 0;
151 	evtimer_set(&startup_ev, startup_handler, NULL);
152 	evtimer_add(&startup_ev, &tv);
153 
154 	event_loop(0);
155 	exit(0);
156 }
157 
158 void
159 startup_handler(int fd, short event, void *arg)
160 {
161 	int rt_fd;
162 	unsigned int rtfilter;
163 
164 	if ((rt_fd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0)
165 		err(1, "no routing socket");
166 
167 	if (load_config() != 0) {
168 		log_warnx("unable to load config");
169 		exit(1);
170 	}
171 
172 	rtfilter = ROUTE_FILTER(RTM_IFINFO);
173 	if (setsockopt(rt_fd, PF_ROUTE, ROUTE_MSGFILTER,
174 	    &rtfilter, sizeof(rtfilter)) == -1)         /* not fatal */
175 		log_warn("startup_handler: setsockopt");
176 
177 	event_set(&rt_msg_ev, rt_fd, EV_READ|EV_PERSIST, rt_msg_handler, NULL);
178 	event_add(&rt_msg_ev, NULL);
179 
180 	signal_set(&sighup_ev, SIGHUP, sighup_handler, NULL);
181 	signal_add(&sighup_ev, NULL);
182 
183 	log_info("started");
184 }
185 
186 void
187 sighup_handler(int fd, short event, void *arg)
188 {
189 	log_info("reloading config");
190 	if (load_config() != 0)
191 		log_warnx("unable to reload config");
192 }
193 
194 int
195 load_config(void)
196 {
197 	if ((newconf = parse_config(configfile, opts)) == NULL)
198 		return (-1);
199 	if (conf != NULL)
200 		clear_config(conf);
201 	conf = newconf;
202 	conf->always.entered = time(NULL);
203 	fetch_state();
204 	external_evtimer_setup(&conf->always, IFSD_EVTIMER_ADD);
205 	adjust_external_expressions(&conf->always);
206 	eval_state(&conf->always);
207 	if (conf->curstate != NULL) {
208 		log_info("initial state: %s", conf->curstate->name);
209 		conf->curstate->entered = time(NULL);
210 		conf->nextstate = conf->curstate;
211 		conf->curstate = NULL;
212 		while (state_change())
213 			do_action(conf->curstate->always);
214 	}
215 	return (0);
216 }
217 
218 void
219 rt_msg_handler(int fd, short event, void *arg)
220 {
221 	char msg[2048];
222 	struct rt_msghdr *rtm = (struct rt_msghdr *)&msg;
223 	struct if_msghdr ifm;
224 	int len;
225 
226 	len = read(fd, msg, sizeof(msg));
227 
228 	/* XXX ignore errors? */
229 	if (len < sizeof(struct rt_msghdr))
230 		return;
231 
232 	if (rtm->rtm_version != RTM_VERSION)
233 		return;
234 
235 	if (rtm->rtm_type != RTM_IFINFO)
236 		return;
237 
238 	memcpy(&ifm, rtm, sizeof(ifm));
239 	scan_ifstate(ifm.ifm_index, ifm.ifm_data.ifi_link_state, 1);
240 }
241 
242 void
243 sigchld_handler(int fd, short event, void *arg)
244 {
245 	check_external_status(&conf->always);
246 	if (conf->curstate != NULL)
247 		check_external_status(conf->curstate);
248 }
249 
250 void
251 external_handler(int fd, short event, void *arg)
252 {
253 	struct ifsd_external *external = (struct ifsd_external *)arg;
254 	struct timeval tv;
255 
256 	/* re-schedule */
257 	tv.tv_usec = 0;
258 	tv.tv_sec = external->frequency;
259 	evtimer_set(&external->ev, external_handler, external);
260 	evtimer_add(&external->ev, &tv);
261 
262 	/* execute */
263 	external_exec(external, 1);
264 }
265 
266 void
267 external_exec(struct ifsd_external *external, int async)
268 {
269 	char *argp[] = {"sh", "-c", NULL, NULL};
270 	pid_t pid;
271 	int s;
272 
273 	if (external->pid > 0) {
274 		log_info("previous command %s [%d] still running, killing it",
275 		    external->command, external->pid);
276 		kill(external->pid, SIGKILL);
277 		waitpid(external->pid, &s, 0);
278 		external->pid = 0;
279 	}
280 
281 	argp[2] = external->command;
282 	log_debug("running %s", external->command);
283 	pid = fork();
284 	if (pid < 0) {
285 		log_warn("fork error");
286 	} else if (pid == 0) {
287 		execv("/bin/sh", argp);
288 		_exit(1);
289 		/* NOTREACHED */
290 	} else {
291 		external->pid = pid;
292 	}
293 	if (!async) {
294 		waitpid(external->pid, &s, 0);
295 		external->pid = 0;
296 		if (WIFEXITED(s))
297 			external->prevstatus = WEXITSTATUS(s);
298 	}
299 }
300 
301 void
302 adjust_external_expressions(struct ifsd_state *state)
303 {
304 	struct ifsd_external *external;
305 	struct ifsd_expression_list expressions;
306 
307 	TAILQ_INIT(&expressions);
308 	TAILQ_FOREACH(external, &state->external_tests, entries) {
309 		struct ifsd_expression *expression;
310 
311 		if (external->prevstatus == -1)
312 			continue;
313 
314 		TAILQ_FOREACH(expression, &external->expressions, entries) {
315 			TAILQ_INSERT_TAIL(&expressions,
316 			    expression, eval);
317 			expression->truth = !external->prevstatus;
318 		}
319 		adjust_expressions(&expressions, conf->maxdepth);
320 	}
321 }
322 
323 void
324 check_external_status(struct ifsd_state *state)
325 {
326 	struct ifsd_external *external, *end = NULL;
327 	int status, s, changed = 0;
328 
329 	/* Do this manually; change ordering so the oldest is first */
330 	external = TAILQ_FIRST(&state->external_tests);
331 	while (external != NULL && external != end) {
332 		struct ifsd_external *newexternal;
333 
334 		newexternal = TAILQ_NEXT(external, entries);
335 
336 		if (external->pid <= 0)
337 			goto loop;
338 
339 		if (wait4(external->pid, &s, WNOHANG, NULL) == 0)
340 			goto loop;
341 
342 		external->pid = 0;
343 		if (end == NULL)
344 			end = external;
345 		if (WIFEXITED(s))
346 			status = WEXITSTATUS(s);
347 		else {
348 			log_warnx("%s exited abnormally", external->command);
349 			goto loop;
350 		}
351 
352 		if (external->prevstatus != status &&
353 		    (external->prevstatus != -1 || !opt_inhibit)) {
354 			changed = 1;
355 			external->prevstatus = status;
356 		}
357 		external->lastexec = time(NULL);
358 		TAILQ_REMOVE(&state->external_tests, external, entries);
359 		TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
360 loop:
361 		external = newexternal;
362 	}
363 
364 	if (changed) {
365 		adjust_external_expressions(state);
366 		eval_state(state);
367 	}
368 }
369 
370 void
371 external_evtimer_setup(struct ifsd_state *state, int action)
372 {
373 	struct ifsd_external *external;
374 	int s;
375 
376 	if (state != NULL) {
377 		switch (action) {
378 		case IFSD_EVTIMER_ADD:
379 			TAILQ_FOREACH(external,
380 			    &state->external_tests, entries) {
381 				struct timeval tv;
382 
383 				/* run it once right away */
384 				external_exec(external, 0);
385 
386 				/* schedule it for later */
387 				tv.tv_usec = 0;
388 				tv.tv_sec = external->frequency;
389 				evtimer_set(&external->ev, external_handler,
390 				    external);
391 				evtimer_add(&external->ev, &tv);
392 			}
393 			break;
394 		case IFSD_EVTIMER_DEL:
395 			TAILQ_FOREACH(external,
396 			    &state->external_tests, entries) {
397 				if (external->pid > 0) {
398 					kill(external->pid, SIGKILL);
399 					waitpid(external->pid, &s, 0);
400 					external->pid = 0;
401 				}
402 				evtimer_del(&external->ev);
403 			}
404 			break;
405 		}
406 	}
407 }
408 
409 int
410 scan_ifstate_single(int ifindex, int s, struct ifsd_state *state)
411 {
412 	struct ifsd_ifstate *ifstate;
413 	struct ifsd_expression_list expressions;
414 	int changed = 0;
415 
416 	TAILQ_INIT(&expressions);
417 
418 	TAILQ_FOREACH(ifstate, &state->interface_states, entries) {
419 		if (ifstate->ifindex == ifindex) {
420 			if (ifstate->prevstate != s &&
421 			    (ifstate->prevstate != -1 || !opt_inhibit)) {
422 				struct ifsd_expression *expression;
423 				int truth;
424 
425 				truth = (ifstate->ifstate == s) ||
426 				    (ifstate->ifstate == IFSD_LINKUP &&
427 				    LINK_STATE_IS_UP(s));
428 
429 				TAILQ_FOREACH(expression,
430 				    &ifstate->expressions, entries) {
431 					expression->truth = truth;
432 					TAILQ_INSERT_TAIL(&expressions,
433 					    expression, eval);
434 					changed = 1;
435 				}
436 				ifstate->prevstate = s;
437 			}
438 		}
439 	}
440 
441 	if (changed)
442 		adjust_expressions(&expressions, conf->maxdepth);
443 	return (changed);
444 }
445 
446 void
447 scan_ifstate(int ifindex, int s, int do_eval)
448 {
449 	struct ifsd_state *state;
450 	int cur_eval = 0;
451 
452 	if (scan_ifstate_single(ifindex, s, &conf->always) && do_eval)
453 		eval_state(&conf->always);
454 	TAILQ_FOREACH(state, &conf->states, entries) {
455 		if (scan_ifstate_single(ifindex, s, state) &&
456 		    (do_eval && state == conf->curstate))
457 			cur_eval = 1;
458 	}
459 	/* execute actions _after_ all expressions have been adjusted */
460 	if (cur_eval)
461 		eval_state(conf->curstate);
462 }
463 
464 /*
465  * Do a bottom-up ajustment of the expression tree's truth value,
466  * level-by-level to ensure that each expression's subexpressions have been
467  * evaluated.
468  */
469 void
470 adjust_expressions(struct ifsd_expression_list *expressions, int depth)
471 {
472 	struct ifsd_expression_list nexpressions;
473 	struct ifsd_expression *expression;
474 
475 	TAILQ_INIT(&nexpressions);
476 	while ((expression = TAILQ_FIRST(expressions)) != NULL) {
477 		TAILQ_REMOVE(expressions, expression, eval);
478 		if (expression->depth == depth) {
479 			struct ifsd_expression *te;
480 
481 			switch (expression->type) {
482 			case IFSD_OPER_AND:
483 				expression->truth = expression->left->truth &&
484 				    expression->right->truth;
485 				break;
486 			case IFSD_OPER_OR:
487 				expression->truth = expression->left->truth ||
488 				    expression->right->truth;
489 				break;
490 			case IFSD_OPER_NOT:
491 				expression->truth = !expression->right->truth;
492 				break;
493 			default:
494 				break;
495 			}
496 			if (expression->parent != NULL) {
497 				if (TAILQ_EMPTY(&nexpressions))
498 				te = NULL;
499 				TAILQ_FOREACH(te, &nexpressions, eval)
500 					if (expression->parent == te)
501 						break;
502 				if (te == NULL)
503 					TAILQ_INSERT_TAIL(&nexpressions,
504 					    expression->parent, eval);
505 			}
506 		} else
507 			TAILQ_INSERT_TAIL(&nexpressions, expression, eval);
508 	}
509 	if (depth > 0)
510 		adjust_expressions(&nexpressions, depth - 1);
511 }
512 
513 void
514 eval_state(struct ifsd_state *state)
515 {
516 	struct ifsd_external *external = TAILQ_FIRST(&state->external_tests);
517 	if (external == NULL || external->lastexec >= state->entered ||
518 	    external->lastexec == 0) {
519 		do_action(state->always);
520 		while (state_change())
521 			do_action(conf->curstate->always);
522 	}
523 }
524 
525 /*
526  *If a previous action included a state change, process it.
527  */
528 int
529 state_change(void)
530 {
531 	if (conf->nextstate != NULL && conf->curstate != conf->nextstate) {
532 		log_info("changing state to %s", conf->nextstate->name);
533 		if (conf->curstate != NULL) {
534 			evtimer_del(&conf->curstate->ev);
535 			external_evtimer_setup(conf->curstate,
536 			    IFSD_EVTIMER_DEL);
537 		}
538 		conf->curstate = conf->nextstate;
539 		conf->nextstate = NULL;
540 		conf->curstate->entered = time(NULL);
541 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_ADD);
542 		adjust_external_expressions(conf->curstate);
543 		do_action(conf->curstate->init);
544 		return (1);
545 	}
546 	return (0);
547 }
548 
549 /*
550  * Run recursively through the tree of actions.
551  */
552 void
553 do_action(struct ifsd_action *action)
554 {
555 	struct ifsd_action *subaction;
556 
557 	switch (action->type) {
558 	case IFSD_ACTION_COMMAND:
559 		log_info("running %s", action->act.command);
560 		system(action->act.command);
561 		break;
562 	case IFSD_ACTION_CHANGESTATE:
563 		conf->nextstate = action->act.nextstate;
564 		break;
565 	case IFSD_ACTION_CONDITION:
566 		if ((action->act.c.expression != NULL &&
567 		    action->act.c.expression->truth) ||
568 		    action->act.c.expression == NULL) {
569 			TAILQ_FOREACH(subaction, &action->act.c.actions,
570 			    entries)
571 				do_action(subaction);
572 		}
573 		break;
574 	default:
575 		log_debug("do_action: unknown action %d", action->type);
576 		break;
577 	}
578 }
579 
580 /*
581  * Fetch the current link states.
582  */
583 void
584 fetch_state(void)
585 {
586 	struct ifaddrs *ifap, *ifa;
587 	char *oname = NULL;
588 	int sock = socket(AF_INET, SOCK_DGRAM, 0);
589 
590 	if (getifaddrs(&ifap) != 0)
591 		err(1, "getifaddrs");
592 
593 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
594 		struct ifreq ifr;
595 		struct if_data  ifrdat;
596 
597 		if (oname && !strcmp(oname, ifa->ifa_name))
598 			continue;
599 		oname = ifa->ifa_name;
600 
601 		strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
602 		ifr.ifr_data = (caddr_t)&ifrdat;
603 
604 		if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
605 			continue;
606 
607 		scan_ifstate(if_nametoindex(ifa->ifa_name),
608 		    ifrdat.ifi_link_state, 0);
609 	}
610 	freeifaddrs(ifap);
611 	close(sock);
612 }
613 
614 
615 
616 /*
617  * Clear the config.
618  */
619 void
620 clear_config(struct ifsd_config *oconf)
621 {
622 	struct ifsd_state *state;
623 
624 	external_evtimer_setup(&conf->always, IFSD_EVTIMER_DEL);
625 	if (conf != NULL && conf->curstate != NULL)
626 		external_evtimer_setup(conf->curstate, IFSD_EVTIMER_DEL);
627 	while ((state = TAILQ_FIRST(&oconf->states)) != NULL) {
628 		TAILQ_REMOVE(&oconf->states, state, entries);
629 		remove_action(state->init, state);
630 		remove_action(state->always, state);
631 		free(state->name);
632 		free(state);
633 	}
634 	remove_action(oconf->always.init, &oconf->always);
635 	remove_action(oconf->always.always, &oconf->always);
636 	free(oconf);
637 }
638 
639 void
640 remove_action(struct ifsd_action *action, struct ifsd_state *state)
641 {
642 	struct ifsd_action *subaction;
643 
644 	if (action == NULL || state == NULL)
645 		return;
646 
647 	switch (action->type) {
648 	case IFSD_ACTION_LOG:
649 		free(action->act.logmessage);
650 		break;
651 	case IFSD_ACTION_COMMAND:
652 		free(action->act.command);
653 		break;
654 	case IFSD_ACTION_CHANGESTATE:
655 		break;
656 	case IFSD_ACTION_CONDITION:
657 		if (action->act.c.expression != NULL)
658 			remove_expression(action->act.c.expression, state);
659 		while ((subaction =
660 		    TAILQ_FIRST(&action->act.c.actions)) != NULL) {
661 			TAILQ_REMOVE(&action->act.c.actions,
662 			    subaction, entries);
663 			remove_action(subaction, state);
664 		}
665 	}
666 	free(action);
667 }
668 
669 void
670 remove_expression(struct ifsd_expression *expression,
671     struct ifsd_state *state)
672 {
673 	switch (expression->type) {
674 	case IFSD_OPER_IFSTATE:
675 		TAILQ_REMOVE(&expression->u.ifstate->expressions, expression,
676 		    entries);
677 		if (--expression->u.ifstate->refcount == 0) {
678 			TAILQ_REMOVE(&state->interface_states,
679 			    expression->u.ifstate, entries);
680 			free(expression->u.ifstate);
681 		}
682 		break;
683 	case IFSD_OPER_EXTERNAL:
684 		TAILQ_REMOVE(&expression->u.external->expressions, expression,
685 		    entries);
686 		if (--expression->u.external->refcount == 0) {
687 			TAILQ_REMOVE(&state->external_tests,
688 			    expression->u.external, entries);
689 			free(expression->u.external->command);
690 			event_del(&expression->u.external->ev);
691 			free(expression->u.external);
692 		}
693 		break;
694 	default:
695 		if (expression->left != NULL)
696 			remove_expression(expression->left, state);
697 		if (expression->right != NULL)
698 			remove_expression(expression->right, state);
699 		break;
700 	}
701 	free(expression);
702 }
703