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