parse.y revision 246922
1%{
2/*-
3 * Copyright (c) 2009-2010 The FreeBSD Foundation
4 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sbin/hastd/parse.y 246922 2013-02-17 21:12:34Z pjd $
32 */
33
34#include <sys/param.h>	/* MAXHOSTNAMELEN */
35#include <sys/queue.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38
39#include <arpa/inet.h>
40
41#include <err.h>
42#include <errno.h>
43#include <stdio.h>
44#include <string.h>
45#include <sysexits.h>
46#include <unistd.h>
47
48#include <pjdlog.h>
49
50#include "hast.h"
51
52extern int depth;
53extern int lineno;
54
55extern FILE *yyin;
56extern char *yytext;
57
58static struct hastd_config *lconfig;
59static struct hast_resource *curres;
60static bool mynode, hadmynode;
61
62static char depth0_control[HAST_ADDRSIZE];
63static char depth0_pidfile[PATH_MAX];
64static char depth0_listen_tcp4[HAST_ADDRSIZE];
65static char depth0_listen_tcp6[HAST_ADDRSIZE];
66static TAILQ_HEAD(, hastd_listen) depth0_listen;
67static int depth0_replication;
68static int depth0_checksum;
69static int depth0_compression;
70static int depth0_timeout;
71static char depth0_exec[PATH_MAX];
72static int depth0_metaflush;
73
74static char depth1_provname[PATH_MAX];
75static char depth1_localpath[PATH_MAX];
76static int depth1_metaflush;
77
78extern void yyrestart(FILE *);
79
80static int isitme(const char *name);
81static bool family_supported(int family);
82static int node_names(char **namesp);
83%}
84
85%token CONTROL PIDFILE LISTEN REPLICATION CHECKSUM COMPRESSION METAFLUSH
86%token TIMEOUT EXEC RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
87%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
88%token NUM STR OB CB
89
90%type <str> remote_str
91%type <num> replication_type
92%type <num> checksum_type
93%type <num> compression_type
94%type <num> boolean
95
96%union
97{
98	int num;
99	char *str;
100}
101
102%token <num> NUM
103%token <str> STR
104
105%%
106
107statements:
108	|
109	statements statement
110	;
111
112statement:
113	control_statement
114	|
115	pidfile_statement
116	|
117	listen_statement
118	|
119	replication_statement
120	|
121	checksum_statement
122	|
123	compression_statement
124	|
125	timeout_statement
126	|
127	exec_statement
128	|
129	metaflush_statement
130	|
131	node_statement
132	|
133	resource_statement
134	;
135
136control_statement:	CONTROL STR
137	{
138		switch (depth) {
139		case 0:
140			if (strlcpy(depth0_control, $2,
141			    sizeof(depth0_control)) >=
142			    sizeof(depth0_control)) {
143				pjdlog_error("control argument is too long.");
144				free($2);
145				return (1);
146			}
147			break;
148		case 1:
149			if (!mynode)
150				break;
151			if (strlcpy(lconfig->hc_controladdr, $2,
152			    sizeof(lconfig->hc_controladdr)) >=
153			    sizeof(lconfig->hc_controladdr)) {
154				pjdlog_error("control argument is too long.");
155				free($2);
156				return (1);
157			}
158			break;
159		default:
160			PJDLOG_ABORT("control at wrong depth level");
161		}
162		free($2);
163	}
164	;
165
166pidfile_statement:	PIDFILE STR
167	{
168		switch (depth) {
169		case 0:
170			if (strlcpy(depth0_pidfile, $2,
171			    sizeof(depth0_pidfile)) >=
172			    sizeof(depth0_pidfile)) {
173				pjdlog_error("pidfile argument is too long.");
174				free($2);
175				return (1);
176			}
177			break;
178		case 1:
179			if (!mynode)
180				break;
181			if (strlcpy(lconfig->hc_pidfile, $2,
182			    sizeof(lconfig->hc_pidfile)) >=
183			    sizeof(lconfig->hc_pidfile)) {
184				pjdlog_error("pidfile argument is too long.");
185				free($2);
186				return (1);
187			}
188			break;
189		default:
190			PJDLOG_ABORT("pidfile at wrong depth level");
191		}
192		free($2);
193	}
194	;
195
196listen_statement:	LISTEN STR
197	{
198		struct hastd_listen *lst;
199
200		lst = calloc(1, sizeof(*lst));
201		if (lst == NULL) {
202			pjdlog_error("Unable to allocate memory for listen address.");
203			free($2);
204			return (1);
205		}
206		if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
207		    sizeof(lst->hl_addr)) {
208			pjdlog_error("listen argument is too long.");
209			free($2);
210			free(lst);
211			return (1);
212		}
213		switch (depth) {
214		case 0:
215			TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
216			break;
217		case 1:
218			if (mynode)
219				TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
220			else
221				free(lst);
222			break;
223		default:
224			PJDLOG_ABORT("listen at wrong depth level");
225		}
226		free($2);
227	}
228	;
229
230replication_statement:	REPLICATION replication_type
231	{
232		switch (depth) {
233		case 0:
234			depth0_replication = $2;
235			break;
236		case 1:
237			PJDLOG_ASSERT(curres != NULL);
238			curres->hr_replication = $2;
239			curres->hr_original_replication = $2;
240			break;
241		default:
242			PJDLOG_ABORT("replication at wrong depth level");
243		}
244	}
245	;
246
247replication_type:
248	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
249	|
250	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
251	|
252	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
253	;
254
255checksum_statement:	CHECKSUM checksum_type
256	{
257		switch (depth) {
258		case 0:
259			depth0_checksum = $2;
260			break;
261		case 1:
262			PJDLOG_ASSERT(curres != NULL);
263			curres->hr_checksum = $2;
264			break;
265		default:
266			PJDLOG_ABORT("checksum at wrong depth level");
267		}
268	}
269	;
270
271checksum_type:
272	NONE		{ $$ = HAST_CHECKSUM_NONE; }
273	|
274	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
275	|
276	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
277	;
278
279compression_statement:	COMPRESSION compression_type
280	{
281		switch (depth) {
282		case 0:
283			depth0_compression = $2;
284			break;
285		case 1:
286			PJDLOG_ASSERT(curres != NULL);
287			curres->hr_compression = $2;
288			break;
289		default:
290			PJDLOG_ABORT("compression at wrong depth level");
291		}
292	}
293	;
294
295compression_type:
296	NONE		{ $$ = HAST_COMPRESSION_NONE; }
297	|
298	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
299	|
300	LZF		{ $$ = HAST_COMPRESSION_LZF; }
301	;
302
303timeout_statement:	TIMEOUT NUM
304	{
305		if ($2 <= 0) {
306			pjdlog_error("Negative or zero timeout.");
307			return (1);
308		}
309		switch (depth) {
310		case 0:
311			depth0_timeout = $2;
312			break;
313		case 1:
314			PJDLOG_ASSERT(curres != NULL);
315			curres->hr_timeout = $2;
316			break;
317		default:
318			PJDLOG_ABORT("timeout at wrong depth level");
319		}
320	}
321	;
322
323exec_statement:		EXEC STR
324	{
325		switch (depth) {
326		case 0:
327			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
328			    sizeof(depth0_exec)) {
329				pjdlog_error("Exec path is too long.");
330				free($2);
331				return (1);
332			}
333			break;
334		case 1:
335			PJDLOG_ASSERT(curres != NULL);
336			if (strlcpy(curres->hr_exec, $2,
337			    sizeof(curres->hr_exec)) >=
338			    sizeof(curres->hr_exec)) {
339				pjdlog_error("Exec path is too long.");
340				free($2);
341				return (1);
342			}
343			break;
344		default:
345			PJDLOG_ABORT("exec at wrong depth level");
346		}
347		free($2);
348	}
349	;
350
351metaflush_statement:	METAFLUSH boolean
352	{
353		switch (depth) {
354		case 0:
355			depth0_metaflush = $2;
356			break;
357		case 1:
358			PJDLOG_ASSERT(curres != NULL);
359			depth1_metaflush = $2;
360			break;
361		case 2:
362			if (!mynode)
363				break;
364			PJDLOG_ASSERT(curres != NULL);
365			curres->hr_metaflush = $2;
366			break;
367		default:
368			PJDLOG_ABORT("metaflush at wrong depth level");
369		}
370	}
371	;
372
373boolean:
374	ON		{ $$ = 1; }
375	|
376	OFF		{ $$ = 0; }
377	;
378
379node_statement:		ON node_start OB node_entries CB
380	{
381		mynode = false;
382	}
383	;
384
385node_start:	STR
386	{
387		switch (isitme($1)) {
388		case -1:
389			free($1);
390			return (1);
391		case 0:
392			break;
393		case 1:
394			mynode = true;
395			break;
396		default:
397			PJDLOG_ABORT("invalid isitme() return value");
398		}
399		free($1);
400	}
401	;
402
403node_entries:
404	|
405	node_entries node_entry
406	;
407
408node_entry:
409	control_statement
410	|
411	pidfile_statement
412	|
413	listen_statement
414	;
415
416resource_statement:	RESOURCE resource_start OB resource_entries CB
417	{
418		if (curres != NULL) {
419			/*
420			 * There must be section for this node, at least with
421			 * remote address configuration.
422			 */
423			if (!hadmynode) {
424				char *names;
425
426				if (node_names(&names) != 0)
427					return (1);
428				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
429				    curres->hr_name, names);
430				return (1);
431			}
432
433			/*
434			 * Let's see if there are some resource-level settings
435			 * that we can use for node-level settings.
436			 */
437			if (curres->hr_provname[0] == '\0' &&
438			    depth1_provname[0] != '\0') {
439				/*
440				 * Provider name is not set at node-level,
441				 * but is set at resource-level, use it.
442				 */
443				strlcpy(curres->hr_provname, depth1_provname,
444				    sizeof(curres->hr_provname));
445			}
446			if (curres->hr_localpath[0] == '\0' &&
447			    depth1_localpath[0] != '\0') {
448				/*
449				 * Path to local provider is not set at
450				 * node-level, but is set at resource-level,
451				 * use it.
452				 */
453				strlcpy(curres->hr_localpath, depth1_localpath,
454				    sizeof(curres->hr_localpath));
455			}
456			if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
457				/*
458				 * Metaflush is not set at node-level,
459				 * but is set at resource-level, use it.
460				 */
461				curres->hr_metaflush = depth1_metaflush;
462			}
463
464			/*
465			 * If provider name is not given, use resource name
466			 * as provider name.
467			 */
468			if (curres->hr_provname[0] == '\0') {
469				strlcpy(curres->hr_provname, curres->hr_name,
470				    sizeof(curres->hr_provname));
471			}
472
473			/*
474			 * Remote address has to be configured at this point.
475			 */
476			if (curres->hr_remoteaddr[0] == '\0') {
477				pjdlog_error("Remote address not configured for resource %s.",
478				    curres->hr_name);
479				return (1);
480			}
481			/*
482			 * Path to local provider has to be configured at this
483			 * point.
484			 */
485			if (curres->hr_localpath[0] == '\0') {
486				pjdlog_error("Path to local component not configured for resource %s.",
487				    curres->hr_name);
488				return (1);
489			}
490
491			/* Put it onto resource list. */
492			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
493			curres = NULL;
494		}
495	}
496	;
497
498resource_start:	STR
499	{
500		/* Check if there is no duplicate entry. */
501		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
502			if (strcmp(curres->hr_name, $1) == 0) {
503				pjdlog_error("Resource %s configured more than once.",
504				    curres->hr_name);
505				free($1);
506				return (1);
507			}
508		}
509
510		/*
511		 * Clear those, so we can tell if they were set at
512		 * resource-level or not.
513		 */
514		depth1_provname[0] = '\0';
515		depth1_localpath[0] = '\0';
516		depth1_metaflush = -1;
517		hadmynode = false;
518
519		curres = calloc(1, sizeof(*curres));
520		if (curres == NULL) {
521			pjdlog_error("Unable to allocate memory for resource.");
522			free($1);
523			return (1);
524		}
525		if (strlcpy(curres->hr_name, $1,
526		    sizeof(curres->hr_name)) >=
527		    sizeof(curres->hr_name)) {
528			pjdlog_error("Resource name is too long.");
529			free(curres);
530			free($1);
531			return (1);
532		}
533		free($1);
534		curres->hr_role = HAST_ROLE_INIT;
535		curres->hr_previous_role = HAST_ROLE_INIT;
536		curres->hr_replication = -1;
537		curres->hr_original_replication = -1;
538		curres->hr_checksum = -1;
539		curres->hr_compression = -1;
540		curres->hr_version = 1;
541		curres->hr_timeout = -1;
542		curres->hr_exec[0] = '\0';
543		curres->hr_provname[0] = '\0';
544		curres->hr_localpath[0] = '\0';
545		curres->hr_localfd = -1;
546		curres->hr_localflush = true;
547		curres->hr_metaflush = -1;
548		curres->hr_remoteaddr[0] = '\0';
549		curres->hr_sourceaddr[0] = '\0';
550		curres->hr_ggateunit = -1;
551	}
552	;
553
554resource_entries:
555	|
556	resource_entries resource_entry
557	;
558
559resource_entry:
560	replication_statement
561	|
562	checksum_statement
563	|
564	compression_statement
565	|
566	timeout_statement
567	|
568	exec_statement
569	|
570	metaflush_statement
571	|
572	name_statement
573	|
574	local_statement
575	|
576	resource_node_statement
577	;
578
579name_statement:		NAME STR
580	{
581		switch (depth) {
582		case 1:
583			if (strlcpy(depth1_provname, $2,
584			    sizeof(depth1_provname)) >=
585			    sizeof(depth1_provname)) {
586				pjdlog_error("name argument is too long.");
587				free($2);
588				return (1);
589			}
590			break;
591		case 2:
592			if (!mynode)
593				break;
594			PJDLOG_ASSERT(curres != NULL);
595			if (strlcpy(curres->hr_provname, $2,
596			    sizeof(curres->hr_provname)) >=
597			    sizeof(curres->hr_provname)) {
598				pjdlog_error("name argument is too long.");
599				free($2);
600				return (1);
601			}
602			break;
603		default:
604			PJDLOG_ABORT("name at wrong depth level");
605		}
606		free($2);
607	}
608	;
609
610local_statement:	LOCAL STR
611	{
612		switch (depth) {
613		case 1:
614			if (strlcpy(depth1_localpath, $2,
615			    sizeof(depth1_localpath)) >=
616			    sizeof(depth1_localpath)) {
617				pjdlog_error("local argument is too long.");
618				free($2);
619				return (1);
620			}
621			break;
622		case 2:
623			if (!mynode)
624				break;
625			PJDLOG_ASSERT(curres != NULL);
626			if (strlcpy(curres->hr_localpath, $2,
627			    sizeof(curres->hr_localpath)) >=
628			    sizeof(curres->hr_localpath)) {
629				pjdlog_error("local argument is too long.");
630				free($2);
631				return (1);
632			}
633			break;
634		default:
635			PJDLOG_ABORT("local at wrong depth level");
636		}
637		free($2);
638	}
639	;
640
641resource_node_statement:ON resource_node_start OB resource_node_entries CB
642	{
643		mynode = false;
644	}
645	;
646
647resource_node_start:	STR
648	{
649		if (curres != NULL) {
650			switch (isitme($1)) {
651			case -1:
652				free($1);
653				return (1);
654			case 0:
655				break;
656			case 1:
657				mynode = hadmynode = true;
658				break;
659			default:
660				PJDLOG_ABORT("invalid isitme() return value");
661			}
662		}
663		free($1);
664	}
665	;
666
667resource_node_entries:
668	|
669	resource_node_entries resource_node_entry
670	;
671
672resource_node_entry:
673	name_statement
674	|
675	local_statement
676	|
677	remote_statement
678	|
679	source_statement
680	|
681	metaflush_statement
682	;
683
684remote_statement:	REMOTE remote_str
685	{
686		PJDLOG_ASSERT(depth == 2);
687		if (mynode) {
688			PJDLOG_ASSERT(curres != NULL);
689			if (strlcpy(curres->hr_remoteaddr, $2,
690			    sizeof(curres->hr_remoteaddr)) >=
691			    sizeof(curres->hr_remoteaddr)) {
692				pjdlog_error("remote argument is too long.");
693				free($2);
694				return (1);
695			}
696		}
697		free($2);
698	}
699	;
700
701remote_str:
702	NONE		{ $$ = strdup("none"); }
703	|
704	STR		{ }
705	;
706
707source_statement:	SOURCE STR
708	{
709		PJDLOG_ASSERT(depth == 2);
710		if (mynode) {
711			PJDLOG_ASSERT(curres != NULL);
712			if (strlcpy(curres->hr_sourceaddr, $2,
713			    sizeof(curres->hr_sourceaddr)) >=
714			    sizeof(curres->hr_sourceaddr)) {
715				pjdlog_error("source argument is too long.");
716				free($2);
717				return (1);
718			}
719		}
720		free($2);
721	}
722	;
723
724%%
725
726static int
727isitme(const char *name)
728{
729	char buf[MAXHOSTNAMELEN];
730	unsigned long hostid;
731	char *pos;
732	size_t bufsize;
733
734	/*
735	 * First check if the given name matches our full hostname.
736	 */
737	if (gethostname(buf, sizeof(buf)) < 0) {
738		pjdlog_errno(LOG_ERR, "gethostname() failed");
739		return (-1);
740	}
741	if (strcmp(buf, name) == 0)
742		return (1);
743
744	/*
745	 * Check if it matches first part of the host name.
746	 */
747	pos = strchr(buf, '.');
748	if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
749	    strncmp(buf, name, pos - buf) == 0) {
750		return (1);
751	}
752
753	/*
754	 * Check if it matches host UUID.
755	 */
756	bufsize = sizeof(buf);
757	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
758		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
759		return (-1);
760	}
761	if (strcasecmp(buf, name) == 0)
762		return (1);
763
764	/*
765	 * Check if it matches hostid.
766	 */
767	bufsize = sizeof(hostid);
768	if (sysctlbyname("kern.hostid", &hostid, &bufsize, NULL, 0) < 0) {
769		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostid) failed");
770		return (-1);
771	}
772	(void)snprintf(buf, sizeof(buf), "hostid%lu", hostid);
773	if (strcmp(buf, name) == 0)
774		return (1);
775
776	/*
777	 * Looks like this isn't about us.
778	 */
779	return (0);
780}
781
782static bool
783family_supported(int family)
784{
785	int sock;
786
787	sock = socket(family, SOCK_STREAM, 0);
788	if (sock == -1 && errno == EPROTONOSUPPORT)
789		return (false);
790	if (sock >= 0)
791		(void)close(sock);
792	return (true);
793}
794
795static int
796node_names(char **namesp)
797{
798	static char names[MAXHOSTNAMELEN * 3];
799	char buf[MAXHOSTNAMELEN];
800	unsigned long hostid;
801	char *pos;
802	size_t bufsize;
803
804	if (gethostname(buf, sizeof(buf)) < 0) {
805		pjdlog_errno(LOG_ERR, "gethostname() failed");
806		return (-1);
807	}
808
809	/* First component of the host name. */
810	pos = strchr(buf, '.');
811	if (pos != NULL && pos != buf) {
812		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
813		    sizeof(names)));
814		(void)strlcat(names, ", ", sizeof(names));
815	}
816
817	/* Full host name. */
818	(void)strlcat(names, buf, sizeof(names));
819	(void)strlcat(names, ", ", sizeof(names));
820
821	/* Host UUID. */
822	bufsize = sizeof(buf);
823	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
824		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
825		return (-1);
826	}
827	(void)strlcat(names, buf, sizeof(names));
828	(void)strlcat(names, ", ", sizeof(names));
829
830	/* Host ID. */
831	bufsize = sizeof(hostid);
832	if (sysctlbyname("kern.hostid", &hostid, &bufsize, NULL, 0) < 0) {
833		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostid) failed");
834		return (-1);
835	}
836	(void)snprintf(buf, sizeof(buf), "hostid%lu", hostid);
837	(void)strlcat(names, buf, sizeof(names));
838
839	*namesp = names;
840
841	return (0);
842}
843
844void
845yyerror(const char *str)
846{
847
848	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
849	    lineno, yytext, str);
850}
851
852struct hastd_config *
853yy_config_parse(const char *config, bool exitonerror)
854{
855	int ret;
856
857	curres = NULL;
858	mynode = false;
859	depth = 0;
860	lineno = 0;
861
862	depth0_timeout = HAST_TIMEOUT;
863	depth0_replication = HAST_REPLICATION_MEMSYNC;
864	depth0_checksum = HAST_CHECKSUM_NONE;
865	depth0_compression = HAST_COMPRESSION_HOLE;
866	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
867	strlcpy(depth0_pidfile, HASTD_PIDFILE, sizeof(depth0_pidfile));
868	TAILQ_INIT(&depth0_listen);
869	strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
870	    sizeof(depth0_listen_tcp4));
871	strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
872	    sizeof(depth0_listen_tcp6));
873	depth0_exec[0] = '\0';
874	depth0_metaflush = 1;
875
876	lconfig = calloc(1, sizeof(*lconfig));
877	if (lconfig == NULL) {
878		pjdlog_error("Unable to allocate memory for configuration.");
879		if (exitonerror)
880			exit(EX_TEMPFAIL);
881		return (NULL);
882	}
883
884	TAILQ_INIT(&lconfig->hc_listen);
885	TAILQ_INIT(&lconfig->hc_resources);
886
887	yyin = fopen(config, "r");
888	if (yyin == NULL) {
889		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
890		    config);
891		yy_config_free(lconfig);
892		if (exitonerror)
893			exit(EX_OSFILE);
894		return (NULL);
895	}
896	yyrestart(yyin);
897	ret = yyparse();
898	fclose(yyin);
899	if (ret != 0) {
900		yy_config_free(lconfig);
901		if (exitonerror)
902			exit(EX_CONFIG);
903		return (NULL);
904	}
905
906	/*
907	 * Let's see if everything is set up.
908	 */
909	if (lconfig->hc_controladdr[0] == '\0') {
910		strlcpy(lconfig->hc_controladdr, depth0_control,
911		    sizeof(lconfig->hc_controladdr));
912	}
913	if (lconfig->hc_pidfile[0] == '\0') {
914		strlcpy(lconfig->hc_pidfile, depth0_pidfile,
915		    sizeof(lconfig->hc_pidfile));
916	}
917	if (!TAILQ_EMPTY(&depth0_listen))
918		TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
919	if (TAILQ_EMPTY(&lconfig->hc_listen)) {
920		struct hastd_listen *lst;
921
922		if (family_supported(AF_INET)) {
923			lst = calloc(1, sizeof(*lst));
924			if (lst == NULL) {
925				pjdlog_error("Unable to allocate memory for listen address.");
926				yy_config_free(lconfig);
927				if (exitonerror)
928					exit(EX_TEMPFAIL);
929				return (NULL);
930			}
931			(void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
932			    sizeof(lst->hl_addr));
933			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
934		} else {
935			pjdlog_debug(1,
936			    "No IPv4 support in the kernel, not listening on IPv4 address.");
937		}
938		if (family_supported(AF_INET6)) {
939			lst = calloc(1, sizeof(*lst));
940			if (lst == NULL) {
941				pjdlog_error("Unable to allocate memory for listen address.");
942				yy_config_free(lconfig);
943				if (exitonerror)
944					exit(EX_TEMPFAIL);
945				return (NULL);
946			}
947			(void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
948			    sizeof(lst->hl_addr));
949			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
950		} else {
951			pjdlog_debug(1,
952			    "No IPv6 support in the kernel, not listening on IPv6 address.");
953		}
954		if (TAILQ_EMPTY(&lconfig->hc_listen)) {
955			pjdlog_error("No address to listen on.");
956			yy_config_free(lconfig);
957			if (exitonerror)
958				exit(EX_TEMPFAIL);
959			return (NULL);
960		}
961	}
962	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
963		PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
964		PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
965		PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
966
967		if (curres->hr_replication == -1) {
968			/*
969			 * Replication is not set at resource-level.
970			 * Use global or default setting.
971			 */
972			curres->hr_replication = depth0_replication;
973			curres->hr_original_replication = depth0_replication;
974		}
975		if (curres->hr_checksum == -1) {
976			/*
977			 * Checksum is not set at resource-level.
978			 * Use global or default setting.
979			 */
980			curres->hr_checksum = depth0_checksum;
981		}
982		if (curres->hr_compression == -1) {
983			/*
984			 * Compression is not set at resource-level.
985			 * Use global or default setting.
986			 */
987			curres->hr_compression = depth0_compression;
988		}
989		if (curres->hr_timeout == -1) {
990			/*
991			 * Timeout is not set at resource-level.
992			 * Use global or default setting.
993			 */
994			curres->hr_timeout = depth0_timeout;
995		}
996		if (curres->hr_exec[0] == '\0') {
997			/*
998			 * Exec is not set at resource-level.
999			 * Use global or default setting.
1000			 */
1001			strlcpy(curres->hr_exec, depth0_exec,
1002			    sizeof(curres->hr_exec));
1003		}
1004		if (curres->hr_metaflush == -1) {
1005			/*
1006			 * Metaflush is not set at resource-level.
1007			 * Use global or default setting.
1008			 */
1009			curres->hr_metaflush = depth0_metaflush;
1010		}
1011	}
1012
1013	return (lconfig);
1014}
1015
1016void
1017yy_config_free(struct hastd_config *config)
1018{
1019	struct hastd_listen *lst;
1020	struct hast_resource *res;
1021
1022	while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
1023		TAILQ_REMOVE(&depth0_listen, lst, hl_next);
1024		free(lst);
1025	}
1026	while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
1027		TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
1028		free(lst);
1029	}
1030	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
1031		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
1032		free(res);
1033	}
1034	free(config);
1035}
1036