parse.y revision 244538
1214501Srpaulo%{
2214501Srpaulo/*-
3214501Srpaulo * Copyright (c) 2009-2010 The FreeBSD Foundation
4214501Srpaulo * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5214501Srpaulo * All rights reserved.
6214501Srpaulo *
7214501Srpaulo * This software was developed by Pawel Jakub Dawidek under sponsorship from
8214501Srpaulo * the FreeBSD Foundation.
9214501Srpaulo *
10214501Srpaulo * Redistribution and use in source and binary forms, with or without
11214501Srpaulo * modification, are permitted provided that the following conditions
12214501Srpaulo * are met:
13214501Srpaulo * 1. Redistributions of source code must retain the above copyright
14214501Srpaulo *    notice, this list of conditions and the following disclaimer.
15214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
16214501Srpaulo *    notice, this list of conditions and the following disclaimer in the
17214501Srpaulo *    documentation and/or other materials provided with the distribution.
18214501Srpaulo *
19214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22214501Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29214501Srpaulo * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sbin/hastd/parse.y 244538 2012-12-21 15:54:13Z kevlo $
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			break;
240		default:
241			PJDLOG_ABORT("replication at wrong depth level");
242		}
243	}
244	;
245
246replication_type:
247	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
248	|
249	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
250	|
251	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
252	;
253
254checksum_statement:	CHECKSUM checksum_type
255	{
256		switch (depth) {
257		case 0:
258			depth0_checksum = $2;
259			break;
260		case 1:
261			PJDLOG_ASSERT(curres != NULL);
262			curres->hr_checksum = $2;
263			break;
264		default:
265			PJDLOG_ABORT("checksum at wrong depth level");
266		}
267	}
268	;
269
270checksum_type:
271	NONE		{ $$ = HAST_CHECKSUM_NONE; }
272	|
273	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
274	|
275	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
276	;
277
278compression_statement:	COMPRESSION compression_type
279	{
280		switch (depth) {
281		case 0:
282			depth0_compression = $2;
283			break;
284		case 1:
285			PJDLOG_ASSERT(curres != NULL);
286			curres->hr_compression = $2;
287			break;
288		default:
289			PJDLOG_ABORT("compression at wrong depth level");
290		}
291	}
292	;
293
294compression_type:
295	NONE		{ $$ = HAST_COMPRESSION_NONE; }
296	|
297	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
298	|
299	LZF		{ $$ = HAST_COMPRESSION_LZF; }
300	;
301
302timeout_statement:	TIMEOUT NUM
303	{
304		if ($2 <= 0) {
305			pjdlog_error("Negative or zero timeout.");
306			return (1);
307		}
308		switch (depth) {
309		case 0:
310			depth0_timeout = $2;
311			break;
312		case 1:
313			PJDLOG_ASSERT(curres != NULL);
314			curres->hr_timeout = $2;
315			break;
316		default:
317			PJDLOG_ABORT("timeout at wrong depth level");
318		}
319	}
320	;
321
322exec_statement:		EXEC STR
323	{
324		switch (depth) {
325		case 0:
326			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
327			    sizeof(depth0_exec)) {
328				pjdlog_error("Exec path is too long.");
329				free($2);
330				return (1);
331			}
332			break;
333		case 1:
334			PJDLOG_ASSERT(curres != NULL);
335			if (strlcpy(curres->hr_exec, $2,
336			    sizeof(curres->hr_exec)) >=
337			    sizeof(curres->hr_exec)) {
338				pjdlog_error("Exec path is too long.");
339				free($2);
340				return (1);
341			}
342			break;
343		default:
344			PJDLOG_ABORT("exec at wrong depth level");
345		}
346		free($2);
347	}
348	;
349
350metaflush_statement:	METAFLUSH boolean
351	{
352		switch (depth) {
353		case 0:
354			depth0_metaflush = $2;
355			break;
356		case 1:
357			PJDLOG_ASSERT(curres != NULL);
358			depth1_metaflush = $2;
359			break;
360		case 2:
361			if (!mynode)
362				break;
363			PJDLOG_ASSERT(curres != NULL);
364			curres->hr_metaflush = $2;
365			break;
366		default:
367			PJDLOG_ABORT("metaflush at wrong depth level");
368		}
369	}
370	;
371
372boolean:
373	ON		{ $$ = 1; }
374	|
375	OFF		{ $$ = 0; }
376	;
377
378node_statement:		ON node_start OB node_entries CB
379	{
380		mynode = false;
381	}
382	;
383
384node_start:	STR
385	{
386		switch (isitme($1)) {
387		case -1:
388			free($1);
389			return (1);
390		case 0:
391			break;
392		case 1:
393			mynode = true;
394			break;
395		default:
396			PJDLOG_ABORT("invalid isitme() return value");
397		}
398		free($1);
399	}
400	;
401
402node_entries:
403	|
404	node_entries node_entry
405	;
406
407node_entry:
408	control_statement
409	|
410	pidfile_statement
411	|
412	listen_statement
413	;
414
415resource_statement:	RESOURCE resource_start OB resource_entries CB
416	{
417		if (curres != NULL) {
418			/*
419			 * There must be section for this node, at least with
420			 * remote address configuration.
421			 */
422			if (!hadmynode) {
423				char *names;
424
425				if (node_names(&names) != 0)
426					return (1);
427				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
428				    curres->hr_name, names);
429				return (1);
430			}
431
432			/*
433			 * Let's see if there are some resource-level settings
434			 * that we can use for node-level settings.
435			 */
436			if (curres->hr_provname[0] == '\0' &&
437			    depth1_provname[0] != '\0') {
438				/*
439				 * Provider name is not set at node-level,
440				 * but is set at resource-level, use it.
441				 */
442				strlcpy(curres->hr_provname, depth1_provname,
443				    sizeof(curres->hr_provname));
444			}
445			if (curres->hr_localpath[0] == '\0' &&
446			    depth1_localpath[0] != '\0') {
447				/*
448				 * Path to local provider is not set at
449				 * node-level, but is set at resource-level,
450				 * use it.
451				 */
452				strlcpy(curres->hr_localpath, depth1_localpath,
453				    sizeof(curres->hr_localpath));
454			}
455			if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
456				/*
457				 * Metaflush is not set at node-level,
458				 * but is set at resource-level, use it.
459				 */
460				curres->hr_metaflush = depth1_metaflush;
461			}
462
463			/*
464			 * If provider name is not given, use resource name
465			 * as provider name.
466			 */
467			if (curres->hr_provname[0] == '\0') {
468				strlcpy(curres->hr_provname, curres->hr_name,
469				    sizeof(curres->hr_provname));
470			}
471
472			/*
473			 * Remote address has to be configured at this point.
474			 */
475			if (curres->hr_remoteaddr[0] == '\0') {
476				pjdlog_error("Remote address not configured for resource %s.",
477				    curres->hr_name);
478				return (1);
479			}
480			/*
481			 * Path to local provider has to be configured at this
482			 * point.
483			 */
484			if (curres->hr_localpath[0] == '\0') {
485				pjdlog_error("Path to local component not configured for resource %s.",
486				    curres->hr_name);
487				return (1);
488			}
489
490			/* Put it onto resource list. */
491			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
492			curres = NULL;
493		}
494	}
495	;
496
497resource_start:	STR
498	{
499		/* Check if there is no duplicate entry. */
500		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
501			if (strcmp(curres->hr_name, $1) == 0) {
502				pjdlog_error("Resource %s configured more than once.",
503				    curres->hr_name);
504				free($1);
505				return (1);
506			}
507		}
508
509		/*
510		 * Clear those, so we can tell if they were set at
511		 * resource-level or not.
512		 */
513		depth1_provname[0] = '\0';
514		depth1_localpath[0] = '\0';
515		depth1_metaflush = -1;
516		hadmynode = false;
517
518		curres = calloc(1, sizeof(*curres));
519		if (curres == NULL) {
520			pjdlog_error("Unable to allocate memory for resource.");
521			free($1);
522			return (1);
523		}
524		if (strlcpy(curres->hr_name, $1,
525		    sizeof(curres->hr_name)) >=
526		    sizeof(curres->hr_name)) {
527			pjdlog_error("Resource name is too long.");
528			free(curres);
529			free($1);
530			return (1);
531		}
532		free($1);
533		curres->hr_role = HAST_ROLE_INIT;
534		curres->hr_previous_role = HAST_ROLE_INIT;
535		curres->hr_replication = -1;
536		curres->hr_checksum = -1;
537		curres->hr_compression = -1;
538		curres->hr_timeout = -1;
539		curres->hr_exec[0] = '\0';
540		curres->hr_provname[0] = '\0';
541		curres->hr_localpath[0] = '\0';
542		curres->hr_localfd = -1;
543		curres->hr_localflush = true;
544		curres->hr_metaflush = -1;
545		curres->hr_remoteaddr[0] = '\0';
546		curres->hr_sourceaddr[0] = '\0';
547		curres->hr_ggateunit = -1;
548	}
549	;
550
551resource_entries:
552	|
553	resource_entries resource_entry
554	;
555
556resource_entry:
557	replication_statement
558	|
559	checksum_statement
560	|
561	compression_statement
562	|
563	timeout_statement
564	|
565	exec_statement
566	|
567	metaflush_statement
568	|
569	name_statement
570	|
571	local_statement
572	|
573	resource_node_statement
574	;
575
576name_statement:		NAME STR
577	{
578		switch (depth) {
579		case 1:
580			if (strlcpy(depth1_provname, $2,
581			    sizeof(depth1_provname)) >=
582			    sizeof(depth1_provname)) {
583				pjdlog_error("name argument is too long.");
584				free($2);
585				return (1);
586			}
587			break;
588		case 2:
589			if (!mynode)
590				break;
591			PJDLOG_ASSERT(curres != NULL);
592			if (strlcpy(curres->hr_provname, $2,
593			    sizeof(curres->hr_provname)) >=
594			    sizeof(curres->hr_provname)) {
595				pjdlog_error("name argument is too long.");
596				free($2);
597				return (1);
598			}
599			break;
600		default:
601			PJDLOG_ABORT("name at wrong depth level");
602		}
603		free($2);
604	}
605	;
606
607local_statement:	LOCAL STR
608	{
609		switch (depth) {
610		case 1:
611			if (strlcpy(depth1_localpath, $2,
612			    sizeof(depth1_localpath)) >=
613			    sizeof(depth1_localpath)) {
614				pjdlog_error("local argument is too long.");
615				free($2);
616				return (1);
617			}
618			break;
619		case 2:
620			if (!mynode)
621				break;
622			PJDLOG_ASSERT(curres != NULL);
623			if (strlcpy(curres->hr_localpath, $2,
624			    sizeof(curres->hr_localpath)) >=
625			    sizeof(curres->hr_localpath)) {
626				pjdlog_error("local argument is too long.");
627				free($2);
628				return (1);
629			}
630			break;
631		default:
632			PJDLOG_ABORT("local at wrong depth level");
633		}
634		free($2);
635	}
636	;
637
638resource_node_statement:ON resource_node_start OB resource_node_entries CB
639	{
640		mynode = false;
641	}
642	;
643
644resource_node_start:	STR
645	{
646		if (curres != NULL) {
647			switch (isitme($1)) {
648			case -1:
649				free($1);
650				return (1);
651			case 0:
652				break;
653			case 1:
654				mynode = hadmynode = true;
655				break;
656			default:
657				PJDLOG_ABORT("invalid isitme() return value");
658			}
659		}
660		free($1);
661	}
662	;
663
664resource_node_entries:
665	|
666	resource_node_entries resource_node_entry
667	;
668
669resource_node_entry:
670	name_statement
671	|
672	local_statement
673	|
674	remote_statement
675	|
676	source_statement
677	|
678	metaflush_statement
679	;
680
681remote_statement:	REMOTE remote_str
682	{
683		PJDLOG_ASSERT(depth == 2);
684		if (mynode) {
685			PJDLOG_ASSERT(curres != NULL);
686			if (strlcpy(curres->hr_remoteaddr, $2,
687			    sizeof(curres->hr_remoteaddr)) >=
688			    sizeof(curres->hr_remoteaddr)) {
689				pjdlog_error("remote argument is too long.");
690				free($2);
691				return (1);
692			}
693		}
694		free($2);
695	}
696	;
697
698remote_str:
699	NONE		{ $$ = strdup("none"); }
700	|
701	STR		{ }
702	;
703
704source_statement:	SOURCE STR
705	{
706		PJDLOG_ASSERT(depth == 2);
707		if (mynode) {
708			PJDLOG_ASSERT(curres != NULL);
709			if (strlcpy(curres->hr_sourceaddr, $2,
710			    sizeof(curres->hr_sourceaddr)) >=
711			    sizeof(curres->hr_sourceaddr)) {
712				pjdlog_error("source argument is too long.");
713				free($2);
714				return (1);
715			}
716		}
717		free($2);
718	}
719	;
720
721%%
722
723static int
724isitme(const char *name)
725{
726	char buf[MAXHOSTNAMELEN];
727	char *pos;
728	size_t bufsize;
729
730	/*
731	 * First check if the given name matches our full hostname.
732	 */
733	if (gethostname(buf, sizeof(buf)) < 0) {
734		pjdlog_errno(LOG_ERR, "gethostname() failed");
735		return (-1);
736	}
737	if (strcmp(buf, name) == 0)
738		return (1);
739
740	/*
741	 * Now check if it matches first part of the host name.
742	 */
743	pos = strchr(buf, '.');
744	if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
745	    strncmp(buf, name, pos - buf) == 0) {
746		return (1);
747	}
748
749	/*
750	 * At the end check if name is equal to our host's UUID.
751	 */
752	bufsize = sizeof(buf);
753	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
754		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
755		return (-1);
756	}
757	if (strcasecmp(buf, name) == 0)
758		return (1);
759
760	/*
761	 * Looks like this isn't about us.
762	 */
763	return (0);
764}
765
766static bool
767family_supported(int family)
768{
769	int sock;
770
771	sock = socket(family, SOCK_STREAM, 0);
772	if (sock == -1 && errno == EAFNOSUPPORT)
773		return (false);
774	if (sock >= 0)
775		(void)close(sock);
776	return (true);
777}
778
779static int
780node_names(char **namesp)
781{
782	static char names[MAXHOSTNAMELEN * 3];
783	char buf[MAXHOSTNAMELEN];
784	char *pos;
785	size_t bufsize;
786
787	if (gethostname(buf, sizeof(buf)) < 0) {
788		pjdlog_errno(LOG_ERR, "gethostname() failed");
789		return (-1);
790	}
791
792	/* First component of the host name. */
793	pos = strchr(buf, '.');
794	if (pos != NULL && pos != buf) {
795		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
796		    sizeof(names)));
797		(void)strlcat(names, ", ", sizeof(names));
798	}
799
800	/* Full host name. */
801	(void)strlcat(names, buf, sizeof(names));
802	(void)strlcat(names, ", ", sizeof(names));
803
804	/* Host UUID. */
805	bufsize = sizeof(buf);
806	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
807		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
808		return (-1);
809	}
810	(void)strlcat(names, buf, sizeof(names));
811
812	*namesp = names;
813
814	return (0);
815}
816
817void
818yyerror(const char *str)
819{
820
821	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
822	    lineno, yytext, str);
823}
824
825struct hastd_config *
826yy_config_parse(const char *config, bool exitonerror)
827{
828	int ret;
829
830	curres = NULL;
831	mynode = false;
832	depth = 0;
833	lineno = 0;
834
835	depth0_timeout = HAST_TIMEOUT;
836	depth0_replication = HAST_REPLICATION_FULLSYNC;
837	depth0_checksum = HAST_CHECKSUM_NONE;
838	depth0_compression = HAST_COMPRESSION_HOLE;
839	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
840	strlcpy(depth0_pidfile, HASTD_PIDFILE, sizeof(depth0_pidfile));
841	TAILQ_INIT(&depth0_listen);
842	strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
843	    sizeof(depth0_listen_tcp4));
844	strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
845	    sizeof(depth0_listen_tcp6));
846	depth0_exec[0] = '\0';
847	depth0_metaflush = 1;
848
849	lconfig = calloc(1, sizeof(*lconfig));
850	if (lconfig == NULL) {
851		pjdlog_error("Unable to allocate memory for configuration.");
852		if (exitonerror)
853			exit(EX_TEMPFAIL);
854		return (NULL);
855	}
856
857	TAILQ_INIT(&lconfig->hc_listen);
858	TAILQ_INIT(&lconfig->hc_resources);
859
860	yyin = fopen(config, "r");
861	if (yyin == NULL) {
862		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
863		    config);
864		yy_config_free(lconfig);
865		if (exitonerror)
866			exit(EX_OSFILE);
867		return (NULL);
868	}
869	yyrestart(yyin);
870	ret = yyparse();
871	fclose(yyin);
872	if (ret != 0) {
873		yy_config_free(lconfig);
874		if (exitonerror)
875			exit(EX_CONFIG);
876		return (NULL);
877	}
878
879	/*
880	 * Let's see if everything is set up.
881	 */
882	if (lconfig->hc_controladdr[0] == '\0') {
883		strlcpy(lconfig->hc_controladdr, depth0_control,
884		    sizeof(lconfig->hc_controladdr));
885	}
886	if (lconfig->hc_pidfile[0] == '\0') {
887		strlcpy(lconfig->hc_pidfile, depth0_pidfile,
888		    sizeof(lconfig->hc_pidfile));
889	}
890	if (!TAILQ_EMPTY(&depth0_listen))
891		TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
892	if (TAILQ_EMPTY(&lconfig->hc_listen)) {
893		struct hastd_listen *lst;
894
895		if (family_supported(AF_INET)) {
896			lst = calloc(1, sizeof(*lst));
897			if (lst == NULL) {
898				pjdlog_error("Unable to allocate memory for listen address.");
899				yy_config_free(lconfig);
900				if (exitonerror)
901					exit(EX_TEMPFAIL);
902				return (NULL);
903			}
904			(void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
905			    sizeof(lst->hl_addr));
906			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
907		} else {
908			pjdlog_debug(1,
909			    "No IPv4 support in the kernel, not listening on IPv4 address.");
910		}
911		if (family_supported(AF_INET6)) {
912			lst = calloc(1, sizeof(*lst));
913			if (lst == NULL) {
914				pjdlog_error("Unable to allocate memory for listen address.");
915				yy_config_free(lconfig);
916				if (exitonerror)
917					exit(EX_TEMPFAIL);
918				return (NULL);
919			}
920			(void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
921			    sizeof(lst->hl_addr));
922			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
923		} else {
924			pjdlog_debug(1,
925			    "No IPv6 support in the kernel, not listening on IPv6 address.");
926		}
927		if (TAILQ_EMPTY(&lconfig->hc_listen)) {
928			pjdlog_error("No address to listen on.");
929			yy_config_free(lconfig);
930			if (exitonerror)
931				exit(EX_TEMPFAIL);
932			return (NULL);
933		}
934	}
935	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
936		PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
937		PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
938		PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
939
940		if (curres->hr_replication == -1) {
941			/*
942			 * Replication is not set at resource-level.
943			 * Use global or default setting.
944			 */
945			curres->hr_replication = depth0_replication;
946		}
947		if (curres->hr_replication == HAST_REPLICATION_MEMSYNC) {
948			pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
949			    "memsync", "fullsync");
950			curres->hr_replication = HAST_REPLICATION_FULLSYNC;
951		}
952		if (curres->hr_checksum == -1) {
953			/*
954			 * Checksum is not set at resource-level.
955			 * Use global or default setting.
956			 */
957			curres->hr_checksum = depth0_checksum;
958		}
959		if (curres->hr_compression == -1) {
960			/*
961			 * Compression is not set at resource-level.
962			 * Use global or default setting.
963			 */
964			curres->hr_compression = depth0_compression;
965		}
966		if (curres->hr_timeout == -1) {
967			/*
968			 * Timeout is not set at resource-level.
969			 * Use global or default setting.
970			 */
971			curres->hr_timeout = depth0_timeout;
972		}
973		if (curres->hr_exec[0] == '\0') {
974			/*
975			 * Exec is not set at resource-level.
976			 * Use global or default setting.
977			 */
978			strlcpy(curres->hr_exec, depth0_exec,
979			    sizeof(curres->hr_exec));
980		}
981		if (curres->hr_metaflush == -1) {
982			/*
983			 * Metaflush is not set at resource-level.
984			 * Use global or default setting.
985			 */
986			curres->hr_metaflush = depth0_metaflush;
987		}
988	}
989
990	return (lconfig);
991}
992
993void
994yy_config_free(struct hastd_config *config)
995{
996	struct hastd_listen *lst;
997	struct hast_resource *res;
998
999	while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
1000		TAILQ_REMOVE(&depth0_listen, lst, hl_next);
1001		free(lst);
1002	}
1003	while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
1004		TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
1005		free(lst);
1006	}
1007	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
1008		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
1009		free(res);
1010	}
1011	free(config);
1012}
1013