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