parse.y revision 226463
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: head/sbin/hastd/parse.y 226463 2011-10-17 12:22:09Z pjd $
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		    curres->hr_replication == HAST_REPLICATION_ASYNC) {
306			pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
307			    curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
308			    "memsync" : "async", "fullsync");
309			curres->hr_replication = HAST_REPLICATION_FULLSYNC;
310		}
311		if (curres->hr_checksum == -1) {
312			/*
313			 * Checksum is not set at resource-level.
314			 * Use global or default setting.
315			 */
316			curres->hr_checksum = depth0_checksum;
317		}
318		if (curres->hr_compression == -1) {
319			/*
320			 * Compression is not set at resource-level.
321			 * Use global or default setting.
322			 */
323			curres->hr_compression = depth0_compression;
324		}
325		if (curres->hr_timeout == -1) {
326			/*
327			 * Timeout is not set at resource-level.
328			 * Use global or default setting.
329			 */
330			curres->hr_timeout = depth0_timeout;
331		}
332		if (curres->hr_exec[0] == '\0') {
333			/*
334			 * Exec is not set at resource-level.
335			 * Use global or default setting.
336			 */
337			strlcpy(curres->hr_exec, depth0_exec,
338			    sizeof(curres->hr_exec));
339		}
340		if (curres->hr_metaflush == -1) {
341			/*
342			 * Metaflush is not set at resource-level.
343			 * Use global or default setting.
344			 */
345			curres->hr_metaflush = depth0_metaflush;
346		}
347	}
348
349	return (lconfig);
350}
351
352void
353yy_config_free(struct hastd_config *config)
354{
355	struct hastd_listen *lst;
356	struct hast_resource *res;
357
358	while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
359		TAILQ_REMOVE(&depth0_listen, lst, hl_next);
360		free(lst);
361	}
362	while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
363		TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
364		free(lst);
365	}
366	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
367		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
368		free(res);
369	}
370	free(config);
371}
372%}
373
374%token CONTROL PIDFILE LISTEN PORT REPLICATION CHECKSUM COMPRESSION METAFLUSH
375%token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
376%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
377%token NUM STR OB CB
378
379%type <str> remote_str
380%type <num> replication_type
381%type <num> checksum_type
382%type <num> compression_type
383%type <num> boolean
384
385%union
386{
387	int num;
388	char *str;
389}
390
391%token <num> NUM
392%token <str> STR
393
394%%
395
396statements:
397	|
398	statements statement
399	;
400
401statement:
402	control_statement
403	|
404	pidfile_statement
405	|
406	listen_statement
407	|
408	replication_statement
409	|
410	checksum_statement
411	|
412	compression_statement
413	|
414	timeout_statement
415	|
416	exec_statement
417	|
418	metaflush_statement
419	|
420	node_statement
421	|
422	resource_statement
423	;
424
425control_statement:	CONTROL STR
426	{
427		switch (depth) {
428		case 0:
429			if (strlcpy(depth0_control, $2,
430			    sizeof(depth0_control)) >=
431			    sizeof(depth0_control)) {
432				pjdlog_error("control argument is too long.");
433				free($2);
434				return (1);
435			}
436			break;
437		case 1:
438			if (!mynode)
439				break;
440			if (strlcpy(lconfig->hc_controladdr, $2,
441			    sizeof(lconfig->hc_controladdr)) >=
442			    sizeof(lconfig->hc_controladdr)) {
443				pjdlog_error("control argument is too long.");
444				free($2);
445				return (1);
446			}
447			break;
448		default:
449			PJDLOG_ABORT("control at wrong depth level");
450		}
451		free($2);
452	}
453	;
454
455pidfile_statement:	PIDFILE STR
456	{
457		switch (depth) {
458		case 0:
459			if (strlcpy(depth0_pidfile, $2,
460			    sizeof(depth0_pidfile)) >=
461			    sizeof(depth0_pidfile)) {
462				pjdlog_error("pidfile argument is too long.");
463				free($2);
464				return (1);
465			}
466			break;
467		case 1:
468			if (!mynode)
469				break;
470			if (strlcpy(lconfig->hc_pidfile, $2,
471			    sizeof(lconfig->hc_pidfile)) >=
472			    sizeof(lconfig->hc_pidfile)) {
473				pjdlog_error("pidfile argument is too long.");
474				free($2);
475				return (1);
476			}
477			break;
478		default:
479			PJDLOG_ABORT("pidfile at wrong depth level");
480		}
481		free($2);
482	}
483	;
484
485listen_statement:	LISTEN STR
486	{
487		struct hastd_listen *lst;
488
489		lst = calloc(1, sizeof(*lst));
490		if (lst == NULL) {
491			pjdlog_error("Unable to allocate memory for listen address.");
492			free($2);
493			return (1);
494		}
495		if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
496		    sizeof(lst->hl_addr)) {
497			pjdlog_error("listen argument is too long.");
498			free($2);
499			free(lst);
500			return (1);
501		}
502		switch (depth) {
503		case 0:
504			TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
505			break;
506		case 1:
507			if (mynode)
508				TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
509			else
510				free(lst);
511			break;
512		default:
513			PJDLOG_ABORT("listen at wrong depth level");
514		}
515		free($2);
516	}
517	;
518
519replication_statement:	REPLICATION replication_type
520	{
521		switch (depth) {
522		case 0:
523			depth0_replication = $2;
524			break;
525		case 1:
526			PJDLOG_ASSERT(curres != NULL);
527			curres->hr_replication = $2;
528			break;
529		default:
530			PJDLOG_ABORT("replication at wrong depth level");
531		}
532	}
533	;
534
535replication_type:
536	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
537	|
538	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
539	|
540	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
541	;
542
543checksum_statement:	CHECKSUM checksum_type
544	{
545		switch (depth) {
546		case 0:
547			depth0_checksum = $2;
548			break;
549		case 1:
550			PJDLOG_ASSERT(curres != NULL);
551			curres->hr_checksum = $2;
552			break;
553		default:
554			PJDLOG_ABORT("checksum at wrong depth level");
555		}
556	}
557	;
558
559checksum_type:
560	NONE		{ $$ = HAST_CHECKSUM_NONE; }
561	|
562	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
563	|
564	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
565	;
566
567compression_statement:	COMPRESSION compression_type
568	{
569		switch (depth) {
570		case 0:
571			depth0_compression = $2;
572			break;
573		case 1:
574			PJDLOG_ASSERT(curres != NULL);
575			curres->hr_compression = $2;
576			break;
577		default:
578			PJDLOG_ABORT("compression at wrong depth level");
579		}
580	}
581	;
582
583compression_type:
584	NONE		{ $$ = HAST_COMPRESSION_NONE; }
585	|
586	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
587	|
588	LZF		{ $$ = HAST_COMPRESSION_LZF; }
589	;
590
591timeout_statement:	TIMEOUT NUM
592	{
593		if ($2 <= 0) {
594			pjdlog_error("Negative or zero timeout.");
595			return (1);
596		}
597		switch (depth) {
598		case 0:
599			depth0_timeout = $2;
600			break;
601		case 1:
602			PJDLOG_ASSERT(curres != NULL);
603			curres->hr_timeout = $2;
604			break;
605		default:
606			PJDLOG_ABORT("timeout at wrong depth level");
607		}
608	}
609	;
610
611exec_statement:		EXEC STR
612	{
613		switch (depth) {
614		case 0:
615			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
616			    sizeof(depth0_exec)) {
617				pjdlog_error("Exec path is too long.");
618				free($2);
619				return (1);
620			}
621			break;
622		case 1:
623			PJDLOG_ASSERT(curres != NULL);
624			if (strlcpy(curres->hr_exec, $2,
625			    sizeof(curres->hr_exec)) >=
626			    sizeof(curres->hr_exec)) {
627				pjdlog_error("Exec path is too long.");
628				free($2);
629				return (1);
630			}
631			break;
632		default:
633			PJDLOG_ABORT("exec at wrong depth level");
634		}
635		free($2);
636	}
637	;
638
639metaflush_statement:	METAFLUSH boolean
640	{
641		switch (depth) {
642		case 0:
643			depth0_metaflush = $2;
644			break;
645		case 1:
646			PJDLOG_ASSERT(curres != NULL);
647			depth1_metaflush = $2;
648			break;
649		case 2:
650			if (!mynode)
651				break;
652			PJDLOG_ASSERT(curres != NULL);
653			curres->hr_metaflush = $2;
654			break;
655		default:
656			PJDLOG_ABORT("metaflush at wrong depth level");
657		}
658	}
659	;
660
661boolean:
662	ON		{ $$ = 1; }
663	|
664	OFF		{ $$ = 0; }
665	;
666
667node_statement:		ON node_start OB node_entries CB
668	{
669		mynode = false;
670	}
671	;
672
673node_start:	STR
674	{
675		switch (isitme($1)) {
676		case -1:
677			free($1);
678			return (1);
679		case 0:
680			break;
681		case 1:
682			mynode = true;
683			break;
684		default:
685			PJDLOG_ABORT("invalid isitme() return value");
686		}
687		free($1);
688	}
689	;
690
691node_entries:
692	|
693	node_entries node_entry
694	;
695
696node_entry:
697	control_statement
698	|
699	pidfile_statement
700	|
701	listen_statement
702	;
703
704resource_statement:	RESOURCE resource_start OB resource_entries CB
705	{
706		if (curres != NULL) {
707			/*
708			 * There must be section for this node, at least with
709			 * remote address configuration.
710			 */
711			if (!hadmynode) {
712				char *names;
713
714				if (node_names(&names) != 0)
715					return (1);
716				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
717				    curres->hr_name, names);
718				return (1);
719			}
720
721			/*
722			 * Let's see if there are some resource-level settings
723			 * that we can use for node-level settings.
724			 */
725			if (curres->hr_provname[0] == '\0' &&
726			    depth1_provname[0] != '\0') {
727				/*
728				 * Provider name is not set at node-level,
729				 * but is set at resource-level, use it.
730				 */
731				strlcpy(curres->hr_provname, depth1_provname,
732				    sizeof(curres->hr_provname));
733			}
734			if (curres->hr_localpath[0] == '\0' &&
735			    depth1_localpath[0] != '\0') {
736				/*
737				 * Path to local provider is not set at
738				 * node-level, but is set at resource-level,
739				 * use it.
740				 */
741				strlcpy(curres->hr_localpath, depth1_localpath,
742				    sizeof(curres->hr_localpath));
743			}
744			if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
745				/*
746				 * Metaflush is not set at node-level,
747				 * but is set at resource-level, use it.
748				 */
749				curres->hr_metaflush = depth1_metaflush;
750			}
751
752			/*
753			 * If provider name is not given, use resource name
754			 * as provider name.
755			 */
756			if (curres->hr_provname[0] == '\0') {
757				strlcpy(curres->hr_provname, curres->hr_name,
758				    sizeof(curres->hr_provname));
759			}
760
761			/*
762			 * Remote address has to be configured at this point.
763			 */
764			if (curres->hr_remoteaddr[0] == '\0') {
765				pjdlog_error("Remote address not configured for resource %s.",
766				    curres->hr_name);
767				return (1);
768			}
769			/*
770			 * Path to local provider has to be configured at this
771			 * point.
772			 */
773			if (curres->hr_localpath[0] == '\0') {
774				pjdlog_error("Path to local component not configured for resource %s.",
775				    curres->hr_name);
776				return (1);
777			}
778
779			/* Put it onto resource list. */
780			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
781			curres = NULL;
782		}
783	}
784	;
785
786resource_start:	STR
787	{
788		/* Check if there is no duplicate entry. */
789		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
790			if (strcmp(curres->hr_name, $1) == 0) {
791				pjdlog_error("Resource %s configured more than once.",
792				    curres->hr_name);
793				free($1);
794				return (1);
795			}
796		}
797
798		/*
799		 * Clear those, so we can tell if they were set at
800		 * resource-level or not.
801		 */
802		depth1_provname[0] = '\0';
803		depth1_localpath[0] = '\0';
804		depth1_metaflush = -1;
805		hadmynode = false;
806
807		curres = calloc(1, sizeof(*curres));
808		if (curres == NULL) {
809			pjdlog_error("Unable to allocate memory for resource.");
810			free($1);
811			return (1);
812		}
813		if (strlcpy(curres->hr_name, $1,
814		    sizeof(curres->hr_name)) >=
815		    sizeof(curres->hr_name)) {
816			pjdlog_error("Resource name is too long.");
817			free($1);
818			return (1);
819		}
820		free($1);
821		curres->hr_role = HAST_ROLE_INIT;
822		curres->hr_previous_role = HAST_ROLE_INIT;
823		curres->hr_replication = -1;
824		curres->hr_checksum = -1;
825		curres->hr_compression = -1;
826		curres->hr_timeout = -1;
827		curres->hr_exec[0] = '\0';
828		curres->hr_provname[0] = '\0';
829		curres->hr_localpath[0] = '\0';
830		curres->hr_localfd = -1;
831		curres->hr_localflush = true;
832		curres->hr_metaflush = -1;
833		curres->hr_remoteaddr[0] = '\0';
834		curres->hr_sourceaddr[0] = '\0';
835		curres->hr_ggateunit = -1;
836	}
837	;
838
839resource_entries:
840	|
841	resource_entries resource_entry
842	;
843
844resource_entry:
845	replication_statement
846	|
847	checksum_statement
848	|
849	compression_statement
850	|
851	timeout_statement
852	|
853	exec_statement
854	|
855	metaflush_statement
856	|
857	name_statement
858	|
859	local_statement
860	|
861	resource_node_statement
862	;
863
864name_statement:		NAME STR
865	{
866		switch (depth) {
867		case 1:
868			if (strlcpy(depth1_provname, $2,
869			    sizeof(depth1_provname)) >=
870			    sizeof(depth1_provname)) {
871				pjdlog_error("name argument is too long.");
872				free($2);
873				return (1);
874			}
875			break;
876		case 2:
877			if (!mynode)
878				break;
879			PJDLOG_ASSERT(curres != NULL);
880			if (strlcpy(curres->hr_provname, $2,
881			    sizeof(curres->hr_provname)) >=
882			    sizeof(curres->hr_provname)) {
883				pjdlog_error("name argument is too long.");
884				free($2);
885				return (1);
886			}
887			break;
888		default:
889			PJDLOG_ABORT("name at wrong depth level");
890		}
891		free($2);
892	}
893	;
894
895local_statement:	LOCAL STR
896	{
897		switch (depth) {
898		case 1:
899			if (strlcpy(depth1_localpath, $2,
900			    sizeof(depth1_localpath)) >=
901			    sizeof(depth1_localpath)) {
902				pjdlog_error("local argument is too long.");
903				free($2);
904				return (1);
905			}
906			break;
907		case 2:
908			if (!mynode)
909				break;
910			PJDLOG_ASSERT(curres != NULL);
911			if (strlcpy(curres->hr_localpath, $2,
912			    sizeof(curres->hr_localpath)) >=
913			    sizeof(curres->hr_localpath)) {
914				pjdlog_error("local argument is too long.");
915				free($2);
916				return (1);
917			}
918			break;
919		default:
920			PJDLOG_ABORT("local at wrong depth level");
921		}
922		free($2);
923	}
924	;
925
926resource_node_statement:ON resource_node_start OB resource_node_entries CB
927	{
928		mynode = false;
929	}
930	;
931
932resource_node_start:	STR
933	{
934		if (curres != NULL) {
935			switch (isitme($1)) {
936			case -1:
937				free($1);
938				return (1);
939			case 0:
940				break;
941			case 1:
942				mynode = hadmynode = true;
943				break;
944			default:
945				PJDLOG_ABORT("invalid isitme() return value");
946			}
947		}
948		free($1);
949	}
950	;
951
952resource_node_entries:
953	|
954	resource_node_entries resource_node_entry
955	;
956
957resource_node_entry:
958	name_statement
959	|
960	local_statement
961	|
962	remote_statement
963	|
964	source_statement
965	|
966	metaflush_statement
967	;
968
969remote_statement:	REMOTE remote_str
970	{
971		PJDLOG_ASSERT(depth == 2);
972		if (mynode) {
973			PJDLOG_ASSERT(curres != NULL);
974			if (strlcpy(curres->hr_remoteaddr, $2,
975			    sizeof(curres->hr_remoteaddr)) >=
976			    sizeof(curres->hr_remoteaddr)) {
977				pjdlog_error("remote argument is too long.");
978				free($2);
979				return (1);
980			}
981		}
982		free($2);
983	}
984	;
985
986remote_str:
987	NONE		{ $$ = strdup("none"); }
988	|
989	STR		{ }
990	;
991
992source_statement:	SOURCE STR
993	{
994		PJDLOG_ASSERT(depth == 2);
995		if (mynode) {
996			PJDLOG_ASSERT(curres != NULL);
997			if (strlcpy(curres->hr_sourceaddr, $2,
998			    sizeof(curres->hr_sourceaddr)) >=
999			    sizeof(curres->hr_sourceaddr)) {
1000				pjdlog_error("source argument is too long.");
1001				free($2);
1002				return (1);
1003			}
1004		}
1005		free($2);
1006	}
1007	;
1008