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