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