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