parse.y revision 225830
150397Sobrien%{
2169689Skan/*-
390075Sobrien * Copyright (c) 2009-2010 The FreeBSD Foundation
4122180Skan * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
550397Sobrien * All rights reserved.
6132718Skan *
750397Sobrien * This software was developed by Pawel Jakub Dawidek under sponsorship from
8132718Skan * the FreeBSD Foundation.
950397Sobrien *
1050397Sobrien * Redistribution and use in source and binary forms, with or without
1150397Sobrien * modification, are permitted provided that the following conditions
1250397Sobrien * are met:
13132718Skan * 1. Redistributions of source code must retain the above copyright
1450397Sobrien *    notice, this list of conditions and the following disclaimer.
1550397Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1650397Sobrien *    notice, this list of conditions and the following disclaimer in the
1750397Sobrien *    documentation and/or other materials provided with the distribution.
1850397Sobrien *
19132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2250397Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23122180Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2450397Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2550397Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2690075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2750397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2850397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2990075Sobrien * SUCH DAMAGE.
3050397Sobrien *
3150397Sobrien * $FreeBSD: head/sbin/hastd/parse.y 225830 2011-09-28 13:08:51Z pjd $
32122180Skan */
33122180Skan
34122180Skan#include <sys/param.h>	/* MAXHOSTNAMELEN */
35122180Skan#include <sys/queue.h>
36122180Skan#include <sys/socket.h>
37122180Skan#include <sys/sysctl.h>
38122180Skan
39122180Skan#include <arpa/inet.h>
40122180Skan
41122180Skan#include <err.h>
42122180Skan#include <errno.h>
43122180Skan#include <stdio.h>
44122180Skan#include <string.h>
45122180Skan#include <sysexits.h>
4650397Sobrien#include <unistd.h>
47122180Skan
48122180Skan#include <pjdlog.h>
49122180Skan
5050397Sobrien#include "hast.h"
5150397Sobrien
5250397Sobrienextern int depth;
5350397Sobrienextern int lineno;
5450397Sobrien
5550397Sobrienextern FILE *yyin;
5650397Sobrienextern char *yytext;
57122180Skan
5850397Sobrienstatic struct hastd_config *lconfig;
5950397Sobrienstatic struct hast_resource *curres;
60122180Skanstatic bool mynode, hadmynode;
6150397Sobrien
62122180Skanstatic char depth0_control[HAST_ADDRSIZE];
63122180Skanstatic char depth0_listen_tcp4[HAST_ADDRSIZE];
64117395Skanstatic char depth0_listen_tcp6[HAST_ADDRSIZE];
65117395Skanstatic TAILQ_HEAD(, hastd_listen) depth0_listen;
66122180Skanstatic int depth0_replication;
6750397Sobrienstatic int depth0_checksum;
68122180Skanstatic int depth0_compression;
69122180Skanstatic int depth0_timeout;
7050397Sobrienstatic char depth0_exec[PATH_MAX];
7150397Sobrienstatic int depth0_metaflush;
72122180Skan
7350397Sobrienstatic char depth1_provname[PATH_MAX];
7450397Sobrienstatic char depth1_localpath[PATH_MAX];
7550397Sobrienstatic int depth1_metaflush;
7650397Sobrien
7750397Sobrienextern void yyrestart(FILE *);
7850397Sobrien
7950397Sobrienstatic int
8050397Sobrienisitme(const char *name)
8150397Sobrien{
8250397Sobrien	char buf[MAXHOSTNAMELEN];
8350397Sobrien	char *pos;
8450397Sobrien	size_t bufsize;
8550397Sobrien
8650397Sobrien	/*
8750397Sobrien	 * First check if the give name matches our full hostname.
8890075Sobrien	 */
8990075Sobrien	if (gethostname(buf, sizeof(buf)) < 0) {
9050397Sobrien		pjdlog_errno(LOG_ERR, "gethostname() failed");
9150397Sobrien		return (-1);
9250397Sobrien	}
9350397Sobrien	if (strcmp(buf, name) == 0)
9450397Sobrien		return (1);
9550397Sobrien
9650397Sobrien	/*
9750397Sobrien	 * Now check if it matches first part of the host name.
98122180Skan	 */
9950397Sobrien	pos = strchr(buf, '.');
10050397Sobrien	if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
10150397Sobrien	    strncmp(buf, name, pos - buf) == 0) {
10250397Sobrien		return (1);
103122180Skan	}
10450397Sobrien
10550397Sobrien	/*
10650397Sobrien	 * At the end check if name is equal to our host's UUID.
10750397Sobrien	 */
10850397Sobrien	bufsize = sizeof(buf);
10950397Sobrien	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
11050397Sobrien		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
11150397Sobrien		return (-1);
11250397Sobrien	}
11350397Sobrien	if (strcasecmp(buf, name) == 0)
11450397Sobrien		return (1);
11550397Sobrien
11650397Sobrien	/*
117122180Skan	 * Looks like this isn't about us.
11850397Sobrien	 */
11950397Sobrien	return (0);
12050397Sobrien}
12150397Sobrien
12250397Sobrienstatic bool
12350397Sobrienfamily_supported(int family)
12450397Sobrien{
12550397Sobrien	int sock;
12650397Sobrien
12750397Sobrien	sock = socket(family, SOCK_STREAM, 0);
12850397Sobrien	if (sock == -1 && errno == EPROTONOSUPPORT)
12950397Sobrien		return (false);
13050397Sobrien	if (sock >= 0)
13150397Sobrien		(void)close(sock);
13250397Sobrien	return (true);
133122180Skan}
134122180Skan
135122180Skanstatic int
136122180Skannode_names(char **namesp)
137122180Skan{
138122180Skan	static char names[MAXHOSTNAMELEN * 3];
139122180Skan	char buf[MAXHOSTNAMELEN];
140122180Skan	char *pos;
141122180Skan	size_t bufsize;
142122180Skan
143122180Skan	if (gethostname(buf, sizeof(buf)) < 0) {
144122180Skan		pjdlog_errno(LOG_ERR, "gethostname() failed");
145122180Skan		return (-1);
146132718Skan	}
147122180Skan
148122180Skan	/* First component of the host name. */
149122180Skan	pos = strchr(buf, '.');
150122180Skan	if (pos != NULL && pos != buf) {
151122180Skan		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
152122180Skan		    sizeof(names)));
153122180Skan		(void)strlcat(names, ", ", sizeof(names));
15450397Sobrien	}
15550397Sobrien
156122180Skan	/* Full host name. */
157122180Skan	(void)strlcat(names, buf, sizeof(names));
158122180Skan	(void)strlcat(names, ", ", sizeof(names));
159122180Skan
160122180Skan	/* Host UUID. */
161122180Skan	bufsize = sizeof(buf);
162122180Skan	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
163122180Skan		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
164122180Skan		return (-1);
165122180Skan	}
166122180Skan	(void)strlcat(names, buf, sizeof(names));
167122180Skan
168122180Skan	*namesp = names;
169122180Skan
17050397Sobrien	return (0);
17150397Sobrien}
17250397Sobrien
17350397Sobrienvoid
174122180Skanyyerror(const char *str)
17550397Sobrien{
17650397Sobrien
177122180Skan	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
178122180Skan	    lineno, yytext, str);
17950397Sobrien}
18050397Sobrien
181122180Skanstruct hastd_config *
182122180Skanyy_config_parse(const char *config, bool exitonerror)
183122180Skan{
184122180Skan	int ret;
185122180Skan
18650397Sobrien	curres = NULL;
18750397Sobrien	mynode = false;
18850397Sobrien	depth = 0;
189122180Skan	lineno = 0;
190122180Skan
191122180Skan	depth0_timeout = HAST_TIMEOUT;
19250397Sobrien	depth0_replication = HAST_REPLICATION_FULLSYNC;
193122180Skan	depth0_checksum = HAST_CHECKSUM_NONE;
194122180Skan	depth0_compression = HAST_COMPRESSION_HOLE;
195122180Skan	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
19650397Sobrien	TAILQ_INIT(&depth0_listen);
19750397Sobrien	strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
198122180Skan	    sizeof(depth0_listen_tcp4));
199122180Skan	strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
200122180Skan	    sizeof(depth0_listen_tcp6));
201122180Skan	depth0_exec[0] = '\0';
202122180Skan	depth0_metaflush = 1;
203122180Skan
204122180Skan	lconfig = calloc(1, sizeof(*lconfig));
205122180Skan	if (lconfig == NULL) {
20650397Sobrien		pjdlog_error("Unable to allocate memory for configuration.");
20750397Sobrien		if (exitonerror)
20850397Sobrien			exit(EX_TEMPFAIL);
209122180Skan		return (NULL);
21050397Sobrien	}
211122180Skan
212122180Skan	TAILQ_INIT(&lconfig->hc_listen);
213122180Skan	TAILQ_INIT(&lconfig->hc_resources);
214122180Skan
215122180Skan	yyin = fopen(config, "r");
216122180Skan	if (yyin == NULL) {
217122180Skan		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
218122180Skan		    config);
219122180Skan		yy_config_free(lconfig);
220122180Skan		if (exitonerror)
221122180Skan			exit(EX_OSFILE);
222122180Skan		return (NULL);
223122180Skan	}
224122180Skan	yyrestart(yyin);
225117395Skan	ret = yyparse();
22650397Sobrien	fclose(yyin);
22750397Sobrien	if (ret != 0) {
228117395Skan		yy_config_free(lconfig);
229122180Skan		if (exitonerror)
23050397Sobrien			exit(EX_CONFIG);
23150397Sobrien		return (NULL);
232117395Skan	}
23350397Sobrien
23450397Sobrien	/*
23550397Sobrien	 * Let's see if everything is set up.
23650397Sobrien	 */
23750397Sobrien	if (lconfig->hc_controladdr[0] == '\0') {
23850397Sobrien		strlcpy(lconfig->hc_controladdr, depth0_control,
23950397Sobrien		    sizeof(lconfig->hc_controladdr));
24050397Sobrien	}
24150397Sobrien	if (!TAILQ_EMPTY(&depth0_listen))
24250397Sobrien		TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
24350397Sobrien	if (TAILQ_EMPTY(&lconfig->hc_listen)) {
24450397Sobrien		struct hastd_listen *lst;
24550397Sobrien
24650397Sobrien		if (family_supported(AF_INET)) {
24750397Sobrien			lst = calloc(1, sizeof(*lst));
24850397Sobrien			if (lst == NULL) {
24950397Sobrien				pjdlog_error("Unable to allocate memory for listen address.");
25050397Sobrien				yy_config_free(lconfig);
251117395Skan				if (exitonerror)
25250397Sobrien					exit(EX_TEMPFAIL);
25350397Sobrien				return (NULL);
25450397Sobrien			}
255122180Skan			(void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
256169689Skan			    sizeof(lst->hl_addr));
25750397Sobrien			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
25850397Sobrien		} else {
259122180Skan			pjdlog_debug(1,
260122180Skan			    "No IPv4 support in the kernel, not listening on IPv4 address.");
261122180Skan		}
262122180Skan		if (family_supported(AF_INET6)) {
26350397Sobrien			lst = calloc(1, sizeof(*lst));
26450397Sobrien			if (lst == NULL) {
26550397Sobrien				pjdlog_error("Unable to allocate memory for listen address.");
26650397Sobrien				yy_config_free(lconfig);
26750397Sobrien				if (exitonerror)
26850397Sobrien					exit(EX_TEMPFAIL);
269122180Skan				return (NULL);
270122180Skan			}
271122180Skan			(void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
272122180Skan			    sizeof(lst->hl_addr));
273132718Skan			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
27450397Sobrien		} else {
27550397Sobrien			pjdlog_debug(1,
27650397Sobrien			    "No IPv6 support in the kernel, not listening on IPv6 address.");
277122180Skan		}
27850397Sobrien		if (TAILQ_EMPTY(&lconfig->hc_listen)) {
27990075Sobrien			pjdlog_error("No address to listen on.");
28090075Sobrien			yy_config_free(lconfig);
28190075Sobrien			if (exitonerror)
28290075Sobrien				exit(EX_TEMPFAIL);
28390075Sobrien			return (NULL);
28490075Sobrien		}
28590075Sobrien	}
28690075Sobrien	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
28790075Sobrien		PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
28890075Sobrien		PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
28990075Sobrien		PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
29090075Sobrien
29190075Sobrien		if (curres->hr_replication == -1) {
29290075Sobrien			/*
29390075Sobrien			 * Replication is not set at resource-level.
29490075Sobrien			 * Use global or default setting.
29590075Sobrien			 */
29690075Sobrien			curres->hr_replication = depth0_replication;
29790075Sobrien		}
29890075Sobrien		if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
29990075Sobrien		    curres->hr_replication == HAST_REPLICATION_ASYNC) {
30090075Sobrien			pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
30190075Sobrien			    curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
30290075Sobrien			    "memsync" : "async", "fullsync");
30390075Sobrien			curres->hr_replication = HAST_REPLICATION_FULLSYNC;
30490075Sobrien		}
30590075Sobrien		if (curres->hr_checksum == -1) {
30690075Sobrien			/*
30790075Sobrien			 * Checksum is not set at resource-level.
30890075Sobrien			 * Use global or default setting.
30990075Sobrien			 */
31090075Sobrien			curres->hr_checksum = depth0_checksum;
31190075Sobrien		}
312		if (curres->hr_compression == -1) {
313			/*
314			 * Compression is not set at resource-level.
315			 * Use global or default setting.
316			 */
317			curres->hr_compression = depth0_compression;
318		}
319		if (curres->hr_timeout == -1) {
320			/*
321			 * Timeout is not set at resource-level.
322			 * Use global or default setting.
323			 */
324			curres->hr_timeout = depth0_timeout;
325		}
326		if (curres->hr_exec[0] == '\0') {
327			/*
328			 * Exec is not set at resource-level.
329			 * Use global or default setting.
330			 */
331			strlcpy(curres->hr_exec, depth0_exec,
332			    sizeof(curres->hr_exec));
333		}
334		if (curres->hr_metaflush == -1) {
335			/*
336			 * Metaflush is not set at resource-level.
337			 * Use global or default setting.
338			 */
339			curres->hr_metaflush = depth0_metaflush;
340		}
341	}
342
343	return (lconfig);
344}
345
346void
347yy_config_free(struct hastd_config *config)
348{
349	struct hastd_listen *lst;
350	struct hast_resource *res;
351
352	while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
353		TAILQ_REMOVE(&depth0_listen, lst, hl_next);
354		free(lst);
355	}
356	while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
357		TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
358		free(lst);
359	}
360	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
361		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
362		free(res);
363	}
364	free(config);
365}
366%}
367
368%token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION METAFLUSH
369%token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
370%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
371%token NUM STR OB CB
372
373%type <str> remote_str
374%type <num> replication_type
375%type <num> checksum_type
376%type <num> compression_type
377%type <num> boolean
378
379%union
380{
381	int num;
382	char *str;
383}
384
385%token <num> NUM
386%token <str> STR
387
388%%
389
390statements:
391	|
392	statements statement
393	;
394
395statement:
396	control_statement
397	|
398	listen_statement
399	|
400	replication_statement
401	|
402	checksum_statement
403	|
404	compression_statement
405	|
406	timeout_statement
407	|
408	exec_statement
409	|
410	metaflush_statement
411	|
412	node_statement
413	|
414	resource_statement
415	;
416
417control_statement:	CONTROL STR
418	{
419		switch (depth) {
420		case 0:
421			if (strlcpy(depth0_control, $2,
422			    sizeof(depth0_control)) >=
423			    sizeof(depth0_control)) {
424				pjdlog_error("control argument is too long.");
425				free($2);
426				return (1);
427			}
428			break;
429		case 1:
430			if (!mynode)
431				break;
432			if (strlcpy(lconfig->hc_controladdr, $2,
433			    sizeof(lconfig->hc_controladdr)) >=
434			    sizeof(lconfig->hc_controladdr)) {
435				pjdlog_error("control argument is too long.");
436				free($2);
437				return (1);
438			}
439			break;
440		default:
441			PJDLOG_ABORT("control at wrong depth level");
442		}
443		free($2);
444	}
445	;
446
447listen_statement:	LISTEN STR
448	{
449		struct hastd_listen *lst;
450
451		lst = calloc(1, sizeof(*lst));
452		if (lst == NULL) {
453			pjdlog_error("Unable to allocate memory for listen address.");
454			free($2);
455			return (1);
456		}
457		if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
458		    sizeof(lst->hl_addr)) {
459			pjdlog_error("listen argument is too long.");
460			free($2);
461			free(lst);
462			return (1);
463		}
464		switch (depth) {
465		case 0:
466			TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
467			break;
468		case 1:
469			if (mynode)
470				TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
471			else
472				free(lst);
473			break;
474		default:
475			PJDLOG_ABORT("listen at wrong depth level");
476		}
477		free($2);
478	}
479	;
480
481replication_statement:	REPLICATION replication_type
482	{
483		switch (depth) {
484		case 0:
485			depth0_replication = $2;
486			break;
487		case 1:
488			PJDLOG_ASSERT(curres != NULL);
489			curres->hr_replication = $2;
490			break;
491		default:
492			PJDLOG_ABORT("replication at wrong depth level");
493		}
494	}
495	;
496
497replication_type:
498	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
499	|
500	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
501	|
502	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
503	;
504
505checksum_statement:	CHECKSUM checksum_type
506	{
507		switch (depth) {
508		case 0:
509			depth0_checksum = $2;
510			break;
511		case 1:
512			PJDLOG_ASSERT(curres != NULL);
513			curres->hr_checksum = $2;
514			break;
515		default:
516			PJDLOG_ABORT("checksum at wrong depth level");
517		}
518	}
519	;
520
521checksum_type:
522	NONE		{ $$ = HAST_CHECKSUM_NONE; }
523	|
524	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
525	|
526	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
527	;
528
529compression_statement:	COMPRESSION compression_type
530	{
531		switch (depth) {
532		case 0:
533			depth0_compression = $2;
534			break;
535		case 1:
536			PJDLOG_ASSERT(curres != NULL);
537			curres->hr_compression = $2;
538			break;
539		default:
540			PJDLOG_ABORT("compression at wrong depth level");
541		}
542	}
543	;
544
545compression_type:
546	NONE		{ $$ = HAST_COMPRESSION_NONE; }
547	|
548	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
549	|
550	LZF		{ $$ = HAST_COMPRESSION_LZF; }
551	;
552
553timeout_statement:	TIMEOUT NUM
554	{
555		if ($2 <= 0) {
556			pjdlog_error("Negative or zero timeout.");
557			return (1);
558		}
559		switch (depth) {
560		case 0:
561			depth0_timeout = $2;
562			break;
563		case 1:
564			PJDLOG_ASSERT(curres != NULL);
565			curres->hr_timeout = $2;
566			break;
567		default:
568			PJDLOG_ABORT("timeout at wrong depth level");
569		}
570	}
571	;
572
573exec_statement:		EXEC STR
574	{
575		switch (depth) {
576		case 0:
577			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
578			    sizeof(depth0_exec)) {
579				pjdlog_error("Exec path is too long.");
580				free($2);
581				return (1);
582			}
583			break;
584		case 1:
585			PJDLOG_ASSERT(curres != NULL);
586			if (strlcpy(curres->hr_exec, $2,
587			    sizeof(curres->hr_exec)) >=
588			    sizeof(curres->hr_exec)) {
589				pjdlog_error("Exec path is too long.");
590				free($2);
591				return (1);
592			}
593			break;
594		default:
595			PJDLOG_ABORT("exec at wrong depth level");
596		}
597		free($2);
598	}
599	;
600
601metaflush_statement:	METAFLUSH boolean
602	{
603		switch (depth) {
604		case 0:
605			depth0_metaflush = $2;
606			break;
607		case 1:
608			PJDLOG_ASSERT(curres != NULL);
609			depth1_metaflush = $2;
610			break;
611		case 2:
612			if (!mynode)
613				break;
614			PJDLOG_ASSERT(curres != NULL);
615			curres->hr_metaflush = $2;
616			break;
617		default:
618			PJDLOG_ABORT("metaflush at wrong depth level");
619		}
620	}
621	;
622
623boolean:
624	ON		{ $$ = 1; }
625	|
626	OFF		{ $$ = 0; }
627	;
628
629node_statement:		ON node_start OB node_entries CB
630	{
631		mynode = false;
632	}
633	;
634
635node_start:	STR
636	{
637		switch (isitme($1)) {
638		case -1:
639			free($1);
640			return (1);
641		case 0:
642			break;
643		case 1:
644			mynode = true;
645			break;
646		default:
647			PJDLOG_ABORT("invalid isitme() return value");
648		}
649		free($1);
650	}
651	;
652
653node_entries:
654	|
655	node_entries node_entry
656	;
657
658node_entry:
659	control_statement
660	|
661	listen_statement
662	;
663
664resource_statement:	RESOURCE resource_start OB resource_entries CB
665	{
666		if (curres != NULL) {
667			/*
668			 * There must be section for this node, at least with
669			 * remote address configuration.
670			 */
671			if (!hadmynode) {
672				char *names;
673
674				if (node_names(&names) != 0)
675					return (1);
676				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
677				    curres->hr_name, names);
678				return (1);
679			}
680
681			/*
682			 * Let's see if there are some resource-level settings
683			 * that we can use for node-level settings.
684			 */
685			if (curres->hr_provname[0] == '\0' &&
686			    depth1_provname[0] != '\0') {
687				/*
688				 * Provider name is not set at node-level,
689				 * but is set at resource-level, use it.
690				 */
691				strlcpy(curres->hr_provname, depth1_provname,
692				    sizeof(curres->hr_provname));
693			}
694			if (curres->hr_localpath[0] == '\0' &&
695			    depth1_localpath[0] != '\0') {
696				/*
697				 * Path to local provider is not set at
698				 * node-level, but is set at resource-level,
699				 * use it.
700				 */
701				strlcpy(curres->hr_localpath, depth1_localpath,
702				    sizeof(curres->hr_localpath));
703			}
704			if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
705				/*
706				 * Metaflush is not set at node-level,
707				 * but is set at resource-level, use it.
708				 */
709				curres->hr_metaflush = depth1_metaflush;
710			}
711
712			/*
713			 * If provider name is not given, use resource name
714			 * as provider name.
715			 */
716			if (curres->hr_provname[0] == '\0') {
717				strlcpy(curres->hr_provname, curres->hr_name,
718				    sizeof(curres->hr_provname));
719			}
720
721			/*
722			 * Remote address has to be configured at this point.
723			 */
724			if (curres->hr_remoteaddr[0] == '\0') {
725				pjdlog_error("Remote address not configured for resource %s.",
726				    curres->hr_name);
727				return (1);
728			}
729			/*
730			 * Path to local provider has to be configured at this
731			 * point.
732			 */
733			if (curres->hr_localpath[0] == '\0') {
734				pjdlog_error("Path to local component not configured for resource %s.",
735				    curres->hr_name);
736				return (1);
737			}
738
739			/* Put it onto resource list. */
740			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
741			curres = NULL;
742		}
743	}
744	;
745
746resource_start:	STR
747	{
748		/* Check if there is no duplicate entry. */
749		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
750			if (strcmp(curres->hr_name, $1) == 0) {
751				pjdlog_error("Resource %s configured more than once.",
752				    curres->hr_name);
753				free($1);
754				return (1);
755			}
756		}
757
758		/*
759		 * Clear those, so we can tell if they were set at
760		 * resource-level or not.
761		 */
762		depth1_provname[0] = '\0';
763		depth1_localpath[0] = '\0';
764		depth1_metaflush = -1;
765		hadmynode = false;
766
767		curres = calloc(1, sizeof(*curres));
768		if (curres == NULL) {
769			pjdlog_error("Unable to allocate memory for resource.");
770			free($1);
771			return (1);
772		}
773		if (strlcpy(curres->hr_name, $1,
774		    sizeof(curres->hr_name)) >=
775		    sizeof(curres->hr_name)) {
776			pjdlog_error("Resource name is too long.");
777			free($1);
778			return (1);
779		}
780		free($1);
781		curres->hr_role = HAST_ROLE_INIT;
782		curres->hr_previous_role = HAST_ROLE_INIT;
783		curres->hr_replication = -1;
784		curres->hr_checksum = -1;
785		curres->hr_compression = -1;
786		curres->hr_timeout = -1;
787		curres->hr_exec[0] = '\0';
788		curres->hr_provname[0] = '\0';
789		curres->hr_localpath[0] = '\0';
790		curres->hr_localfd = -1;
791		curres->hr_metaflush = -1;
792		curres->hr_remoteaddr[0] = '\0';
793		curres->hr_sourceaddr[0] = '\0';
794		curres->hr_ggateunit = -1;
795	}
796	;
797
798resource_entries:
799	|
800	resource_entries resource_entry
801	;
802
803resource_entry:
804	replication_statement
805	|
806	checksum_statement
807	|
808	compression_statement
809	|
810	timeout_statement
811	|
812	exec_statement
813	|
814	metaflush_statement
815	|
816	name_statement
817	|
818	local_statement
819	|
820	resource_node_statement
821	;
822
823name_statement:		NAME STR
824	{
825		switch (depth) {
826		case 1:
827			if (strlcpy(depth1_provname, $2,
828			    sizeof(depth1_provname)) >=
829			    sizeof(depth1_provname)) {
830				pjdlog_error("name argument is too long.");
831				free($2);
832				return (1);
833			}
834			break;
835		case 2:
836			if (!mynode)
837				break;
838			PJDLOG_ASSERT(curres != NULL);
839			if (strlcpy(curres->hr_provname, $2,
840			    sizeof(curres->hr_provname)) >=
841			    sizeof(curres->hr_provname)) {
842				pjdlog_error("name argument is too long.");
843				free($2);
844				return (1);
845			}
846			break;
847		default:
848			PJDLOG_ABORT("name at wrong depth level");
849		}
850		free($2);
851	}
852	;
853
854local_statement:	LOCAL STR
855	{
856		switch (depth) {
857		case 1:
858			if (strlcpy(depth1_localpath, $2,
859			    sizeof(depth1_localpath)) >=
860			    sizeof(depth1_localpath)) {
861				pjdlog_error("local argument is too long.");
862				free($2);
863				return (1);
864			}
865			break;
866		case 2:
867			if (!mynode)
868				break;
869			PJDLOG_ASSERT(curres != NULL);
870			if (strlcpy(curres->hr_localpath, $2,
871			    sizeof(curres->hr_localpath)) >=
872			    sizeof(curres->hr_localpath)) {
873				pjdlog_error("local argument is too long.");
874				free($2);
875				return (1);
876			}
877			break;
878		default:
879			PJDLOG_ABORT("local at wrong depth level");
880		}
881		free($2);
882	}
883	;
884
885resource_node_statement:ON resource_node_start OB resource_node_entries CB
886	{
887		mynode = false;
888	}
889	;
890
891resource_node_start:	STR
892	{
893		if (curres != NULL) {
894			switch (isitme($1)) {
895			case -1:
896				free($1);
897				return (1);
898			case 0:
899				break;
900			case 1:
901				mynode = hadmynode = true;
902				break;
903			default:
904				PJDLOG_ABORT("invalid isitme() return value");
905			}
906		}
907		free($1);
908	}
909	;
910
911resource_node_entries:
912	|
913	resource_node_entries resource_node_entry
914	;
915
916resource_node_entry:
917	name_statement
918	|
919	local_statement
920	|
921	remote_statement
922	|
923	source_statement
924	|
925	metaflush_statement
926	;
927
928remote_statement:	REMOTE remote_str
929	{
930		PJDLOG_ASSERT(depth == 2);
931		if (mynode) {
932			PJDLOG_ASSERT(curres != NULL);
933			if (strlcpy(curres->hr_remoteaddr, $2,
934			    sizeof(curres->hr_remoteaddr)) >=
935			    sizeof(curres->hr_remoteaddr)) {
936				pjdlog_error("remote argument is too long.");
937				free($2);
938				return (1);
939			}
940		}
941		free($2);
942	}
943	;
944
945remote_str:
946	NONE		{ $$ = strdup("none"); }
947	|
948	STR		{ }
949	;
950
951source_statement:	SOURCE STR
952	{
953		PJDLOG_ASSERT(depth == 2);
954		if (mynode) {
955			PJDLOG_ASSERT(curres != NULL);
956			if (strlcpy(curres->hr_sourceaddr, $2,
957			    sizeof(curres->hr_sourceaddr)) >=
958			    sizeof(curres->hr_sourceaddr)) {
959				pjdlog_error("source argument is too long.");
960				free($2);
961				return (1);
962			}
963		}
964		free($2);
965	}
966	;
967