GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ifstated/parse.y Lines: 0 338 0.0 %
Date: 2017-11-13 Branches: 0 264 0.0 %

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