1 /*	$OpenBSD: parse.y,v 1.30 2010/08/03 18:42:40 henning Exp $	*/
2 
3 /*
4  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
5  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
6  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
7  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
8  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
9  *
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.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 %{
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <net/if.h>
31 
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <unistd.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <event.h>
44 
45 #include "ifstated.h"
46 
47 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
48 static struct file {
49 	TAILQ_ENTRY(file)	 entry;
50 	FILE			*stream;
51 	char			*name;
52 	int			 lineno;
53 	int			 errors;
54 } *file, *topfile;
55 struct file	*pushfile(const char *, int);
56 int		 popfile(void);
57 int		 check_file_secrecy(int, const char *);
58 int		 yyparse(void);
59 int		 yylex(void);
60 int		 yyerror(const char *, ...);
61 int		 kw_cmp(const void *, const void *);
62 int		 lookup(char *);
63 int		 lgetc(int);
64 int		 lungetc(int);
65 int		 findeol(void);
66 
67 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
68 struct sym {
69 	TAILQ_ENTRY(sym)	 entry;
70 	int			 used;
71 	int			 persist;
72 	char			*nam;
73 	char			*val;
74 };
75 int		 symset(const char *, const char *, int);
76 char		*symget(const char *);
77 
78 static struct ifsd_config	*conf;
79 char				*start_state;
80 
81 struct ifsd_action		*curaction;
82 struct ifsd_state		*curstate = NULL;
83 
84 void			 link_states(struct ifsd_action *);
85 void			 set_expression_depth(struct ifsd_expression *, int);
86 void			 init_state(struct ifsd_state *);
87 struct ifsd_ifstate	*new_ifstate(u_short, int);
88 struct ifsd_external	*new_external(char *, u_int32_t);
89 
90 typedef struct {
91 	union {
92 		int64_t		 number;
93 		char		*string;
94 		struct in_addr	 addr;
95 		u_short		 interface;
96 
97 		struct ifsd_expression	*expression;
98 		struct ifsd_ifstate	*ifstate;
99 		struct ifsd_external	*external;
100 
101 	} v;
102 	int lineno;
103 } YYSTYPE;
104 
105 %}
106 
107 %token	STATE INITSTATE
108 %token	LINK UP DOWN UNKNOWN ADDED REMOVED
109 %token	IF RUN SETSTATE EVERY INIT
110 %left	AND OR
111 %left	UNARY
112 %token	ERROR
113 %token	<v.string>	STRING
114 %token	<v.number>	NUMBER
115 %type	<v.string>	string
116 %type	<v.interface>	interface
117 %type	<v.ifstate>	if_test
118 %type	<v.external>	ext_test
119 %type	<v.expression>	expr term
120 %%
121 
122 grammar		: /* empty */
123 		| grammar '\n'
124 		| grammar conf_main '\n'
125 		| grammar varset '\n'
126 		| grammar action '\n'
127 		| grammar state '\n'
128 		| grammar error '\n'		{ file->errors++; }
129 		;
130 
131 string		: string STRING				{
132 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
133 				free($1);
134 				free($2);
135 				yyerror("string: asprintf");
136 				YYERROR;
137 			}
138 			free($1);
139 			free($2);
140 		}
141 		| STRING
142 		;
143 
144 varset		: STRING '=' string		{
145 			if (conf->opts & IFSD_OPT_VERBOSE)
146 				printf("%s = \"%s\"\n", $1, $3);
147 			if (symset($1, $3, 0) == -1) {
148 				free($1);
149 				free($3);
150 				yyerror("cannot store variable");
151 				YYERROR;
152 			}
153 			free($1);
154 			free($3);
155 		}
156 		;
157 
158 conf_main	: INITSTATE STRING		{
159 			start_state = $2;
160 		}
161 		;
162 
163 interface	: STRING		{
164 			if (($$ = if_nametoindex($1)) == 0) {
165 				yyerror("unknown interface %s", $1);
166 				free($1);
167 				YYERROR;
168 			}
169 			free($1);
170 		}
171 		;
172 
173 optnl		: '\n' optnl
174 		|
175 		;
176 
177 nl		: '\n' optnl		/* one newline or more */
178 		;
179 
180 action		: RUN STRING		{
181 			struct ifsd_action *action;
182 
183 			if ((action = calloc(1, sizeof(*action))) == NULL)
184 				err(1, "action: calloc");
185 			action->type = IFSD_ACTION_COMMAND;
186 			action->act.command = $2;
187 			if (action->act.command == NULL)
188 				err(1, "action: strdup");
189 			TAILQ_INSERT_TAIL(&curaction->act.c.actions,
190 			    action, entries);
191 		}
192 		| SETSTATE STRING	{
193 			struct ifsd_action *action;
194 
195 			if (curstate == NULL) {
196 				free($2);
197 				yyerror("set-state must be used inside 'if'");
198 				YYERROR;
199 			}
200 			if ((action = calloc(1, sizeof(*action))) == NULL)
201 				err(1, "action: calloc");
202 			action->type = IFSD_ACTION_CHANGESTATE;
203 			action->act.statename = $2;
204 			TAILQ_INSERT_TAIL(&curaction->act.c.actions,
205 			    action, entries);
206 		}
207 		| IF {
208 			struct ifsd_action *action;
209 
210 			if ((action = calloc(1, sizeof(*action))) == NULL)
211 				err(1, "action: calloc");
212 			action->type = IFSD_ACTION_CONDITION;
213 			TAILQ_INIT(&action->act.c.actions);
214 			TAILQ_INSERT_TAIL(&curaction->act.c.actions,
215 			    action, entries);
216 			action->parent = curaction;
217 			curaction = action;
218 		} expr action_block {
219 			set_expression_depth(curaction->act.c.expression, 0);
220 			curaction = curaction->parent;
221 		}
222 		;
223 
224 action_block	: optnl '{' optnl action_l '}'
225 		| optnl action
226 		;
227 
228 action_l	: action_l action nl
229 		| action nl
230 		;
231 
232 init		: INIT {
233 			if (curstate != NULL)
234 				curaction = curstate->init;
235 			else
236 				curaction = conf->always.init;
237 		} action_block {
238 			if (curstate != NULL)
239 				curaction = curstate->always;
240 			else
241 				curaction = conf->always.always;
242 		}
243 		;
244 
245 if_test		: interface '.' LINK '.' UP		{
246 			$$ = new_ifstate($1, IFSD_LINKUP);
247 		}
248 		| interface '.' LINK '.' DOWN		{
249 			$$ = new_ifstate($1, IFSD_LINKDOWN);
250 		}
251 		| interface '.' LINK '.' UNKNOWN	{
252 			$$ = new_ifstate($1, IFSD_LINKUNKNOWN);
253 		}
254 		;
255 
256 ext_test	: STRING EVERY NUMBER {
257 			if ($3 <= 0 || $3 > UINT_MAX) {
258 				yyerror("invalid interval: %d", $3);
259 				free($1);
260 				YYERROR;
261 			}
262 			$$ = new_external($1, $3);
263 			free($1);
264 		}
265 		;
266 
267 term		: if_test {
268 			if (($$ = calloc(1, sizeof(*$$))) == NULL)
269 				errx(1, "term: calloc");
270 			curaction->act.c.expression = $$;
271 			$$->type = IFSD_OPER_IFSTATE;
272 			$$->u.ifstate = $1;
273 			TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
274 		}
275 		| ext_test {
276 			if (($$ = calloc(1, sizeof(*$$))) == NULL)
277 				errx(1, "term: calloc");
278 			curaction->act.c.expression = $$;
279 			$$->type = IFSD_OPER_EXTERNAL;
280 			$$->u.external = $1;
281 			TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
282 		}
283 		| '(' expr ')'			{
284 			$$ = $2;
285 		}
286 		;
287 
288 expr		: '!' expr %prec UNARY			{
289 			if (($$ = calloc(1, sizeof(*$$))) == NULL)
290 				errx(1, "expr: calloc");
291 			curaction->act.c.expression = $$;
292 			$$->type = IFSD_OPER_NOT;
293 			$2->parent = $$;
294 			$$->right = $2;
295 		}
296 		| expr AND expr			{
297 			if (($$ = calloc(1, sizeof(*$$))) == NULL)
298 				errx(1, "expr: calloc");
299 			curaction->act.c.expression = $$;
300 			$$->type = IFSD_OPER_AND;
301 			$1->parent = $$;
302 			$3->parent = $$;
303 			$$->left = $1;
304 			$$->right = $3;
305 		}
306 		| expr OR expr			{
307 			if (($$ = calloc(1, sizeof(*$$))) == NULL)
308 				errx(1, "expr: calloc");
309 			curaction->act.c.expression = $$;
310 			$$->type = IFSD_OPER_OR;
311 			$1->parent = $$;
312 			$3->parent = $$;
313 			$$->left = $1;
314 			$$->right = $3;
315 		}
316 		| term
317 		;
318 
319 state		: STATE string {
320 			struct ifsd_state *state = NULL;
321 
322 			TAILQ_FOREACH(state, &conf->states, entries)
323 				if (!strcmp(state->name, $2)) {
324 					yyerror("state %s already exists", $2);
325 					free($2);
326 					YYERROR;
327 				}
328 			if ((state = calloc(1, sizeof(*curstate))) == NULL)
329 				errx(1, "state: calloc");
330 			init_state(state);
331 			state->name = $2;
332 			curstate = state;
333 			curaction = state->always;
334 		} optnl '{' optnl stateopts_l '}' {
335 			TAILQ_INSERT_TAIL(&conf->states, curstate, entries);
336 			curstate = NULL;
337 			curaction = conf->always.always;
338 		}
339 		;
340 
341 stateopts_l	: stateopts_l stateoptsl
342 		| stateoptsl
343 		;
344 
345 stateoptsl	: init nl
346 		| action nl
347 		;
348 
349 %%
350 
351 struct keywords {
352 	const char	*k_name;
353 	int		 k_val;
354 };
355 
356 int
yyerror(const char * fmt,...)357 yyerror(const char *fmt, ...)
358 {
359 	va_list		 ap;
360 
361 	file->errors++;
362 	va_start(ap, fmt);
363 	fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
364 	vfprintf(stderr, fmt, ap);
365 	fprintf(stderr, "\n");
366 	va_end(ap);
367 	return (0);
368 }
369 
370 int
kw_cmp(const void * k,const void * e)371 kw_cmp(const void *k, const void *e)
372 {
373 	return (strcmp(k, ((const struct keywords *)e)->k_name));
374 }
375 
376 int
lookup(char * s)377 lookup(char *s)
378 {
379 	/* this has to be sorted always */
380 	static const struct keywords keywords[] = {
381 		{ "&&",			AND},
382 		{ "added",		ADDED},
383 		{ "down",		DOWN},
384 		{ "every",		EVERY},
385 		{ "if",			IF},
386 		{ "init",		INIT},
387 		{ "init-state",		INITSTATE},
388 		{ "link",		LINK},
389 		{ "removed",		REMOVED},
390 		{ "run",		RUN},
391 		{ "set-state",		SETSTATE},
392 		{ "state",		STATE},
393 		{ "unknown",		UNKNOWN},
394 		{ "up",			UP},
395 		{ "||",			OR}
396 	};
397 	const struct keywords	*p;
398 
399 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
400 	    sizeof(keywords[0]), kw_cmp);
401 
402 	if (p)
403 		return (p->k_val);
404 	else
405 		return (STRING);
406 }
407 
408 #define MAXPUSHBACK	128
409 
410 char	*parsebuf;
411 int	 parseindex;
412 char	 pushback_buffer[MAXPUSHBACK];
413 int	 pushback_index = 0;
414 
415 int
lgetc(int quotec)416 lgetc(int quotec)
417 {
418 	int		c, next;
419 
420 	if (parsebuf) {
421 		/* Read character from the parsebuffer instead of input. */
422 		if (parseindex >= 0) {
423 			c = parsebuf[parseindex++];
424 			if (c != '\0')
425 				return (c);
426 			parsebuf = NULL;
427 		} else
428 			parseindex++;
429 	}
430 
431 	if (pushback_index)
432 		return (pushback_buffer[--pushback_index]);
433 
434 	if (quotec) {
435 		if ((c = getc(file->stream)) == EOF) {
436 			yyerror("reached end of file while parsing "
437 			    "quoted string");
438 			if (file == topfile || popfile() == EOF)
439 				return (EOF);
440 			return (quotec);
441 		}
442 		return (c);
443 	}
444 
445 	while ((c = getc(file->stream)) == '\\') {
446 		next = getc(file->stream);
447 		if (next != '\n') {
448 			c = next;
449 			break;
450 		}
451 		yylval.lineno = file->lineno;
452 		file->lineno++;
453 	}
454 
455 	while (c == EOF) {
456 		if (file == topfile || popfile() == EOF)
457 			return (EOF);
458 		c = getc(file->stream);
459 	}
460 	return (c);
461 }
462 
463 int
lungetc(int c)464 lungetc(int c)
465 {
466 	if (c == EOF)
467 		return (EOF);
468 	if (parsebuf) {
469 		parseindex--;
470 		if (parseindex >= 0)
471 			return (c);
472 	}
473 	if (pushback_index < MAXPUSHBACK-1)
474 		return (pushback_buffer[pushback_index++] = c);
475 	else
476 		return (EOF);
477 }
478 
479 int
findeol(void)480 findeol(void)
481 {
482 	int	c;
483 
484 	parsebuf = NULL;
485 
486 	/* skip to either EOF or the first real EOL */
487 	while (1) {
488 		if (pushback_index)
489 			c = pushback_buffer[--pushback_index];
490 		else
491 			c = lgetc(0);
492 		if (c == '\n') {
493 			file->lineno++;
494 			break;
495 		}
496 		if (c == EOF)
497 			break;
498 	}
499 	return (ERROR);
500 }
501 
502 int
yylex(void)503 yylex(void)
504 {
505 	char	 buf[8096];
506 	char	*p, *val;
507 	int	 quotec, next, c;
508 	int	 token;
509 
510 top:
511 	p = buf;
512 	while ((c = lgetc(0)) == ' ' || c == '\t')
513 		; /* nothing */
514 
515 	yylval.lineno = file->lineno;
516 	if (c == '#')
517 		while ((c = lgetc(0)) != '\n' && c != EOF)
518 			; /* nothing */
519 	if (c == '$' && parsebuf == NULL) {
520 		while (1) {
521 			if ((c = lgetc(0)) == EOF)
522 				return (0);
523 
524 			if (p + 1 >= buf + sizeof(buf) - 1) {
525 				yyerror("string too long");
526 				return (findeol());
527 			}
528 			if (isalnum(c) || c == '_') {
529 				*p++ = (char)c;
530 				continue;
531 			}
532 			*p = '\0';
533 			lungetc(c);
534 			break;
535 		}
536 		val = symget(buf);
537 		if (val == NULL) {
538 			yyerror("macro '%s' not defined", buf);
539 			return (findeol());
540 		}
541 		parsebuf = val;
542 		parseindex = 0;
543 		goto top;
544 	}
545 
546 	switch (c) {
547 	case '\'':
548 	case '"':
549 		quotec = c;
550 		while (1) {
551 			if ((c = lgetc(quotec)) == EOF)
552 				return (0);
553 			if (c == '\n') {
554 				file->lineno++;
555 				continue;
556 			} else if (c == '\\') {
557 				if ((next = lgetc(quotec)) == EOF)
558 					return (0);
559 				if (next == quotec || c == ' ' || c == '\t')
560 					c = next;
561 				else if (next == '\n') {
562 					file->lineno++;
563 					continue;
564 				} else
565 					lungetc(next);
566 			} else if (c == quotec) {
567 				*p = '\0';
568 				break;
569 			}
570 			if (p + 1 >= buf + sizeof(buf) - 1) {
571 				yyerror("string too long");
572 				return (findeol());
573 			}
574 			*p++ = (char)c;
575 		}
576 		yylval.v.string = strdup(buf);
577 		if (yylval.v.string == NULL)
578 			err(1, "yylex: strdup");
579 		return (STRING);
580 	}
581 
582 #define allowed_to_end_number(x) \
583 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
584 
585 	if (c == '-' || isdigit(c)) {
586 		do {
587 			*p++ = c;
588 			if ((unsigned)(p-buf) >= sizeof(buf)) {
589 				yyerror("string too long");
590 				return (findeol());
591 			}
592 		} while ((c = lgetc(0)) != EOF && isdigit(c));
593 		lungetc(c);
594 		if (p == buf + 1 && buf[0] == '-')
595 			goto nodigits;
596 		if (c == EOF || allowed_to_end_number(c)) {
597 			const char *errstr = NULL;
598 
599 			*p = '\0';
600 			yylval.v.number = strtonum(buf, LLONG_MIN,
601 			    LLONG_MAX, &errstr);
602 			if (errstr) {
603 				yyerror("\"%s\" invalid number: %s",
604 				    buf, errstr);
605 				return (findeol());
606 			}
607 			return (NUMBER);
608 		} else {
609 nodigits:
610 			while (p > buf + 1)
611 				lungetc(*--p);
612 			c = *--p;
613 			if (c == '-')
614 				return (c);
615 		}
616 	}
617 
618 #define allowed_in_string(x) \
619 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
620 	x != '{' && x != '}' && \
621 	x != '!' && x != '=' && x != '#' && \
622 	x != ',' && x != '.'))
623 
624 	if (isalnum(c) || c == ':' || c == '_' || c == '&' || c == '|') {
625 		do {
626 			*p++ = c;
627 			if ((unsigned)(p-buf) >= sizeof(buf)) {
628 				yyerror("string too long");
629 				return (findeol());
630 			}
631 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
632 		lungetc(c);
633 		*p = '\0';
634 		if ((token = lookup(buf)) == STRING)
635 			if ((yylval.v.string = strdup(buf)) == NULL)
636 				err(1, "yylex: strdup");
637 		return (token);
638 	}
639 	if (c == '\n') {
640 		yylval.lineno = file->lineno;
641 		file->lineno++;
642 	}
643 	if (c == EOF)
644 		return (0);
645 	return (c);
646 }
647 
648 int
check_file_secrecy(int fd,const char * fname)649 check_file_secrecy(int fd, const char *fname)
650 {
651 	struct stat	st;
652 
653 	if (fstat(fd, &st)) {
654 		warn("cannot stat %s", fname);
655 		return (-1);
656 	}
657 	if (st.st_uid != 0 && st.st_uid != getuid()) {
658 		warnx("%s: owner not root or current user", fname);
659 		return (-1);
660 	}
661 	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
662 		warnx("%s: group/world readable/writeable", fname);
663 		return (-1);
664 	}
665 	return (0);
666 }
667 
668 struct file *
pushfile(const char * name,int secret)669 pushfile(const char *name, int secret)
670 {
671 	struct file	*nfile;
672 
673 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
674 		warn("malloc");
675 		return (NULL);
676 	}
677 	if ((nfile->name = strdup(name)) == NULL) {
678 		warn("malloc");
679 		free(nfile);
680 		return (NULL);
681 	}
682 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
683 		warnx("%s", nfile->name);
684 		free(nfile->name);
685 		free(nfile);
686 		return (NULL);
687 	} else if (secret &&
688 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
689 		fclose(nfile->stream);
690 		free(nfile->name);
691 		free(nfile);
692 		return (NULL);
693 	}
694 	nfile->lineno = 1;
695 	TAILQ_INSERT_TAIL(&files, nfile, entry);
696 	return (nfile);
697 }
698 
699 int
popfile(void)700 popfile(void)
701 {
702 	struct file	*prev;
703 
704 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
705 		prev->errors += file->errors;
706 
707 	TAILQ_REMOVE(&files, file, entry);
708 	fclose(file->stream);
709 	free(file->name);
710 	free(file);
711 	file = prev;
712 	return (file ? 0 : EOF);
713 }
714 
715 struct ifsd_config *
parse_config(char * filename,int opts)716 parse_config(char *filename, int opts)
717 {
718 	int		 errors = 0;
719 	struct sym	*sym, *next;
720 	struct ifsd_state *state;
721 
722 	if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL) {
723 		errx(1, "parse_config calloc");
724 		return (NULL);
725 	}
726 
727 	if ((file = pushfile(filename, 0)) == NULL) {
728 		free(conf);
729 		return (NULL);
730 	}
731 	topfile = file;
732 
733 	TAILQ_INIT(&conf->states);
734 
735 	init_state(&conf->always);
736 	curaction = conf->always.always;
737 	conf->opts = opts;
738 
739 	yyparse();
740 
741 	/* Link states */
742 	TAILQ_FOREACH(state, &conf->states, entries) {
743 		link_states(state->init);
744 		link_states(state->always);
745 	}
746 
747 	errors = file->errors;
748 	popfile();
749 
750 	if (start_state != NULL) {
751 		TAILQ_FOREACH(state, &conf->states, entries) {
752 			if (strcmp(start_state, state->name) == 0) {
753 				conf->curstate = state;
754 				break;
755 			}
756 		}
757 		if (conf->curstate == NULL)
758 			errx(1, "invalid start state %s", start_state);
759 	} else {
760 		conf->curstate = TAILQ_FIRST(&conf->states);
761 	}
762 
763 	/* Free macros and check which have not been used. */
764 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
765 		next = TAILQ_NEXT(sym, entry);
766 		if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used)
767 			fprintf(stderr, "warning: macro '%s' not "
768 			    "used\n", sym->nam);
769 		if (!sym->persist) {
770 			free(sym->nam);
771 			free(sym->val);
772 			TAILQ_REMOVE(&symhead, sym, entry);
773 			free(sym);
774 		}
775 	}
776 
777 	if (errors) {
778 		clear_config(conf);
779 		errors = 0;
780 		return (NULL);
781 	}
782 
783 	return (conf);
784 }
785 
786 void
link_states(struct ifsd_action * action)787 link_states(struct ifsd_action *action)
788 {
789 	struct ifsd_action *subaction;
790 
791 	switch (action->type) {
792 	default:
793 	case IFSD_ACTION_COMMAND:
794 		break;
795 	case IFSD_ACTION_CHANGESTATE: {
796 		struct ifsd_state *state;
797 
798 		TAILQ_FOREACH(state, &conf->states, entries) {
799 			if (strcmp(action->act.statename,
800 			    state->name) == 0) {
801 				action->act.nextstate = state;
802 				break;
803 			}
804 		}
805 		if (state == NULL) {
806 			fprintf(stderr, "error: state '%s' not declared\n",
807 			    action->act.statename);
808 			file->errors++;
809 		}
810 		break;
811 	}
812 	case IFSD_ACTION_CONDITION:
813 		TAILQ_FOREACH(subaction, &action->act.c.actions, entries)
814 			link_states(subaction);
815 		break;
816 	}
817 }
818 
819 int
symset(const char * nam,const char * val,int persist)820 symset(const char *nam, const char *val, int persist)
821 {
822 	struct sym	*sym;
823 
824 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
825 	    sym = TAILQ_NEXT(sym, entry))
826 		;	/* nothing */
827 
828 	if (sym != NULL) {
829 		if (sym->persist == 1)
830 			return (0);
831 		else {
832 			free(sym->nam);
833 			free(sym->val);
834 			TAILQ_REMOVE(&symhead, sym, entry);
835 			free(sym);
836 		}
837 	}
838 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
839 		return (-1);
840 
841 	sym->nam = strdup(nam);
842 	if (sym->nam == NULL) {
843 		free(sym);
844 		return (-1);
845 	}
846 	sym->val = strdup(val);
847 	if (sym->val == NULL) {
848 		free(sym->nam);
849 		free(sym);
850 		return (-1);
851 	}
852 	sym->used = 0;
853 	sym->persist = persist;
854 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
855 	return (0);
856 }
857 
858 int
cmdline_symset(char * s)859 cmdline_symset(char *s)
860 {
861 	char	*sym, *val;
862 	int	ret;
863 	size_t	len;
864 
865 	if ((val = strrchr(s, '=')) == NULL)
866 		return (-1);
867 
868 	len = strlen(s) - strlen(val) + 1;
869 	if ((sym = malloc(len)) == NULL)
870 		errx(1, "cmdline_symset: malloc");
871 
872 	strlcpy(sym, s, len);
873 
874 	ret = symset(sym, val + 1, 1);
875 	free(sym);
876 
877 	return (ret);
878 }
879 
880 char *
symget(const char * nam)881 symget(const char *nam)
882 {
883 	struct sym	*sym;
884 
885 	TAILQ_FOREACH(sym, &symhead, entry)
886 		if (strcmp(nam, sym->nam) == 0) {
887 			sym->used = 1;
888 			return (sym->val);
889 		}
890 	return (NULL);
891 }
892 
893 void
set_expression_depth(struct ifsd_expression * expression,int depth)894 set_expression_depth(struct ifsd_expression *expression, int depth)
895 {
896 	expression->depth = depth;
897 	if (conf->maxdepth < depth)
898 		conf->maxdepth = depth;
899 	if (expression->left != NULL)
900 		set_expression_depth(expression->left, depth + 1);
901 	if (expression->right != NULL)
902 		set_expression_depth(expression->right, depth + 1);
903 }
904 
905 void
init_state(struct ifsd_state * state)906 init_state(struct ifsd_state *state)
907 {
908 	TAILQ_INIT(&state->interface_states);
909 	TAILQ_INIT(&state->external_tests);
910 
911 	if ((state->init = calloc(1, sizeof(*state->init))) == NULL)
912 		err(1, "init_state: calloc");
913 	state->init->type = IFSD_ACTION_CONDITION;
914 	TAILQ_INIT(&state->init->act.c.actions);
915 
916 	if ((state->always = calloc(1, sizeof(*state->always))) == NULL)
917 		err(1, "init_state: calloc");
918 	state->always->type = IFSD_ACTION_CONDITION;
919 	TAILQ_INIT(&state->always->act.c.actions);
920 }
921 
922 struct ifsd_ifstate *
new_ifstate(u_short ifindex,int s)923 new_ifstate(u_short ifindex, int s)
924 {
925 	struct ifsd_ifstate *ifstate = NULL;
926 	struct ifsd_state *state;
927 
928 	if (curstate != NULL)
929 		state = curstate;
930 	else
931 		state = &conf->always;
932 
933 	TAILQ_FOREACH(ifstate, &state->interface_states, entries)
934 		if (ifstate->ifindex == ifindex && ifstate->ifstate == s)
935 			break;
936 	if (ifstate == NULL) {
937 		if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL)
938 			errx(1, "new_ifstate: calloc");
939 		ifstate->ifindex = ifindex;
940 		ifstate->ifstate = s;
941 		TAILQ_INIT(&ifstate->expressions);
942 		TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries);
943 	}
944 	ifstate->prevstate = -1;
945 	ifstate->refcount++;
946 	return (ifstate);
947 }
948 
949 struct ifsd_external *
new_external(char * command,u_int32_t frequency)950 new_external(char *command, u_int32_t frequency)
951 {
952 	struct ifsd_external *external = NULL;
953 	struct ifsd_state *state;
954 
955 	if (curstate != NULL)
956 		state = curstate;
957 	else
958 		state = &conf->always;
959 
960 	TAILQ_FOREACH(external, &state->external_tests, entries)
961 		if (strcmp(external->command, command) == 0 &&
962 		    external->frequency == frequency)
963 			break;
964 	if (external == NULL) {
965 		if ((external = calloc(1, sizeof(*external))) == NULL)
966 			errx(1, "new_external: calloc");
967 		if ((external->command = strdup(command)) == NULL)
968 			errx(1, "new_external: strdup");
969 		external->frequency = frequency;
970 		TAILQ_INIT(&external->expressions);
971 		TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
972 	}
973 	external->prevstatus = -1;
974 	external->refcount++;
975 	return (external);
976 }
977