xref: /freebsd/sbin/hastd/parse.y (revision 7750ad47a9a7dbc83f87158464170c8640723293)
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 
52 extern int depth;
53 extern int lineno;
54 
55 extern FILE *yyin;
56 extern char *yytext;
57 
58 static struct hastd_config *lconfig;
59 static struct hast_resource *curres;
60 static bool mynode, hadmynode;
61 
62 static char depth0_control[HAST_ADDRSIZE];
63 static char depth0_pidfile[PATH_MAX];
64 static char depth0_listen_tcp4[HAST_ADDRSIZE];
65 static char depth0_listen_tcp6[HAST_ADDRSIZE];
66 static TAILQ_HEAD(, hastd_listen) depth0_listen;
67 static int depth0_replication;
68 static int depth0_checksum;
69 static int depth0_compression;
70 static int depth0_timeout;
71 static char depth0_exec[PATH_MAX];
72 static int depth0_metaflush;
73 
74 static char depth1_provname[PATH_MAX];
75 static char depth1_localpath[PATH_MAX];
76 static int depth1_metaflush;
77 
78 extern void yyrestart(FILE *);
79 
80 static int isitme(const char *name);
81 static bool family_supported(int family);
82 static 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 
107 statements:
108 	|
109 	statements statement
110 	;
111 
112 statement:
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 
136 control_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 
166 pidfile_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 
196 listen_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 
230 replication_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 
246 replication_type:
247 	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
248 	|
249 	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
250 	|
251 	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
252 	;
253 
254 checksum_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 
270 checksum_type:
271 	NONE		{ $$ = HAST_CHECKSUM_NONE; }
272 	|
273 	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
274 	|
275 	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
276 	;
277 
278 compression_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 
294 compression_type:
295 	NONE		{ $$ = HAST_COMPRESSION_NONE; }
296 	|
297 	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
298 	|
299 	LZF		{ $$ = HAST_COMPRESSION_LZF; }
300 	;
301 
302 timeout_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 
322 exec_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 
350 metaflush_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 
372 boolean:
373 	ON		{ $$ = 1; }
374 	|
375 	OFF		{ $$ = 0; }
376 	;
377 
378 node_statement:		ON node_start OB node_entries CB
379 	{
380 		mynode = false;
381 	}
382 	;
383 
384 node_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 
402 node_entries:
403 	|
404 	node_entries node_entry
405 	;
406 
407 node_entry:
408 	control_statement
409 	|
410 	pidfile_statement
411 	|
412 	listen_statement
413 	;
414 
415 resource_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 
497 resource_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 
551 resource_entries:
552 	|
553 	resource_entries resource_entry
554 	;
555 
556 resource_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 
576 name_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 
607 local_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 
638 resource_node_statement:ON resource_node_start OB resource_node_entries CB
639 	{
640 		mynode = false;
641 	}
642 	;
643 
644 resource_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 
664 resource_node_entries:
665 	|
666 	resource_node_entries resource_node_entry
667 	;
668 
669 resource_node_entry:
670 	name_statement
671 	|
672 	local_statement
673 	|
674 	remote_statement
675 	|
676 	source_statement
677 	|
678 	metaflush_statement
679 	;
680 
681 remote_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 
698 remote_str:
699 	NONE		{ $$ = strdup("none"); }
700 	|
701 	STR		{ }
702 	;
703 
704 source_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 
723 static int
724 isitme(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 
766 static bool
767 family_supported(int family)
768 {
769 	int sock;
770 
771 	sock = socket(family, SOCK_STREAM, 0);
772 	if (sock == -1 && errno == EPROTONOSUPPORT)
773 		return (false);
774 	if (sock >= 0)
775 		(void)close(sock);
776 	return (true);
777 }
778 
779 static int
780 node_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 
817 void
818 yyerror(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 
825 struct hastd_config *
826 yy_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 
993 void
994 yy_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