xref: /openbsd/usr.sbin/ntpd/parse.y (revision d415bd75)
1 /*	$OpenBSD: parse.y,v 1.78 2021/10/15 15:01:28 naddy Exp $ */
2 
3 /*
4  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
6  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
7  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 %{
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <syslog.h>
36 
37 #include "ntpd.h"
38 
39 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
40 static struct file {
41 	TAILQ_ENTRY(file)	 entry;
42 	FILE			*stream;
43 	char			*name;
44 	int			 lineno;
45 	int			 errors;
46 } *file, *topfile;
47 struct file	*pushfile(const char *);
48 int		 popfile(void);
49 int		 yyparse(void);
50 int		 yylex(void);
51 int		 yyerror(const char *, ...)
52     __attribute__((__format__ (printf, 1, 2)))
53     __attribute__((__nonnull__ (1)));
54 int		 kw_cmp(const void *, const void *);
55 int		 lookup(char *);
56 int		 lgetc(int);
57 int		 lungetc(int);
58 int		 findeol(void);
59 
60 struct sockaddr_in		 query_addr4;
61 struct sockaddr_in6		 query_addr6;
62 int				 poolseqnum;
63 
64 struct opts {
65 	int		weight;
66 	int		correction;
67 	int		stratum;
68 	int		rtable;
69 	int		trusted;
70 	char		*refstr;
71 } opts;
72 void		opts_default(void);
73 
74 typedef struct {
75 	union {
76 		int64_t			 number;
77 		char			*string;
78 		struct ntp_addr_wrap	*addr;
79 		struct opts		 opts;
80 	} v;
81 	int lineno;
82 } YYSTYPE;
83 
84 %}
85 
86 %token	LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY TRUSTED
87 %token	SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
88 %token	ERROR
89 %token	<v.string>		STRING
90 %token	<v.number>		NUMBER
91 %type	<v.addr>		address url urllist
92 %type	<v.opts>		listen_opts listen_opts_l listen_opt
93 %type	<v.opts>		server_opts server_opts_l server_opt
94 %type	<v.opts>		sensor_opts sensor_opts_l sensor_opt
95 %type	<v.opts>		correction
96 %type	<v.opts>		rtable
97 %type	<v.opts>		refid
98 %type	<v.opts>		stratum
99 %type	<v.opts>		weight
100 %type	<v.opts>		trusted
101 %%
102 
103 grammar		: /* empty */
104 		| grammar '\n'
105 		| grammar main '\n'
106 		| grammar error '\n'		{ file->errors++; }
107 		;
108 
109 main		: LISTEN ON address listen_opts	{
110 			struct listen_addr	*la;
111 			struct ntp_addr		*h, *next;
112 
113 			if ((h = $3->a) == NULL &&
114 			    (host_dns($3->name, 0, &h) == -1 || !h)) {
115 				yyerror("could not resolve \"%s\"", $3->name);
116 				free($3->name);
117 				free($3);
118 				YYERROR;
119 			}
120 
121 			for (; h != NULL; h = next) {
122 				next = h->next;
123 				la = calloc(1, sizeof(struct listen_addr));
124 				if (la == NULL)
125 					fatal("listen on calloc");
126 				la->fd = -1;
127 				la->rtable = $4.rtable;
128 				memcpy(&la->sa, &h->ss,
129 				    sizeof(struct sockaddr_storage));
130 				TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
131 				    entry);
132 				free(h);
133 			}
134 			free($3->name);
135 			free($3);
136 		}
137 		| QUERY FROM STRING {
138 			struct sockaddr_in sin4;
139 			struct sockaddr_in6 sin6;
140 
141 			memset(&sin4, 0, sizeof(sin4));
142 			sin4.sin_family = AF_INET;
143 			sin4.sin_len = sizeof(struct sockaddr_in);
144 			memset(&sin6, 0, sizeof(sin6));
145 			sin6.sin6_family = AF_INET6;
146 			sin6.sin6_len = sizeof(struct sockaddr_in6);
147 
148 			if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1)
149 				memcpy(&query_addr4, &sin4, sin4.sin_len);
150 			else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1)
151 				memcpy(&query_addr6, &sin6, sin6.sin6_len);
152 			else {
153 				yyerror("invalid IPv4 or IPv6 address: %s\n",
154 				    $3);
155 				free($3);
156 				YYERROR;
157 			}
158 
159 			free($3);
160 		}
161 		| SERVERS address server_opts	{
162 			struct ntp_peer		*p;
163 			struct ntp_addr		*h, *next;
164 
165 			h = $2->a;
166 			do {
167 				if (h != NULL) {
168 					next = h->next;
169 					if (h->ss.ss_family != AF_INET &&
170 					    h->ss.ss_family != AF_INET6) {
171 						yyerror("IPv4 or IPv6 address "
172 						    "or hostname expected");
173 						free(h);
174 						free($2->name);
175 						free($2);
176 						YYERROR;
177 					}
178 					h->next = NULL;
179 				} else
180 					next = NULL;
181 
182 				p = new_peer();
183 				p->weight = $3.weight;
184 				p->trusted = $3.trusted;
185 				conf->trusted_peers = conf->trusted_peers ||
186 				    $3.trusted;
187 				p->query_addr4 = query_addr4;
188 				p->query_addr6 = query_addr6;
189 				p->addr = h;
190 				p->addr_head.a = h;
191 				p->addr_head.pool = ++poolseqnum;
192 				p->addr_head.name = strdup($2->name);
193 				if (p->addr_head.name == NULL)
194 					fatal(NULL);
195 				if (p->addr != NULL)
196 					p->state = STATE_DNS_DONE;
197 				TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
198 				h = next;
199 			} while (h != NULL);
200 
201 			free($2->name);
202 			free($2);
203 		}
204 		| SERVER address server_opts {
205 			struct ntp_peer		*p;
206 			struct ntp_addr		*h, *next;
207 
208 			p = new_peer();
209 			for (h = $2->a; h != NULL; h = next) {
210 				next = h->next;
211 				if (h->ss.ss_family != AF_INET &&
212 				    h->ss.ss_family != AF_INET6) {
213 					yyerror("IPv4 or IPv6 address "
214 					    "or hostname expected");
215 					free(h);
216 					free(p);
217 					free($2->name);
218 					free($2);
219 					YYERROR;
220 				}
221 				h->next = p->addr;
222 				p->addr = h;
223 			}
224 
225 			p->weight = $3.weight;
226 			p->trusted = $3.trusted;
227 			conf->trusted_peers = conf->trusted_peers ||
228 			    $3.trusted;
229 			p->query_addr4 = query_addr4;
230 			p->query_addr6 = query_addr6;
231 			p->addr_head.a = p->addr;
232 			p->addr_head.pool = 0;
233 			p->addr_head.name = strdup($2->name);
234 			if (p->addr_head.name == NULL)
235 				fatal(NULL);
236 			if (p->addr != NULL)
237 				p->state = STATE_DNS_DONE;
238 			TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
239 			free($2->name);
240 			free($2);
241 		}
242 		| CONSTRAINTS FROM url		{
243 			struct constraint	*p;
244 			struct ntp_addr		*h, *next;
245 
246 			h = $3->a;
247 			do {
248 				if (h != NULL) {
249 					next = h->next;
250 					if (h->ss.ss_family != AF_INET &&
251 					    h->ss.ss_family != AF_INET6) {
252 						yyerror("IPv4 or IPv6 address "
253 						    "or hostname expected");
254 						free(h);
255 						free($3->name);
256 						free($3->path);
257 						free($3);
258 						YYERROR;
259 					}
260 					h->next = NULL;
261 				} else
262 					next = NULL;
263 
264 				p = new_constraint();
265 				p->addr = h;
266 				p->addr_head.a = h;
267 				p->addr_head.pool = ++poolseqnum;
268 				p->addr_head.name = strdup($3->name);
269 				p->addr_head.path = strdup($3->path);
270 				if (p->addr_head.name == NULL ||
271 				    p->addr_head.path == NULL)
272 					fatal(NULL);
273 				if (p->addr != NULL)
274 					p->state = STATE_DNS_DONE;
275 				constraint_add(p);
276 				h = next;
277 			} while (h != NULL);
278 
279 			free($3->name);
280 			free($3);
281 		}
282 		| CONSTRAINT FROM urllist		{
283 			struct constraint	*p;
284 			struct ntp_addr		*h, *next;
285 
286 			p = new_constraint();
287 			for (h = $3->a; h != NULL; h = next) {
288 				next = h->next;
289 				if (h->ss.ss_family != AF_INET &&
290 				    h->ss.ss_family != AF_INET6) {
291 					yyerror("IPv4 or IPv6 address "
292 					    "or hostname expected");
293 					free(h);
294 					free(p);
295 					free($3->name);
296 					free($3->path);
297 					free($3);
298 					YYERROR;
299 				}
300 				h->next = p->addr;
301 				p->addr = h;
302 			}
303 
304 			p->addr_head.a = p->addr;
305 			p->addr_head.pool = 0;
306 			p->addr_head.name = strdup($3->name);
307 			p->addr_head.path = strdup($3->path);
308 			if (p->addr_head.name == NULL ||
309 			    p->addr_head.path == NULL)
310 				fatal(NULL);
311 			if (p->addr != NULL)
312 				p->state = STATE_DNS_DONE;
313 			constraint_add(p);
314 			free($3->name);
315 			free($3);
316 		}
317 		| SENSOR STRING	sensor_opts {
318 			struct ntp_conf_sensor	*s;
319 
320 			s = new_sensor($2);
321 			s->weight = $3.weight;
322 			s->correction = $3.correction;
323 			s->refstr = $3.refstr;
324 			s->stratum = $3.stratum;
325 			s->trusted = $3.trusted;
326 			conf->trusted_sensors = conf->trusted_sensors ||
327 			    $3.trusted;
328 			free($2);
329 			TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
330 		}
331 		;
332 
333 address		: STRING		{
334 			if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
335 			    NULL)
336 				fatal(NULL);
337 			host($1, &$$->a);
338 			$$->name = $1;
339 		}
340 		;
341 
342 urllist		: urllist address {
343 			struct ntp_addr *p, *q = NULL;
344 			struct in_addr ina;
345 			struct in6_addr in6a;
346 
347 			if (inet_pton(AF_INET, $2->name, &ina) != 1 &&
348 			    inet_pton(AF_INET6, $2->name, &in6a) != 1) {
349 				yyerror("url can only be followed by IP "
350 				    "addresses");
351 				free($2->name);
352 				free($2);
353 				YYERROR;
354 			}
355 			p = $2->a;
356 			while (p != NULL) {
357 				q = p;
358 				p = p->next;
359 			}
360 			if (q != NULL) {
361 				q->next = $1->a;
362 				$1->a = $2->a;
363 				free($2);
364 			}
365 			$$ = $1;
366 		}
367 		| url {
368 			$$ = $1;
369 		}
370 		;
371 
372 url		: STRING		{
373 			char	*hname, *path;
374 
375 			if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
376 			    NULL)
377 				fatal("calloc");
378 
379 			if (strncmp("https://", $1,
380 			    strlen("https://")) != 0) {
381 				host($1, &$$->a);
382 				$$->name = $1;
383 			} else {
384 				hname = $1 + strlen("https://");
385 
386 				path = hname + strcspn(hname, "/\\");
387 				if (*path != '\0') {
388 					if (($$->path = strdup(path)) == NULL)
389 						fatal("strdup");
390 					*path = '\0';
391 				}
392 				host(hname, &$$->a);
393 				if (($$->name = strdup(hname)) == NULL)
394 					fatal("strdup");
395 			}
396 			if ($$->path == NULL &&
397 			    ($$->path = strdup("/")) == NULL)
398 				fatal("strdup");
399 		}
400 		;
401 
402 listen_opts	:	{ opts_default(); }
403 		  listen_opts_l
404 			{ $$ = opts; }
405 		|	{ opts_default(); $$ = opts; }
406 		;
407 listen_opts_l	: listen_opts_l listen_opt
408 		| listen_opt
409 		;
410 listen_opt	: rtable
411 		;
412 
413 server_opts	:	{ opts_default(); }
414 		  server_opts_l
415 			{ $$ = opts; }
416 		|	{ opts_default(); $$ = opts; }
417 		;
418 server_opts_l	: server_opts_l server_opt
419 		| server_opt
420 		;
421 server_opt	: weight
422 		| trusted
423 		;
424 
425 sensor_opts	:	{ opts_default(); }
426 		  sensor_opts_l
427 			{ $$ = opts; }
428 		|	{ opts_default(); $$ = opts; }
429 		;
430 sensor_opts_l	: sensor_opts_l sensor_opt
431 		| sensor_opt
432 		;
433 sensor_opt	: correction
434 		| refid
435 		| stratum
436 		| weight
437 		| trusted
438 		;
439 
440 correction	: CORRECTION NUMBER {
441 			if ($2 < -127000000 || $2 > 127000000) {
442 				yyerror("correction must be between "
443 				    "-127000000 and 127000000 microseconds");
444 				YYERROR;
445 			}
446 			opts.correction = $2;
447 		}
448 		;
449 
450 refid		: REFID STRING {
451 			size_t l = strlen($2);
452 
453 			if (l < 1 || l > 4) {
454 				yyerror("refid must be 1 to 4 characters");
455 				free($2);
456 				YYERROR;
457 			}
458 			opts.refstr = $2;
459 		}
460 		;
461 
462 stratum		: STRATUM NUMBER {
463 			if ($2 < 1 || $2 > 15) {
464 				yyerror("stratum must be between "
465 				    "1 and 15");
466 				YYERROR;
467 			}
468 			opts.stratum = $2;
469 		}
470 		;
471 
472 weight		: WEIGHT NUMBER	{
473 			if ($2 < 1 || $2 > 10) {
474 				yyerror("weight must be between 1 and 10");
475 				YYERROR;
476 			}
477 			opts.weight = $2;
478 		}
479 rtable		: RTABLE NUMBER {
480 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
481 				yyerror("rtable must be between 1"
482 				    " and RT_TABLEID_MAX");
483 				YYERROR;
484 			}
485 			opts.rtable = $2;
486 		}
487 		;
488 
489 trusted		: TRUSTED	{
490 			opts.trusted = 1;
491 		}
492 
493 %%
494 
495 void
496 opts_default(void)
497 {
498 	memset(&opts, 0, sizeof opts);
499 	opts.weight = 1;
500 	opts.stratum = 1;
501 }
502 
503 struct keywords {
504 	const char	*k_name;
505 	int		 k_val;
506 };
507 
508 int
509 yyerror(const char *fmt, ...)
510 {
511 	va_list		 ap;
512 	char		*msg;
513 
514 	file->errors++;
515 	va_start(ap, fmt);
516 	if (vasprintf(&msg, fmt, ap) == -1)
517 		fatalx("yyerror vasprintf");
518 	va_end(ap);
519 	log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
520 	free(msg);
521 	return (0);
522 }
523 
524 int
525 kw_cmp(const void *k, const void *e)
526 {
527 	return (strcmp(k, ((const struct keywords *)e)->k_name));
528 }
529 
530 int
531 lookup(char *s)
532 {
533 	/* this has to be sorted always */
534 	static const struct keywords keywords[] = {
535 		{ "constraint",		CONSTRAINT},
536 		{ "constraints",	CONSTRAINTS},
537 		{ "correction",		CORRECTION},
538 		{ "from",		FROM},
539 		{ "listen",		LISTEN},
540 		{ "on",			ON},
541 		{ "query",		QUERY},
542 		{ "refid",		REFID},
543 		{ "rtable",		RTABLE},
544 		{ "sensor",		SENSOR},
545 		{ "server",		SERVER},
546 		{ "servers",		SERVERS},
547 		{ "stratum",		STRATUM},
548 		{ "trusted",		TRUSTED},
549 		{ "weight",		WEIGHT}
550 	};
551 	const struct keywords	*p;
552 
553 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
554 	    sizeof(keywords[0]), kw_cmp);
555 
556 	if (p)
557 		return (p->k_val);
558 	else
559 		return (STRING);
560 }
561 
562 #define MAXPUSHBACK	128
563 
564 char	*parsebuf;
565 int	 parseindex;
566 char	 pushback_buffer[MAXPUSHBACK];
567 int	 pushback_index = 0;
568 
569 int
570 lgetc(int quotec)
571 {
572 	int		c, next;
573 
574 	if (parsebuf) {
575 		/* Read character from the parsebuffer instead of input. */
576 		if (parseindex >= 0) {
577 			c = (unsigned char)parsebuf[parseindex++];
578 			if (c != '\0')
579 				return (c);
580 			parsebuf = NULL;
581 		} else
582 			parseindex++;
583 	}
584 
585 	if (pushback_index)
586 		return ((unsigned char)pushback_buffer[--pushback_index]);
587 
588 	if (quotec) {
589 		if ((c = getc(file->stream)) == EOF) {
590 			yyerror("reached end of file while parsing "
591 			    "quoted string");
592 			if (file == topfile || popfile() == EOF)
593 				return (EOF);
594 			return (quotec);
595 		}
596 		return (c);
597 	}
598 
599 	while ((c = getc(file->stream)) == '\\') {
600 		next = getc(file->stream);
601 		if (next != '\n') {
602 			c = next;
603 			break;
604 		}
605 		yylval.lineno = file->lineno;
606 		file->lineno++;
607 	}
608 
609 	while (c == EOF) {
610 		if (file == topfile || popfile() == EOF)
611 			return (EOF);
612 		c = getc(file->stream);
613 	}
614 	return (c);
615 }
616 
617 int
618 lungetc(int c)
619 {
620 	if (c == EOF)
621 		return (EOF);
622 	if (parsebuf) {
623 		parseindex--;
624 		if (parseindex >= 0)
625 			return (c);
626 	}
627 	if (pushback_index + 1 >= MAXPUSHBACK)
628 		return (EOF);
629 	pushback_buffer[pushback_index++] = c;
630 	return (c);
631 }
632 
633 int
634 findeol(void)
635 {
636 	int	c;
637 
638 	parsebuf = NULL;
639 
640 	/* skip to either EOF or the first real EOL */
641 	while (1) {
642 		if (pushback_index)
643 			c = (unsigned char)pushback_buffer[--pushback_index];
644 		else
645 			c = lgetc(0);
646 		if (c == '\n') {
647 			file->lineno++;
648 			break;
649 		}
650 		if (c == EOF)
651 			break;
652 	}
653 	return (ERROR);
654 }
655 
656 int
657 yylex(void)
658 {
659 	char	 buf[8096];
660 	char	*p;
661 	int	 quotec, next, c;
662 	int	 token;
663 
664 	p = buf;
665 	while ((c = lgetc(0)) == ' ' || c == '\t')
666 		; /* nothing */
667 
668 	yylval.lineno = file->lineno;
669 	if (c == '#')
670 		while ((c = lgetc(0)) != '\n' && c != EOF)
671 			; /* nothing */
672 
673 	switch (c) {
674 	case '\'':
675 	case '"':
676 		quotec = c;
677 		while (1) {
678 			if ((c = lgetc(quotec)) == EOF)
679 				return (0);
680 			if (c == '\n') {
681 				file->lineno++;
682 				continue;
683 			} else if (c == '\\') {
684 				if ((next = lgetc(quotec)) == EOF)
685 					return (0);
686 				if (next == quotec || next == ' ' ||
687 				    next == '\t')
688 					c = next;
689 				else if (next == '\n') {
690 					file->lineno++;
691 					continue;
692 				} else
693 					lungetc(next);
694 			} else if (c == quotec) {
695 				*p = '\0';
696 				break;
697 			} else if (c == '\0') {
698 				yyerror("syntax error");
699 				return (findeol());
700 			}
701 			if (p + 1 >= buf + sizeof(buf) - 1) {
702 				yyerror("string too long");
703 				return (findeol());
704 			}
705 			*p++ = c;
706 		}
707 		yylval.v.string = strdup(buf);
708 		if (yylval.v.string == NULL)
709 			fatal("yylex: strdup");
710 		return (STRING);
711 	}
712 
713 #define allowed_to_end_number(x) \
714 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
715 
716 	if (c == '-' || isdigit(c)) {
717 		do {
718 			*p++ = c;
719 			if ((size_t)(p-buf) >= sizeof(buf)) {
720 				yyerror("string too long");
721 				return (findeol());
722 			}
723 		} while ((c = lgetc(0)) != EOF && isdigit(c));
724 		lungetc(c);
725 		if (p == buf + 1 && buf[0] == '-')
726 			goto nodigits;
727 		if (c == EOF || allowed_to_end_number(c)) {
728 			const char *errstr = NULL;
729 
730 			*p = '\0';
731 			yylval.v.number = strtonum(buf, LLONG_MIN,
732 			    LLONG_MAX, &errstr);
733 			if (errstr) {
734 				yyerror("\"%s\" invalid number: %s",
735 				    buf, errstr);
736 				return (findeol());
737 			}
738 			return (NUMBER);
739 		} else {
740 nodigits:
741 			while (p > buf + 1)
742 				lungetc((unsigned char)*--p);
743 			c = (unsigned char)*--p;
744 			if (c == '-')
745 				return (c);
746 		}
747 	}
748 
749 #define allowed_in_string(x) \
750 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
751 	x != '{' && x != '}' && x != '<' && x != '>' && \
752 	x != '!' && x != '=' && x != '/' && x != '#' && \
753 	x != ','))
754 
755 	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
756 		do {
757 			*p++ = c;
758 			if ((size_t)(p-buf) >= sizeof(buf)) {
759 				yyerror("string too long");
760 				return (findeol());
761 			}
762 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
763 		lungetc(c);
764 		*p = '\0';
765 		if ((token = lookup(buf)) == STRING)
766 			if ((yylval.v.string = strdup(buf)) == NULL)
767 				fatal("yylex: strdup");
768 		return (token);
769 	}
770 	if (c == '\n') {
771 		yylval.lineno = file->lineno;
772 		file->lineno++;
773 	}
774 	if (c == EOF)
775 		return (0);
776 	return (c);
777 }
778 
779 struct file *
780 pushfile(const char *name)
781 {
782 	struct file	*nfile;
783 
784 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
785 		log_warn("%s", __func__);
786 		return (NULL);
787 	}
788 	if ((nfile->name = strdup(name)) == NULL) {
789 		log_warn("%s", __func__);
790 		free(nfile);
791 		return (NULL);
792 	}
793 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
794 		log_warn("%s: %s", __func__, nfile->name);
795 		free(nfile->name);
796 		free(nfile);
797 		return (NULL);
798 	}
799 	nfile->lineno = 1;
800 	TAILQ_INSERT_TAIL(&files, nfile, entry);
801 	return (nfile);
802 }
803 
804 int
805 popfile(void)
806 {
807 	struct file	*prev;
808 
809 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
810 		prev->errors += file->errors;
811 
812 	TAILQ_REMOVE(&files, file, entry);
813 	fclose(file->stream);
814 	free(file->name);
815 	free(file);
816 	file = prev;
817 	return (file ? 0 : EOF);
818 }
819 
820 int
821 parse_config(const char *filename, struct ntpd_conf *xconf)
822 {
823 	int		 errors = 0;
824 
825 	conf = xconf;
826 	TAILQ_INIT(&conf->listen_addrs);
827 	TAILQ_INIT(&conf->ntp_peers);
828 	TAILQ_INIT(&conf->ntp_conf_sensors);
829 	TAILQ_INIT(&conf->constraints);
830 
831 	if ((file = pushfile(filename)) == NULL) {
832 		return (-1);
833 	}
834 	topfile = file;
835 
836 	yyparse();
837 	errors = file->errors;
838 	popfile();
839 
840 	return (errors ? -1 : 0);
841 }
842