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