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