1*08f6ba19Snaddy /* $OpenBSD: parse.y,v 1.56 2021/10/15 15:01:28 naddy Exp $ */
2b73b88efSmcbride
3b73b88efSmcbride /*
4b73b88efSmcbride * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
5b73b88efSmcbride * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
6b73b88efSmcbride * Copyright (c) 2001 Markus Friedl. All rights reserved.
7b73b88efSmcbride * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
8b73b88efSmcbride * Copyright (c) 2001 Theo de Raadt. All rights reserved.
9b73b88efSmcbride *
10b73b88efSmcbride * Permission to use, copy, modify, and distribute this software for any
11b73b88efSmcbride * purpose with or without fee is hereby granted, provided that the above
12b73b88efSmcbride * copyright notice and this permission notice appear in all copies.
13b73b88efSmcbride *
14b73b88efSmcbride * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15b73b88efSmcbride * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16b73b88efSmcbride * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17b73b88efSmcbride * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18b73b88efSmcbride * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19b73b88efSmcbride * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20b73b88efSmcbride * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21b73b88efSmcbride */
22b73b88efSmcbride
23b73b88efSmcbride %{
24b73b88efSmcbride #include <sys/types.h>
25b73b88efSmcbride #include <sys/time.h>
26b73b88efSmcbride #include <sys/socket.h>
2720741916Sderaadt #include <sys/stat.h>
28b73b88efSmcbride #include <netinet/in.h>
29b73b88efSmcbride #include <arpa/inet.h>
30b73b88efSmcbride #include <net/if.h>
31b73b88efSmcbride
32b73b88efSmcbride #include <ctype.h>
3320741916Sderaadt #include <unistd.h>
34b73b88efSmcbride #include <err.h>
35b73b88efSmcbride #include <errno.h>
3620741916Sderaadt #include <limits.h>
37b73b88efSmcbride #include <stdarg.h>
38b73b88efSmcbride #include <stdio.h>
39b73b88efSmcbride #include <string.h>
40b73b88efSmcbride #include <syslog.h>
41b73b88efSmcbride #include <event.h>
42b73b88efSmcbride
43b73b88efSmcbride #include "ifstated.h"
44bfdb9ad4Sbenno #include "log.h"
45b73b88efSmcbride
4620741916Sderaadt TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
4720741916Sderaadt static struct file {
4820741916Sderaadt TAILQ_ENTRY(file) entry;
4920741916Sderaadt FILE *stream;
5020741916Sderaadt char *name;
5120741916Sderaadt int lineno;
5220741916Sderaadt int errors;
53c6004ab9Smpf } *file, *topfile;
5420741916Sderaadt struct file *pushfile(const char *, int);
5520741916Sderaadt int popfile(void);
5620741916Sderaadt int check_file_secrecy(int, const char *);
57b73b88efSmcbride int yyparse(void);
5820741916Sderaadt int yylex(void);
597fe19e9cSdoug int yyerror(const char *, ...)
607fe19e9cSdoug __attribute__((__format__ (printf, 1, 2)))
617fe19e9cSdoug __attribute__((__nonnull__ (1)));
62b73b88efSmcbride int kw_cmp(const void *, const void *);
63b73b88efSmcbride int lookup(char *);
64d5d66eaeSderaadt int lgetc(int);
65b73b88efSmcbride int lungetc(int);
66b73b88efSmcbride int findeol(void);
67b73b88efSmcbride
68b73b88efSmcbride TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
69b73b88efSmcbride struct sym {
7020741916Sderaadt TAILQ_ENTRY(sym) entry;
71b73b88efSmcbride int used;
72b73b88efSmcbride int persist;
73b73b88efSmcbride char *nam;
74b73b88efSmcbride char *val;
75b73b88efSmcbride };
76b73b88efSmcbride int symset(const char *, const char *, int);
77b73b88efSmcbride char *symget(const char *);
7820741916Sderaadt
7920741916Sderaadt static struct ifsd_config *conf;
8020741916Sderaadt char *start_state;
8120741916Sderaadt
8220741916Sderaadt struct ifsd_action *curaction;
83d64c7ac9Sderaadt struct ifsd_state *curstate;
8420741916Sderaadt
8520741916Sderaadt void link_states(struct ifsd_action *);
86b73b88efSmcbride void set_expression_depth(struct ifsd_expression *, int);
87b73b88efSmcbride void init_state(struct ifsd_state *);
8818e4260cSrob struct ifsd_ifstate *new_ifstate(char *, int);
89b73b88efSmcbride struct ifsd_external *new_external(char *, u_int32_t);
90b73b88efSmcbride
91b73b88efSmcbride typedef struct {
92b73b88efSmcbride union {
9339c88d90Sderaadt int64_t number;
94b73b88efSmcbride char *string;
95b73b88efSmcbride struct in_addr addr;
96b73b88efSmcbride
97b73b88efSmcbride struct ifsd_expression *expression;
98b73b88efSmcbride struct ifsd_ifstate *ifstate;
99b73b88efSmcbride struct ifsd_external *external;
100b73b88efSmcbride
101b73b88efSmcbride } v;
102b73b88efSmcbride int lineno;
103b73b88efSmcbride } YYSTYPE;
104b73b88efSmcbride
105b73b88efSmcbride %}
106b73b88efSmcbride
107b73b88efSmcbride %token STATE INITSTATE
108ac553da5Sbenno %token LINK UP DOWN UNKNOWN
10935360db1Spyr %token IF RUN SETSTATE EVERY INIT
110b73b88efSmcbride %left AND OR
111b73b88efSmcbride %left UNARY
112b73b88efSmcbride %token ERROR
113b73b88efSmcbride %token <v.string> STRING
11439c88d90Sderaadt %token <v.number> NUMBER
115b73b88efSmcbride %type <v.string> string
11618e4260cSrob %type <v.string> interface
117b73b88efSmcbride %type <v.ifstate> if_test
118b73b88efSmcbride %type <v.external> ext_test
119b73b88efSmcbride %type <v.expression> expr term
120b73b88efSmcbride %%
121b73b88efSmcbride
122b73b88efSmcbride grammar : /* empty */
123b73b88efSmcbride | grammar '\n'
124b73b88efSmcbride | grammar conf_main '\n'
125b73b88efSmcbride | grammar varset '\n'
126b73b88efSmcbride | grammar action '\n'
127b73b88efSmcbride | grammar state '\n'
12820741916Sderaadt | grammar error '\n' { file->errors++; }
129b73b88efSmcbride ;
130b73b88efSmcbride
131b73b88efSmcbride string : string STRING {
132b73b88efSmcbride if (asprintf(&$$, "%s %s", $1, $2) == -1) {
13332c20dfcShenning free($1);
13432c20dfcShenning free($2);
135b73b88efSmcbride yyerror("string: asprintf");
136b73b88efSmcbride YYERROR;
137b73b88efSmcbride }
138b73b88efSmcbride free($1);
139b73b88efSmcbride free($2);
140b73b88efSmcbride }
141b73b88efSmcbride | STRING
142b73b88efSmcbride ;
143b73b88efSmcbride
144b73b88efSmcbride varset : STRING '=' string {
1450c7b4ca6Sbenno char *s = $1;
146b73b88efSmcbride if (conf->opts & IFSD_OPT_VERBOSE)
147b73b88efSmcbride printf("%s = \"%s\"\n", $1, $3);
1480c7b4ca6Sbenno while (*s++) {
1490c7b4ca6Sbenno if (isspace((unsigned char)*s)) {
1500c7b4ca6Sbenno yyerror("macro name cannot contain "
1510c7b4ca6Sbenno "whitespace");
15216a0a906Skrw free($1);
15316a0a906Skrw free($3);
1540c7b4ca6Sbenno YYERROR;
1550c7b4ca6Sbenno }
1560c7b4ca6Sbenno }
157b73b88efSmcbride if (symset($1, $3, 0) == -1) {
15832c20dfcShenning free($1);
15932c20dfcShenning free($3);
160b73b88efSmcbride yyerror("cannot store variable");
161b73b88efSmcbride YYERROR;
162b73b88efSmcbride }
16332c20dfcShenning free($1);
16432c20dfcShenning free($3);
165b73b88efSmcbride }
166b73b88efSmcbride ;
167b73b88efSmcbride
168b73b88efSmcbride conf_main : INITSTATE STRING {
16932c20dfcShenning start_state = $2;
170b73b88efSmcbride }
171b73b88efSmcbride ;
172b73b88efSmcbride
173b73b88efSmcbride interface : STRING {
17418e4260cSrob if (if_nametoindex($1) == 0) {
175b73b88efSmcbride yyerror("unknown interface %s", $1);
17632c20dfcShenning free($1);
177b73b88efSmcbride YYERROR;
178b73b88efSmcbride }
17918e4260cSrob $$ = $1;
180b73b88efSmcbride }
181b73b88efSmcbride ;
182b73b88efSmcbride
183b73b88efSmcbride optnl : '\n' optnl
184b73b88efSmcbride |
185b73b88efSmcbride ;
186b73b88efSmcbride
187b73b88efSmcbride nl : '\n' optnl /* one newline or more */
188b73b88efSmcbride ;
189b73b88efSmcbride
190b73b88efSmcbride action : RUN STRING {
191b73b88efSmcbride struct ifsd_action *action;
192b73b88efSmcbride
193b73b88efSmcbride if ((action = calloc(1, sizeof(*action))) == NULL)
194b73b88efSmcbride err(1, "action: calloc");
195b73b88efSmcbride action->type = IFSD_ACTION_COMMAND;
19632c20dfcShenning action->act.command = $2;
197b73b88efSmcbride TAILQ_INSERT_TAIL(&curaction->act.c.actions,
198b73b88efSmcbride action, entries);
199b73b88efSmcbride }
200b73b88efSmcbride | SETSTATE STRING {
201b73b88efSmcbride struct ifsd_action *action;
202b73b88efSmcbride
203b73b88efSmcbride if (curstate == NULL) {
20432c20dfcShenning free($2);
205f03638d1Smcbride yyerror("set-state must be used inside 'if'");
206b73b88efSmcbride YYERROR;
207b73b88efSmcbride }
208b73b88efSmcbride if ((action = calloc(1, sizeof(*action))) == NULL)
209b73b88efSmcbride err(1, "action: calloc");
210b73b88efSmcbride action->type = IFSD_ACTION_CHANGESTATE;
21132c20dfcShenning action->act.statename = $2;
212b73b88efSmcbride TAILQ_INSERT_TAIL(&curaction->act.c.actions,
213b73b88efSmcbride action, entries);
214b73b88efSmcbride }
215b73b88efSmcbride | IF {
216b73b88efSmcbride struct ifsd_action *action;
217b73b88efSmcbride
218b73b88efSmcbride if ((action = calloc(1, sizeof(*action))) == NULL)
219b73b88efSmcbride err(1, "action: calloc");
220b73b88efSmcbride action->type = IFSD_ACTION_CONDITION;
221b73b88efSmcbride TAILQ_INIT(&action->act.c.actions);
222b73b88efSmcbride TAILQ_INSERT_TAIL(&curaction->act.c.actions,
223b73b88efSmcbride action, entries);
224b73b88efSmcbride action->parent = curaction;
225b73b88efSmcbride curaction = action;
22642b18258Smpf } expr action_block {
227b73b88efSmcbride set_expression_depth(curaction->act.c.expression, 0);
228b73b88efSmcbride curaction = curaction->parent;
229b73b88efSmcbride }
230b73b88efSmcbride ;
231b73b88efSmcbride
23242b18258Smpf action_block : optnl '{' optnl action_l '}'
23342b18258Smpf | optnl action
23442b18258Smpf ;
23542b18258Smpf
236b73b88efSmcbride action_l : action_l action nl
237b73b88efSmcbride | action nl
238b73b88efSmcbride ;
239b73b88efSmcbride
240b73b88efSmcbride init : INIT {
241b73b88efSmcbride if (curstate != NULL)
242b73b88efSmcbride curaction = curstate->init;
243b73b88efSmcbride else
24420863243Sbenno curaction = conf->initstate.init;
2454726560cSsturm } action_block {
246b73b88efSmcbride if (curstate != NULL)
247138e089fSbenno curaction = curstate->body;
248b73b88efSmcbride else
24920863243Sbenno curaction = conf->initstate.body;
250b73b88efSmcbride }
25197c59c6cSderaadt ;
252b73b88efSmcbride
25342b18258Smpf if_test : interface '.' LINK '.' UP {
254b73b88efSmcbride $$ = new_ifstate($1, IFSD_LINKUP);
255b73b88efSmcbride }
25642b18258Smpf | interface '.' LINK '.' DOWN {
257b73b88efSmcbride $$ = new_ifstate($1, IFSD_LINKDOWN);
258b73b88efSmcbride }
25942b18258Smpf | interface '.' LINK '.' UNKNOWN {
260b73b88efSmcbride $$ = new_ifstate($1, IFSD_LINKUNKNOWN);
261b73b88efSmcbride }
262b73b88efSmcbride ;
263b73b88efSmcbride
26439c88d90Sderaadt ext_test : STRING EVERY NUMBER {
26565cb20dfSderaadt if ($3 <= 0 || $3 > UINT_MAX) {
2667fe19e9cSdoug yyerror("invalid interval: %lld", $3);
26739c88d90Sderaadt free($1);
26839c88d90Sderaadt YYERROR;
26939c88d90Sderaadt }
270b73b88efSmcbride $$ = new_external($1, $3);
27132c20dfcShenning free($1);
272b73b88efSmcbride }
273b73b88efSmcbride ;
274b73b88efSmcbride
275b73b88efSmcbride term : if_test {
276b73b88efSmcbride if (($$ = calloc(1, sizeof(*$$))) == NULL)
277e9514afcSmmcc err(1, NULL);
278b73b88efSmcbride curaction->act.c.expression = $$;
279b73b88efSmcbride $$->type = IFSD_OPER_IFSTATE;
280b73b88efSmcbride $$->u.ifstate = $1;
281b73b88efSmcbride TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
282b73b88efSmcbride }
283b73b88efSmcbride | ext_test {
284b73b88efSmcbride if (($$ = calloc(1, sizeof(*$$))) == NULL)
285e9514afcSmmcc err(1, NULL);
286b73b88efSmcbride curaction->act.c.expression = $$;
287b73b88efSmcbride $$->type = IFSD_OPER_EXTERNAL;
288b73b88efSmcbride $$->u.external = $1;
289b73b88efSmcbride TAILQ_INSERT_TAIL(&$1->expressions, $$, entries);
290b73b88efSmcbride }
291b73b88efSmcbride | '(' expr ')' {
292b73b88efSmcbride $$ = $2;
293b73b88efSmcbride }
294b73b88efSmcbride ;
295b73b88efSmcbride
296b73b88efSmcbride expr : '!' expr %prec UNARY {
297b73b88efSmcbride if (($$ = calloc(1, sizeof(*$$))) == NULL)
298e9514afcSmmcc err(1, NULL);
299b73b88efSmcbride curaction->act.c.expression = $$;
300b73b88efSmcbride $$->type = IFSD_OPER_NOT;
301b73b88efSmcbride $2->parent = $$;
302b73b88efSmcbride $$->right = $2;
303b73b88efSmcbride }
304b73b88efSmcbride | expr AND expr {
305b73b88efSmcbride if (($$ = calloc(1, sizeof(*$$))) == NULL)
306e9514afcSmmcc err(1, NULL);
307b73b88efSmcbride curaction->act.c.expression = $$;
308b73b88efSmcbride $$->type = IFSD_OPER_AND;
309b73b88efSmcbride $1->parent = $$;
310b73b88efSmcbride $3->parent = $$;
311b73b88efSmcbride $$->left = $1;
312b73b88efSmcbride $$->right = $3;
313b73b88efSmcbride }
314b73b88efSmcbride | expr OR expr {
315b73b88efSmcbride if (($$ = calloc(1, sizeof(*$$))) == NULL)
316e9514afcSmmcc err(1, NULL);
317b73b88efSmcbride curaction->act.c.expression = $$;
318b73b88efSmcbride $$->type = IFSD_OPER_OR;
319b73b88efSmcbride $1->parent = $$;
320b73b88efSmcbride $3->parent = $$;
321b73b88efSmcbride $$->left = $1;
322b73b88efSmcbride $$->right = $3;
323b73b88efSmcbride }
324b73b88efSmcbride | term
325b73b88efSmcbride ;
326b73b88efSmcbride
327b73b88efSmcbride state : STATE string {
328b73b88efSmcbride struct ifsd_state *state = NULL;
329b73b88efSmcbride
330b73b88efSmcbride TAILQ_FOREACH(state, &conf->states, entries)
331b73b88efSmcbride if (!strcmp(state->name, $2)) {
332b73b88efSmcbride yyerror("state %s already exists", $2);
33332c20dfcShenning free($2);
334b73b88efSmcbride YYERROR;
335b73b88efSmcbride }
336b73b88efSmcbride if ((state = calloc(1, sizeof(*curstate))) == NULL)
337e9514afcSmmcc err(1, NULL);
338b73b88efSmcbride init_state(state);
33932c20dfcShenning state->name = $2;
340b73b88efSmcbride curstate = state;
341138e089fSbenno curaction = state->body;
342b73b88efSmcbride } optnl '{' optnl stateopts_l '}' {
343b73b88efSmcbride TAILQ_INSERT_TAIL(&conf->states, curstate, entries);
344b73b88efSmcbride curstate = NULL;
34520863243Sbenno curaction = conf->initstate.body;
346b73b88efSmcbride }
347b73b88efSmcbride ;
348b73b88efSmcbride
349b73b88efSmcbride stateopts_l : stateopts_l stateoptsl
350b73b88efSmcbride | stateoptsl
351b73b88efSmcbride ;
352b73b88efSmcbride
353b73b88efSmcbride stateoptsl : init nl
354b73b88efSmcbride | action nl
355b73b88efSmcbride ;
356b73b88efSmcbride
357b73b88efSmcbride %%
358b73b88efSmcbride
359b73b88efSmcbride struct keywords {
360b73b88efSmcbride const char *k_name;
361b73b88efSmcbride int k_val;
362b73b88efSmcbride };
363b73b88efSmcbride
364b73b88efSmcbride int
yyerror(const char * fmt,...)365b73b88efSmcbride yyerror(const char *fmt, ...)
366b73b88efSmcbride {
367b73b88efSmcbride va_list ap;
368e3490c9cSbluhm char *msg;
369b73b88efSmcbride
37020741916Sderaadt file->errors++;
371b73b88efSmcbride va_start(ap, fmt);
372e3490c9cSbluhm if (vasprintf(&msg, fmt, ap) == -1)
373e3490c9cSbluhm fatalx("yyerror vasprintf");
374b73b88efSmcbride va_end(ap);
375e3490c9cSbluhm logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
376e3490c9cSbluhm free(msg);
377b73b88efSmcbride return (0);
378b73b88efSmcbride }
379b73b88efSmcbride
380b73b88efSmcbride int
kw_cmp(const void * k,const void * e)381b73b88efSmcbride kw_cmp(const void *k, const void *e)
382b73b88efSmcbride {
383b73b88efSmcbride return (strcmp(k, ((const struct keywords *)e)->k_name));
384b73b88efSmcbride }
385b73b88efSmcbride
386b73b88efSmcbride int
lookup(char * s)387b73b88efSmcbride lookup(char *s)
388b73b88efSmcbride {
389b73b88efSmcbride /* this has to be sorted always */
390b73b88efSmcbride static const struct keywords keywords[] = {
39142b18258Smpf { "&&", AND},
392b73b88efSmcbride { "down", DOWN},
393b73b88efSmcbride { "every", EVERY},
394b73b88efSmcbride { "if", IF},
395b73b88efSmcbride { "init", INIT},
396b73b88efSmcbride { "init-state", INITSTATE},
397b73b88efSmcbride { "link", LINK},
398b73b88efSmcbride { "run", RUN},
399b73b88efSmcbride { "set-state", SETSTATE},
400b73b88efSmcbride { "state", STATE},
401b73b88efSmcbride { "unknown", UNKNOWN},
40242b18258Smpf { "up", UP},
40342b18258Smpf { "||", OR}
404b73b88efSmcbride };
405b73b88efSmcbride const struct keywords *p;
406b73b88efSmcbride
407b73b88efSmcbride p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
408b73b88efSmcbride sizeof(keywords[0]), kw_cmp);
409b73b88efSmcbride
4105dbd3954Shenning if (p)
411b73b88efSmcbride return (p->k_val);
4125dbd3954Shenning else
413b73b88efSmcbride return (STRING);
414b73b88efSmcbride }
415b73b88efSmcbride
416b73b88efSmcbride #define MAXPUSHBACK 128
417b73b88efSmcbride
418*08f6ba19Snaddy char *parsebuf;
419b73b88efSmcbride int parseindex;
420*08f6ba19Snaddy char pushback_buffer[MAXPUSHBACK];
4214a99a2bcSrob int pushback_index = 0;
422b73b88efSmcbride
423b73b88efSmcbride int
lgetc(int quotec)42420741916Sderaadt lgetc(int quotec)
425b73b88efSmcbride {
426b73b88efSmcbride int c, next;
427b73b88efSmcbride
428b73b88efSmcbride if (parsebuf) {
429b73b88efSmcbride /* Read character from the parsebuffer instead of input. */
430b73b88efSmcbride if (parseindex >= 0) {
431*08f6ba19Snaddy c = (unsigned char)parsebuf[parseindex++];
432b73b88efSmcbride if (c != '\0')
433b73b88efSmcbride return (c);
434b73b88efSmcbride parsebuf = NULL;
435b73b88efSmcbride } else
436b73b88efSmcbride parseindex++;
437b73b88efSmcbride }
438b73b88efSmcbride
439b73b88efSmcbride if (pushback_index)
440*08f6ba19Snaddy return ((unsigned char)pushback_buffer[--pushback_index]);
441b73b88efSmcbride
44220741916Sderaadt if (quotec) {
44320741916Sderaadt if ((c = getc(file->stream)) == EOF) {
444c6004ab9Smpf yyerror("reached end of file while parsing "
445c6004ab9Smpf "quoted string");
446c6004ab9Smpf if (file == topfile || popfile() == EOF)
44720741916Sderaadt return (EOF);
44820741916Sderaadt return (quotec);
44920741916Sderaadt }
450d5d66eaeSderaadt return (c);
451d5d66eaeSderaadt }
452d5d66eaeSderaadt
45320741916Sderaadt while ((c = getc(file->stream)) == '\\') {
45420741916Sderaadt next = getc(file->stream);
455b73b88efSmcbride if (next != '\n') {
456e3bfd77aSderaadt c = next;
457b73b88efSmcbride break;
458b73b88efSmcbride }
45920741916Sderaadt yylval.lineno = file->lineno;
46020741916Sderaadt file->lineno++;
461b73b88efSmcbride }
462b73b88efSmcbride
46320741916Sderaadt while (c == EOF) {
464c6004ab9Smpf if (file == topfile || popfile() == EOF)
46520741916Sderaadt return (EOF);
46620741916Sderaadt c = getc(file->stream);
46720741916Sderaadt }
468b73b88efSmcbride return (c);
469b73b88efSmcbride }
470b73b88efSmcbride
471b73b88efSmcbride int
lungetc(int c)472b73b88efSmcbride lungetc(int c)
473b73b88efSmcbride {
474b73b88efSmcbride if (c == EOF)
475b73b88efSmcbride return (EOF);
476b73b88efSmcbride if (parsebuf) {
477b73b88efSmcbride parseindex--;
478b73b88efSmcbride if (parseindex >= 0)
479b73b88efSmcbride return (c);
480b73b88efSmcbride }
481*08f6ba19Snaddy if (pushback_index + 1 >= MAXPUSHBACK)
482b73b88efSmcbride return (EOF);
483*08f6ba19Snaddy pushback_buffer[pushback_index++] = c;
484*08f6ba19Snaddy return (c);
485b73b88efSmcbride }
486b73b88efSmcbride
487b73b88efSmcbride int
findeol(void)488b73b88efSmcbride findeol(void)
489b73b88efSmcbride {
490b73b88efSmcbride int c;
491b73b88efSmcbride
492b73b88efSmcbride parsebuf = NULL;
493b73b88efSmcbride
494b73b88efSmcbride /* skip to either EOF or the first real EOL */
495b73b88efSmcbride while (1) {
4961a28f514Shenning if (pushback_index)
497*08f6ba19Snaddy c = (unsigned char)pushback_buffer[--pushback_index];
4981a28f514Shenning else
499d5d66eaeSderaadt c = lgetc(0);
500b73b88efSmcbride if (c == '\n') {
50120741916Sderaadt file->lineno++;
502b73b88efSmcbride break;
503b73b88efSmcbride }
504b73b88efSmcbride if (c == EOF)
505b73b88efSmcbride break;
506b73b88efSmcbride }
507b73b88efSmcbride return (ERROR);
508b73b88efSmcbride }
509b73b88efSmcbride
510b73b88efSmcbride int
yylex(void)511b73b88efSmcbride yylex(void)
512b73b88efSmcbride {
513*08f6ba19Snaddy char buf[8096];
514*08f6ba19Snaddy char *p, *val;
51520741916Sderaadt int quotec, next, c;
516b73b88efSmcbride int token;
517b73b88efSmcbride
518b73b88efSmcbride top:
519b73b88efSmcbride p = buf;
5202053f12aSmpf while ((c = lgetc(0)) == ' ' || c == '\t')
521b73b88efSmcbride ; /* nothing */
522b73b88efSmcbride
52320741916Sderaadt yylval.lineno = file->lineno;
524b73b88efSmcbride if (c == '#')
525d5d66eaeSderaadt while ((c = lgetc(0)) != '\n' && c != EOF)
526b73b88efSmcbride ; /* nothing */
527b73b88efSmcbride if (c == '$' && parsebuf == NULL) {
528b73b88efSmcbride while (1) {
529d5d66eaeSderaadt if ((c = lgetc(0)) == EOF)
530b73b88efSmcbride return (0);
531b73b88efSmcbride
532b73b88efSmcbride if (p + 1 >= buf + sizeof(buf) - 1) {
533b73b88efSmcbride yyerror("string too long");
534b73b88efSmcbride return (findeol());
535b73b88efSmcbride }
536b73b88efSmcbride if (isalnum(c) || c == '_') {
537015d7b4dSbenno *p++ = c;
538b73b88efSmcbride continue;
539b73b88efSmcbride }
540b73b88efSmcbride *p = '\0';
541b73b88efSmcbride lungetc(c);
542b73b88efSmcbride break;
543b73b88efSmcbride }
544b73b88efSmcbride val = symget(buf);
545b73b88efSmcbride if (val == NULL) {
546b73b88efSmcbride yyerror("macro '%s' not defined", buf);
547b73b88efSmcbride return (findeol());
548b73b88efSmcbride }
549b73b88efSmcbride parsebuf = val;
550b73b88efSmcbride parseindex = 0;
551b73b88efSmcbride goto top;
552b73b88efSmcbride }
553b73b88efSmcbride
554b73b88efSmcbride switch (c) {
555b73b88efSmcbride case '\'':
556b73b88efSmcbride case '"':
55720741916Sderaadt quotec = c;
558b73b88efSmcbride while (1) {
55920741916Sderaadt if ((c = lgetc(quotec)) == EOF)
560b73b88efSmcbride return (0);
561b73b88efSmcbride if (c == '\n') {
56220741916Sderaadt file->lineno++;
563b73b88efSmcbride continue;
564d5d66eaeSderaadt } else if (c == '\\') {
56520741916Sderaadt if ((next = lgetc(quotec)) == EOF)
566d5d66eaeSderaadt return (0);
567a1533359Ssashan if (next == quotec || next == ' ' ||
568a1533359Ssashan next == '\t')
569d5d66eaeSderaadt c = next;
570daf24110Shenning else if (next == '\n') {
571daf24110Shenning file->lineno++;
572ea014f46Sderaadt continue;
573daf24110Shenning } else
574d5d66eaeSderaadt lungetc(next);
57520741916Sderaadt } else if (c == quotec) {
576d5d66eaeSderaadt *p = '\0';
577d5d66eaeSderaadt break;
57841eef22fSjsg } else if (c == '\0') {
57941eef22fSjsg yyerror("syntax error");
58041eef22fSjsg return (findeol());
581b73b88efSmcbride }
582b73b88efSmcbride if (p + 1 >= buf + sizeof(buf) - 1) {
583b73b88efSmcbride yyerror("string too long");
584b73b88efSmcbride return (findeol());
585b73b88efSmcbride }
586015d7b4dSbenno *p++ = c;
587b73b88efSmcbride }
588b73b88efSmcbride yylval.v.string = strdup(buf);
589b73b88efSmcbride if (yylval.v.string == NULL)
590a062aa9dSkrw err(1, "%s", __func__);
591b73b88efSmcbride return (STRING);
592b73b88efSmcbride }
593b73b88efSmcbride
59439c88d90Sderaadt #define allowed_to_end_number(x) \
5950cf2c9c3Smpf (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
59639c88d90Sderaadt
59739c88d90Sderaadt if (c == '-' || isdigit(c)) {
59839c88d90Sderaadt do {
59939c88d90Sderaadt *p++ = c;
600915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
60139c88d90Sderaadt yyerror("string too long");
60239c88d90Sderaadt return (findeol());
60339c88d90Sderaadt }
604d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && isdigit(c));
60539c88d90Sderaadt lungetc(c);
60639c88d90Sderaadt if (p == buf + 1 && buf[0] == '-')
60739c88d90Sderaadt goto nodigits;
60839c88d90Sderaadt if (c == EOF || allowed_to_end_number(c)) {
60939c88d90Sderaadt const char *errstr = NULL;
61039c88d90Sderaadt
61139c88d90Sderaadt *p = '\0';
61239c88d90Sderaadt yylval.v.number = strtonum(buf, LLONG_MIN,
61339c88d90Sderaadt LLONG_MAX, &errstr);
61439c88d90Sderaadt if (errstr) {
61539c88d90Sderaadt yyerror("\"%s\" invalid number: %s",
61639c88d90Sderaadt buf, errstr);
61739c88d90Sderaadt return (findeol());
61839c88d90Sderaadt }
61939c88d90Sderaadt return (NUMBER);
62039c88d90Sderaadt } else {
62139c88d90Sderaadt nodigits:
62239c88d90Sderaadt while (p > buf + 1)
623*08f6ba19Snaddy lungetc((unsigned char)*--p);
624*08f6ba19Snaddy c = (unsigned char)*--p;
62539c88d90Sderaadt if (c == '-')
62639c88d90Sderaadt return (c);
62739c88d90Sderaadt }
62839c88d90Sderaadt }
62939c88d90Sderaadt
630b73b88efSmcbride #define allowed_in_string(x) \
631b73b88efSmcbride (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
632b73b88efSmcbride x != '{' && x != '}' && \
633b73b88efSmcbride x != '!' && x != '=' && x != '#' && \
63442b18258Smpf x != ',' && x != '.'))
635b73b88efSmcbride
63642b18258Smpf if (isalnum(c) || c == ':' || c == '_' || c == '&' || c == '|') {
637b73b88efSmcbride do {
638b73b88efSmcbride *p++ = c;
639915c3f33Sderaadt if ((size_t)(p-buf) >= sizeof(buf)) {
640b73b88efSmcbride yyerror("string too long");
641b73b88efSmcbride return (findeol());
642b73b88efSmcbride }
643d5d66eaeSderaadt } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
644b73b88efSmcbride lungetc(c);
645b73b88efSmcbride *p = '\0';
646edda3f1eShenning if ((token = lookup(buf)) == STRING)
647edda3f1eShenning if ((yylval.v.string = strdup(buf)) == NULL)
648a062aa9dSkrw err(1, "%s", __func__);
649b73b88efSmcbride return (token);
650b73b88efSmcbride }
651b73b88efSmcbride if (c == '\n') {
65220741916Sderaadt yylval.lineno = file->lineno;
65320741916Sderaadt file->lineno++;
654b73b88efSmcbride }
655b73b88efSmcbride if (c == EOF)
656b73b88efSmcbride return (0);
657b73b88efSmcbride return (c);
658b73b88efSmcbride }
659b73b88efSmcbride
66020741916Sderaadt int
check_file_secrecy(int fd,const char * fname)66120741916Sderaadt check_file_secrecy(int fd, const char *fname)
66220741916Sderaadt {
66320741916Sderaadt struct stat st;
66420741916Sderaadt
66520741916Sderaadt if (fstat(fd, &st)) {
66620741916Sderaadt warn("cannot stat %s", fname);
66720741916Sderaadt return (-1);
66820741916Sderaadt }
66920741916Sderaadt if (st.st_uid != 0 && st.st_uid != getuid()) {
67020741916Sderaadt warnx("%s: owner not root or current user", fname);
67120741916Sderaadt return (-1);
67220741916Sderaadt }
6737140c133Shenning if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
6747140c133Shenning warnx("%s: group writable or world read/writable", fname);
67520741916Sderaadt return (-1);
67620741916Sderaadt }
67720741916Sderaadt return (0);
67820741916Sderaadt }
67920741916Sderaadt
68020741916Sderaadt struct file *
pushfile(const char * name,int secret)68120741916Sderaadt pushfile(const char *name, int secret)
68220741916Sderaadt {
68320741916Sderaadt struct file *nfile;
68420741916Sderaadt
6857fc93de0Stobias if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
6866a3d55f9Skrw warn("%s", __func__);
68720741916Sderaadt return (NULL);
688fc0bc67fSpyr }
6897fc93de0Stobias if ((nfile->name = strdup(name)) == NULL) {
6906a3d55f9Skrw warn("%s", __func__);
6917fc93de0Stobias free(nfile);
6927fc93de0Stobias return (NULL);
6937fc93de0Stobias }
69420741916Sderaadt if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
6956a3d55f9Skrw warn("%s: %s", __func__, nfile->name);
69620741916Sderaadt free(nfile->name);
69720741916Sderaadt free(nfile);
69820741916Sderaadt return (NULL);
69920741916Sderaadt } else if (secret &&
70020741916Sderaadt check_file_secrecy(fileno(nfile->stream), nfile->name)) {
70120741916Sderaadt fclose(nfile->stream);
70220741916Sderaadt free(nfile->name);
70320741916Sderaadt free(nfile);
70420741916Sderaadt return (NULL);
70520741916Sderaadt }
70620741916Sderaadt nfile->lineno = 1;
70720741916Sderaadt TAILQ_INSERT_TAIL(&files, nfile, entry);
70820741916Sderaadt return (nfile);
70920741916Sderaadt }
71020741916Sderaadt
71120741916Sderaadt int
popfile(void)71220741916Sderaadt popfile(void)
71320741916Sderaadt {
71420741916Sderaadt struct file *prev;
71520741916Sderaadt
716c6004ab9Smpf if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
71720741916Sderaadt prev->errors += file->errors;
718c6004ab9Smpf
71920741916Sderaadt TAILQ_REMOVE(&files, file, entry);
72020741916Sderaadt fclose(file->stream);
72120741916Sderaadt free(file->name);
72220741916Sderaadt free(file);
72320741916Sderaadt file = prev;
724c6004ab9Smpf return (file ? 0 : EOF);
72520741916Sderaadt }
72620741916Sderaadt
7278bb56c35Smcbride struct ifsd_config *
parse_config(char * filename,int opts)7288bb56c35Smcbride parse_config(char *filename, int opts)
729b73b88efSmcbride {
73020741916Sderaadt int errors = 0;
731b73b88efSmcbride struct sym *sym, *next;
732b73b88efSmcbride struct ifsd_state *state;
733b73b88efSmcbride
7348bb56c35Smcbride if ((conf = calloc(1, sizeof(struct ifsd_config))) == NULL) {
735a062aa9dSkrw err(1, "%s", __func__);
7368bb56c35Smcbride return (NULL);
7378bb56c35Smcbride }
7388bb56c35Smcbride
73920741916Sderaadt if ((file = pushfile(filename, 0)) == NULL) {
7408bb56c35Smcbride free(conf);
7418bb56c35Smcbride return (NULL);
7428bb56c35Smcbride }
743c6004ab9Smpf topfile = file;
744b73b88efSmcbride
745b73b88efSmcbride TAILQ_INIT(&conf->states);
746b73b88efSmcbride
74720863243Sbenno init_state(&conf->initstate);
74820863243Sbenno curaction = conf->initstate.body;
7498bb56c35Smcbride conf->opts = opts;
750b73b88efSmcbride
751b73b88efSmcbride yyparse();
752b73b88efSmcbride
753b73b88efSmcbride /* Link states */
754b73b88efSmcbride TAILQ_FOREACH(state, &conf->states, entries) {
755b73b88efSmcbride link_states(state->init);
756138e089fSbenno link_states(state->body);
757b73b88efSmcbride }
758b73b88efSmcbride
759172ae0e5Smpf errors = file->errors;
760172ae0e5Smpf popfile();
761172ae0e5Smpf
762b73b88efSmcbride if (start_state != NULL) {
763b73b88efSmcbride TAILQ_FOREACH(state, &conf->states, entries) {
764b73b88efSmcbride if (strcmp(start_state, state->name) == 0) {
765b73b88efSmcbride conf->curstate = state;
766b73b88efSmcbride break;
767b73b88efSmcbride }
768b73b88efSmcbride }
769b73b88efSmcbride if (conf->curstate == NULL)
770b73b88efSmcbride errx(1, "invalid start state %s", start_state);
771b73b88efSmcbride } else {
772b73b88efSmcbride conf->curstate = TAILQ_FIRST(&conf->states);
773b73b88efSmcbride }
774b73b88efSmcbride
775b73b88efSmcbride /* Free macros and check which have not been used. */
77646bca67bSkrw TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
777b73b88efSmcbride if ((conf->opts & IFSD_OPT_VERBOSE2) && !sym->used)
778b73b88efSmcbride fprintf(stderr, "warning: macro '%s' not "
779b73b88efSmcbride "used\n", sym->nam);
780b73b88efSmcbride if (!sym->persist) {
781b73b88efSmcbride free(sym->nam);
782b73b88efSmcbride free(sym->val);
78320741916Sderaadt TAILQ_REMOVE(&symhead, sym, entry);
784b73b88efSmcbride free(sym);
785b73b88efSmcbride }
786b73b88efSmcbride }
787b73b88efSmcbride
7888bb56c35Smcbride if (errors) {
7898bb56c35Smcbride clear_config(conf);
7909e05c8a2Smcbride errors = 0;
7918bb56c35Smcbride return (NULL);
7928bb56c35Smcbride }
793b73b88efSmcbride
7948bb56c35Smcbride return (conf);
795b73b88efSmcbride }
796b73b88efSmcbride
797b73b88efSmcbride void
link_states(struct ifsd_action * action)798b73b88efSmcbride link_states(struct ifsd_action *action)
799b73b88efSmcbride {
800b73b88efSmcbride struct ifsd_action *subaction;
801b73b88efSmcbride
802b73b88efSmcbride switch (action->type) {
803b73b88efSmcbride default:
804b73b88efSmcbride case IFSD_ACTION_COMMAND:
805b73b88efSmcbride break;
806b73b88efSmcbride case IFSD_ACTION_CHANGESTATE: {
807b73b88efSmcbride struct ifsd_state *state;
808b73b88efSmcbride
809b73b88efSmcbride TAILQ_FOREACH(state, &conf->states, entries) {
810b73b88efSmcbride if (strcmp(action->act.statename,
811b73b88efSmcbride state->name) == 0) {
812b73b88efSmcbride action->act.nextstate = state;
813b73b88efSmcbride break;
814b73b88efSmcbride }
815b73b88efSmcbride }
816addc91abSmcbride if (state == NULL) {
817addc91abSmcbride fprintf(stderr, "error: state '%s' not declared\n",
818addc91abSmcbride action->act.statename);
81920741916Sderaadt file->errors++;
820addc91abSmcbride }
821b73b88efSmcbride break;
822b73b88efSmcbride }
823b73b88efSmcbride case IFSD_ACTION_CONDITION:
824b73b88efSmcbride TAILQ_FOREACH(subaction, &action->act.c.actions, entries)
825b73b88efSmcbride link_states(subaction);
826b73b88efSmcbride break;
827b73b88efSmcbride }
828b73b88efSmcbride }
829b73b88efSmcbride
830b73b88efSmcbride int
symset(const char * nam,const char * val,int persist)831b73b88efSmcbride symset(const char *nam, const char *val, int persist)
832b73b88efSmcbride {
833b73b88efSmcbride struct sym *sym;
834b73b88efSmcbride
83554c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) {
83654c95b7aSkrw if (strcmp(nam, sym->nam) == 0)
83754c95b7aSkrw break;
83854c95b7aSkrw }
839b73b88efSmcbride
840b73b88efSmcbride if (sym != NULL) {
841b73b88efSmcbride if (sym->persist == 1)
842b73b88efSmcbride return (0);
843b73b88efSmcbride else {
844b73b88efSmcbride free(sym->nam);
845b73b88efSmcbride free(sym->val);
84620741916Sderaadt TAILQ_REMOVE(&symhead, sym, entry);
847b73b88efSmcbride free(sym);
848b73b88efSmcbride }
849b73b88efSmcbride }
850b73b88efSmcbride if ((sym = calloc(1, sizeof(*sym))) == NULL)
851b73b88efSmcbride return (-1);
852b73b88efSmcbride
853b73b88efSmcbride sym->nam = strdup(nam);
854b73b88efSmcbride if (sym->nam == NULL) {
855b73b88efSmcbride free(sym);
856b73b88efSmcbride return (-1);
857b73b88efSmcbride }
858b73b88efSmcbride sym->val = strdup(val);
859b73b88efSmcbride if (sym->val == NULL) {
860b73b88efSmcbride free(sym->nam);
861b73b88efSmcbride free(sym);
862b73b88efSmcbride return (-1);
863b73b88efSmcbride }
864b73b88efSmcbride sym->used = 0;
865b73b88efSmcbride sym->persist = persist;
86620741916Sderaadt TAILQ_INSERT_TAIL(&symhead, sym, entry);
867b73b88efSmcbride return (0);
868b73b88efSmcbride }
869b73b88efSmcbride
870b73b88efSmcbride int
cmdline_symset(char * s)871b73b88efSmcbride cmdline_symset(char *s)
872b73b88efSmcbride {
873b73b88efSmcbride char *sym, *val;
874b73b88efSmcbride int ret;
875b73b88efSmcbride
876b73b88efSmcbride if ((val = strrchr(s, '=')) == NULL)
877b73b88efSmcbride return (-1);
878ed1b9eb8Smiko sym = strndup(s, val - s);
879ed1b9eb8Smiko if (sym == NULL)
880a062aa9dSkrw err(1, "%s", __func__);
881b73b88efSmcbride ret = symset(sym, val + 1, 1);
882b73b88efSmcbride free(sym);
883b73b88efSmcbride
884b73b88efSmcbride return (ret);
885b73b88efSmcbride }
886b73b88efSmcbride
887b73b88efSmcbride char *
symget(const char * nam)888b73b88efSmcbride symget(const char *nam)
889b73b88efSmcbride {
890b73b88efSmcbride struct sym *sym;
891b73b88efSmcbride
89254c95b7aSkrw TAILQ_FOREACH(sym, &symhead, entry) {
893b73b88efSmcbride if (strcmp(nam, sym->nam) == 0) {
894b73b88efSmcbride sym->used = 1;
895b73b88efSmcbride return (sym->val);
896b73b88efSmcbride }
89754c95b7aSkrw }
898b73b88efSmcbride return (NULL);
899b73b88efSmcbride }
900b73b88efSmcbride
901b73b88efSmcbride void
set_expression_depth(struct ifsd_expression * expression,int depth)902b73b88efSmcbride set_expression_depth(struct ifsd_expression *expression, int depth)
903b73b88efSmcbride {
904b73b88efSmcbride expression->depth = depth;
905b73b88efSmcbride if (conf->maxdepth < depth)
906b73b88efSmcbride conf->maxdepth = depth;
907b73b88efSmcbride if (expression->left != NULL)
908b73b88efSmcbride set_expression_depth(expression->left, depth + 1);
909b73b88efSmcbride if (expression->right != NULL)
910b73b88efSmcbride set_expression_depth(expression->right, depth + 1);
911b73b88efSmcbride }
912b73b88efSmcbride
913b73b88efSmcbride void
init_state(struct ifsd_state * state)914b73b88efSmcbride init_state(struct ifsd_state *state)
915b73b88efSmcbride {
916b73b88efSmcbride TAILQ_INIT(&state->interface_states);
917b73b88efSmcbride TAILQ_INIT(&state->external_tests);
918b73b88efSmcbride
919b73b88efSmcbride if ((state->init = calloc(1, sizeof(*state->init))) == NULL)
920a062aa9dSkrw err(1, "%s", __func__);
921b73b88efSmcbride state->init->type = IFSD_ACTION_CONDITION;
922b73b88efSmcbride TAILQ_INIT(&state->init->act.c.actions);
923b73b88efSmcbride
924138e089fSbenno if ((state->body = calloc(1, sizeof(*state->body))) == NULL)
925a062aa9dSkrw err(1, "%s", __func__);
926138e089fSbenno state->body->type = IFSD_ACTION_CONDITION;
927138e089fSbenno TAILQ_INIT(&state->body->act.c.actions);
928b73b88efSmcbride }
929b73b88efSmcbride
930b73b88efSmcbride struct ifsd_ifstate *
new_ifstate(char * ifname,int s)93118e4260cSrob new_ifstate(char *ifname, int s)
932b73b88efSmcbride {
933b73b88efSmcbride struct ifsd_ifstate *ifstate = NULL;
934b73b88efSmcbride struct ifsd_state *state;
935b73b88efSmcbride
936b73b88efSmcbride if (curstate != NULL)
937b73b88efSmcbride state = curstate;
938b73b88efSmcbride else
93920863243Sbenno state = &conf->initstate;
940b73b88efSmcbride
941b73b88efSmcbride TAILQ_FOREACH(ifstate, &state->interface_states, entries)
94218e4260cSrob if (strcmp(ifstate->ifname, ifname) == 0 &&
94318e4260cSrob ifstate->ifstate == s)
944b73b88efSmcbride break;
945b73b88efSmcbride if (ifstate == NULL) {
946b73b88efSmcbride if ((ifstate = calloc(1, sizeof(*ifstate))) == NULL)
947a062aa9dSkrw err(1, "%s", __func__);
94818e4260cSrob if (strlcpy(ifstate->ifname, ifname,
94918e4260cSrob sizeof(ifstate->ifname)) >= sizeof(ifstate->ifname))
95018e4260cSrob errx(1, "ifname strlcpy truncation");
95118e4260cSrob free(ifname);
952b73b88efSmcbride ifstate->ifstate = s;
953b73b88efSmcbride TAILQ_INIT(&ifstate->expressions);
954b73b88efSmcbride TAILQ_INSERT_TAIL(&state->interface_states, ifstate, entries);
955b73b88efSmcbride }
956b73b88efSmcbride ifstate->prevstate = -1;
957b73b88efSmcbride ifstate->refcount++;
958b73b88efSmcbride return (ifstate);
959b73b88efSmcbride }
960b73b88efSmcbride
961b73b88efSmcbride struct ifsd_external *
new_external(char * command,u_int32_t frequency)962b73b88efSmcbride new_external(char *command, u_int32_t frequency)
963b73b88efSmcbride {
964b73b88efSmcbride struct ifsd_external *external = NULL;
965b73b88efSmcbride struct ifsd_state *state;
966b73b88efSmcbride
967b73b88efSmcbride if (curstate != NULL)
968b73b88efSmcbride state = curstate;
969b73b88efSmcbride else
97020863243Sbenno state = &conf->initstate;
971b73b88efSmcbride
972b73b88efSmcbride TAILQ_FOREACH(external, &state->external_tests, entries)
973b73b88efSmcbride if (strcmp(external->command, command) == 0 &&
974b73b88efSmcbride external->frequency == frequency)
975b73b88efSmcbride break;
976b73b88efSmcbride if (external == NULL) {
977b73b88efSmcbride if ((external = calloc(1, sizeof(*external))) == NULL)
978a062aa9dSkrw err(1, "%s", __func__);
979b73b88efSmcbride if ((external->command = strdup(command)) == NULL)
980a062aa9dSkrw err(1, "%s", __func__);
981b73b88efSmcbride external->frequency = frequency;
982b73b88efSmcbride TAILQ_INIT(&external->expressions);
983b73b88efSmcbride TAILQ_INSERT_TAIL(&state->external_tests, external, entries);
984b73b88efSmcbride }
985b73b88efSmcbride external->prevstatus = -1;
986b73b88efSmcbride external->refcount++;
987b73b88efSmcbride return (external);
988b73b88efSmcbride }
989