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