parse.y revision 229509
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: stable/9/sbin/hastd/parse.y 229509 2012-01-04 17:22:10Z trociny $
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
52extern int depth;
53extern int lineno;
54
55extern FILE *yyin;
56extern char *yytext;
57
58static struct hastd_config *lconfig;
59static struct hast_resource *curres;
60static bool mynode, hadmynode;
61
62static char depth0_control[HAST_ADDRSIZE];
63static char depth0_pidfile[PATH_MAX];
64static char depth0_listen_tcp4[HAST_ADDRSIZE];
65static char depth0_listen_tcp6[HAST_ADDRSIZE];
66static TAILQ_HEAD(, hastd_listen) depth0_listen;
67static int depth0_replication;
68static int depth0_checksum;
69static int depth0_compression;
70static int depth0_timeout;
71static char depth0_exec[PATH_MAX];
72static int depth0_metaflush;
73
74static char depth1_provname[PATH_MAX];
75static char depth1_localpath[PATH_MAX];
76static int depth1_metaflush;
77
78extern void yyrestart(FILE *);
79
80static int
81isitme(const char *name)
82{
83	char buf[MAXHOSTNAMELEN];
84	char *pos;
85	size_t bufsize;
86
87	/*
88	 * First check if the give 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
123static bool
124family_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
136static int
137node_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
174void
175yyerror(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
182struct hastd_config *
183yy_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
350void
351yy_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 PORT REPLICATION CHECKSUM COMPRESSION METAFLUSH
373%token TIMEOUT EXEC EXTENTSIZE 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
394statements:
395	|
396	statements statement
397	;
398
399statement:
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
423control_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
453pidfile_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
483listen_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
517replication_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
533replication_type:
534	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
535	|
536	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
537	|
538	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
539	;
540
541checksum_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
557checksum_type:
558	NONE		{ $$ = HAST_CHECKSUM_NONE; }
559	|
560	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
561	|
562	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
563	;
564
565compression_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
581compression_type:
582	NONE		{ $$ = HAST_COMPRESSION_NONE; }
583	|
584	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
585	|
586	LZF		{ $$ = HAST_COMPRESSION_LZF; }
587	;
588
589timeout_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
609exec_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
637metaflush_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
659boolean:
660	ON		{ $$ = 1; }
661	|
662	OFF		{ $$ = 0; }
663	;
664
665node_statement:		ON node_start OB node_entries CB
666	{
667		mynode = false;
668	}
669	;
670
671node_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
689node_entries:
690	|
691	node_entries node_entry
692	;
693
694node_entry:
695	control_statement
696	|
697	pidfile_statement
698	|
699	listen_statement
700	;
701
702resource_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
784resource_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($1);
816			return (1);
817		}
818		free($1);
819		curres->hr_role = HAST_ROLE_INIT;
820		curres->hr_previous_role = HAST_ROLE_INIT;
821		curres->hr_replication = -1;
822		curres->hr_checksum = -1;
823		curres->hr_compression = -1;
824		curres->hr_timeout = -1;
825		curres->hr_exec[0] = '\0';
826		curres->hr_provname[0] = '\0';
827		curres->hr_localpath[0] = '\0';
828		curres->hr_localfd = -1;
829		curres->hr_localflush = true;
830		curres->hr_metaflush = -1;
831		curres->hr_remoteaddr[0] = '\0';
832		curres->hr_sourceaddr[0] = '\0';
833		curres->hr_ggateunit = -1;
834	}
835	;
836
837resource_entries:
838	|
839	resource_entries resource_entry
840	;
841
842resource_entry:
843	replication_statement
844	|
845	checksum_statement
846	|
847	compression_statement
848	|
849	timeout_statement
850	|
851	exec_statement
852	|
853	metaflush_statement
854	|
855	name_statement
856	|
857	local_statement
858	|
859	resource_node_statement
860	;
861
862name_statement:		NAME STR
863	{
864		switch (depth) {
865		case 1:
866			if (strlcpy(depth1_provname, $2,
867			    sizeof(depth1_provname)) >=
868			    sizeof(depth1_provname)) {
869				pjdlog_error("name argument is too long.");
870				free($2);
871				return (1);
872			}
873			break;
874		case 2:
875			if (!mynode)
876				break;
877			PJDLOG_ASSERT(curres != NULL);
878			if (strlcpy(curres->hr_provname, $2,
879			    sizeof(curres->hr_provname)) >=
880			    sizeof(curres->hr_provname)) {
881				pjdlog_error("name argument is too long.");
882				free($2);
883				return (1);
884			}
885			break;
886		default:
887			PJDLOG_ABORT("name at wrong depth level");
888		}
889		free($2);
890	}
891	;
892
893local_statement:	LOCAL STR
894	{
895		switch (depth) {
896		case 1:
897			if (strlcpy(depth1_localpath, $2,
898			    sizeof(depth1_localpath)) >=
899			    sizeof(depth1_localpath)) {
900				pjdlog_error("local argument is too long.");
901				free($2);
902				return (1);
903			}
904			break;
905		case 2:
906			if (!mynode)
907				break;
908			PJDLOG_ASSERT(curres != NULL);
909			if (strlcpy(curres->hr_localpath, $2,
910			    sizeof(curres->hr_localpath)) >=
911			    sizeof(curres->hr_localpath)) {
912				pjdlog_error("local argument is too long.");
913				free($2);
914				return (1);
915			}
916			break;
917		default:
918			PJDLOG_ABORT("local at wrong depth level");
919		}
920		free($2);
921	}
922	;
923
924resource_node_statement:ON resource_node_start OB resource_node_entries CB
925	{
926		mynode = false;
927	}
928	;
929
930resource_node_start:	STR
931	{
932		if (curres != NULL) {
933			switch (isitme($1)) {
934			case -1:
935				free($1);
936				return (1);
937			case 0:
938				break;
939			case 1:
940				mynode = hadmynode = true;
941				break;
942			default:
943				PJDLOG_ABORT("invalid isitme() return value");
944			}
945		}
946		free($1);
947	}
948	;
949
950resource_node_entries:
951	|
952	resource_node_entries resource_node_entry
953	;
954
955resource_node_entry:
956	name_statement
957	|
958	local_statement
959	|
960	remote_statement
961	|
962	source_statement
963	|
964	metaflush_statement
965	;
966
967remote_statement:	REMOTE remote_str
968	{
969		PJDLOG_ASSERT(depth == 2);
970		if (mynode) {
971			PJDLOG_ASSERT(curres != NULL);
972			if (strlcpy(curres->hr_remoteaddr, $2,
973			    sizeof(curres->hr_remoteaddr)) >=
974			    sizeof(curres->hr_remoteaddr)) {
975				pjdlog_error("remote argument is too long.");
976				free($2);
977				return (1);
978			}
979		}
980		free($2);
981	}
982	;
983
984remote_str:
985	NONE		{ $$ = strdup("none"); }
986	|
987	STR		{ }
988	;
989
990source_statement:	SOURCE STR
991	{
992		PJDLOG_ASSERT(depth == 2);
993		if (mynode) {
994			PJDLOG_ASSERT(curres != NULL);
995			if (strlcpy(curres->hr_sourceaddr, $2,
996			    sizeof(curres->hr_sourceaddr)) >=
997			    sizeof(curres->hr_sourceaddr)) {
998				pjdlog_error("source argument is too long.");
999				free($2);
1000				return (1);
1001			}
1002		}
1003		free($2);
1004	}
1005	;
1006