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