GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ntpd/parse.y Lines: 0 174 0.0 %
Date: 2017-11-07 Branches: 0 143 0.0 %

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