xref: /freebsd/sbin/hastd/parse.y (revision d6e636c98852fbae89c94bde764872f0d510dca0)
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 			curres->hr_original_replication = $2;
240 			break;
241 		default:
242 			PJDLOG_ABORT("replication at wrong depth level");
243 		}
244 	}
245 	;
246 
247 replication_type:
248 	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
249 	|
250 	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
251 	|
252 	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
253 	;
254 
255 checksum_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 
271 checksum_type:
272 	NONE		{ $$ = HAST_CHECKSUM_NONE; }
273 	|
274 	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
275 	|
276 	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
277 	;
278 
279 compression_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 
295 compression_type:
296 	NONE		{ $$ = HAST_COMPRESSION_NONE; }
297 	|
298 	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
299 	|
300 	LZF		{ $$ = HAST_COMPRESSION_LZF; }
301 	;
302 
303 timeout_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 
323 exec_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 
351 metaflush_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 
373 boolean:
374 	ON		{ $$ = 1; }
375 	|
376 	OFF		{ $$ = 0; }
377 	;
378 
379 node_statement:		ON node_start OB node_entries CB
380 	{
381 		mynode = false;
382 	}
383 	;
384 
385 node_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 
403 node_entries:
404 	|
405 	node_entries node_entry
406 	;
407 
408 node_entry:
409 	control_statement
410 	|
411 	pidfile_statement
412 	|
413 	listen_statement
414 	;
415 
416 resource_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 
498 resource_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 
554 resource_entries:
555 	|
556 	resource_entries resource_entry
557 	;
558 
559 resource_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 
579 name_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 
610 local_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 
641 resource_node_statement:ON resource_node_start OB resource_node_entries CB
642 	{
643 		mynode = false;
644 	}
645 	;
646 
647 resource_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 
667 resource_node_entries:
668 	|
669 	resource_node_entries resource_node_entry
670 	;
671 
672 resource_node_entry:
673 	name_statement
674 	|
675 	local_statement
676 	|
677 	remote_statement
678 	|
679 	source_statement
680 	|
681 	metaflush_statement
682 	;
683 
684 remote_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 
701 remote_str:
702 	NONE		{ $$ = strdup("none"); }
703 	|
704 	STR		{ }
705 	;
706 
707 source_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 
726 static int
727 isitme(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 
782 static bool
783 family_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 
795 static int
796 node_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 
844 void
845 yyerror(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 
852 struct hastd_config *
853 yy_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 
1016 void
1017 yy_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