parse.y revision 225784
1153758Swollman%{
2192886Sedwin/*-
3192886Sedwin * Copyright (c) 2009-2010 The FreeBSD Foundation
42744Swollman * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
558782Sru * All rights reserved.
658782Sru *
758782Sru * This software was developed by Pawel Jakub Dawidek under sponsorship from
858782Sru * the FreeBSD Foundation.
943009Swollman *
1043009Swollman * Redistribution and use in source and binary forms, with or without
1143009Swollman * modification, are permitted provided that the following conditions
1258782Sru * are met:
1343009Swollman * 1. Redistributions of source code must retain the above copyright
1443009Swollman *    notice, this list of conditions and the following disclaimer.
1519876Swollman * 2. Redistributions in binary form must reproduce the above copyright
1619876Swollman *    notice, this list of conditions and the following disclaimer in the
1730708Swollman *    documentation and/or other materials provided with the distribution.
1819876Swollman *
1975264Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
202744Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2120091Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2220091Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2320091Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2420091Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25169808Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26158417Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27158417Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2820091Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2920091Swollman * SUCH DAMAGE.
30169808Swollman *
31169808Swollman * $FreeBSD: head/sbin/hastd/parse.y 225784 2011-09-27 08:04:01Z pjd $
32169808Swollman */
33169808Swollman
34169808Swollman#include <sys/param.h>	/* MAXHOSTNAMELEN */
35169808Swollman#include <sys/queue.h>
36169808Swollman#include <sys/socket.h>
37169808Swollman#include <sys/sysctl.h>
38169808Swollman
39169808Swollman#include <arpa/inet.h>
40169808Swollman
41169808Swollman#include <err.h>
42169808Swollman#include <errno.h>
43169808Swollman#include <stdio.h>
44233445Sedwin#include <string.h>
45233445Sedwin#include <sysexits.h>
46233445Sedwin#include <unistd.h>
47233445Sedwin
48233445Sedwin#include <pjdlog.h>
49233445Sedwin
50233445Sedwin#include "hast.h"
51233445Sedwin
52233445Sedwinextern int depth;
53248307Sedwinextern int lineno;
54248307Sedwin
552744Swollmanextern FILE *yyin;
5658782Sruextern char *yytext;
5758782Sru
5858782Srustatic struct hastd_config *lconfig;
5958782Srustatic struct hast_resource *curres;
6058782Srustatic bool mynode, hadmynode;
6158782Sru
6258782Srustatic char depth0_control[HAST_ADDRSIZE];
6358782Srustatic char depth0_listen_tcp4[HAST_ADDRSIZE];
642744Swollmanstatic char depth0_listen_tcp6[HAST_ADDRSIZE];
6519876Swollmanstatic TAILQ_HEAD(, hastd_listen) depth0_listen;
6630708Swollmanstatic int depth0_replication;
6758782Srustatic int depth0_checksum;
68153667Swollmanstatic int depth0_compression;
6958782Srustatic int depth0_timeout;
7058782Srustatic char depth0_exec[PATH_MAX];
7158782Sru
7219876Swollmanstatic char depth1_provname[PATH_MAX];
7319876Swollmanstatic char depth1_localpath[PATH_MAX];
7458782Sru
7558782Sruextern void yyrestart(FILE *);
7658782Sru
7758782Srustatic int
78199107Sedwinisitme(const char *name)
79199107Sedwin{
80199107Sedwin	char buf[MAXHOSTNAMELEN];
81199107Sedwin	char *pos;
82199107Sedwin	size_t bufsize;
83199107Sedwin
84199107Sedwin	/*
85199107Sedwin	 * First check if the give name matches our full hostname.
86199107Sedwin	 */
87199107Sedwin	if (gethostname(buf, sizeof(buf)) < 0) {
88199107Sedwin		pjdlog_errno(LOG_ERR, "gethostname() failed");
89199107Sedwin		return (-1);
90199107Sedwin	}
91199107Sedwin	if (strcmp(buf, name) == 0)
92199107Sedwin		return (1);
93199107Sedwin
94199107Sedwin	/*
95199107Sedwin	 * Now check if it matches first part of the host name.
96205475Sedwin	 */
97249692Sedwin	pos = strchr(buf, '.');
98205475Sedwin	if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
99205475Sedwin	    strncmp(buf, name, pos - buf) == 0) {
100205475Sedwin		return (1);
101205475Sedwin	}
102205475Sedwin
103205475Sedwin	/*
104205475Sedwin	 * At the end check if name is equal to our host's UUID.
105205475Sedwin	 */
106205475Sedwin	bufsize = sizeof(buf);
107205475Sedwin	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
108205475Sedwin		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
109205475Sedwin		return (-1);
110205475Sedwin	}
111205475Sedwin	if (strcasecmp(buf, name) == 0)
112205475Sedwin		return (1);
11319876Swollman
11475264Swollman	/*
115199107Sedwin	 * Looks like this isn't about us.
116199107Sedwin	 */
117205475Sedwin	return (0);
118205475Sedwin}
119233445Sedwin
120233445Sedwinstatic bool
121205475Sedwinfamily_supported(int family)
12275264Swollman{
12343009Swollman	int sock;
12475264Swollman
125199336Sedwin	sock = socket(family, SOCK_STREAM, 0);
126205475Sedwin	if (sock == -1 && errno == EPROTONOSUPPORT)
127233445Sedwin		return (false);
128233445Sedwin	if (sock >= 0)
129205475Sedwin		(void)close(sock);
13075264Swollman	return (true);
131199107Sedwin}
132199107Sedwin
133199107Sedwinstatic int
13419876Swollmannode_names(char **namesp)
13543009Swollman{
13643009Swollman	static char names[MAXHOSTNAMELEN * 3];
13743009Swollman	char buf[MAXHOSTNAMELEN];
13843009Swollman	char *pos;
13943009Swollman	size_t bufsize;
14043009Swollman
14143009Swollman	if (gethostname(buf, sizeof(buf)) < 0) {
14243009Swollman		pjdlog_errno(LOG_ERR, "gethostname() failed");
14343009Swollman		return (-1);
14414338Swollman	}
14519876Swollman
146149511Swollman	/* First component of the host name. */
14714338Swollman	pos = strchr(buf, '.');
14886218Swollman	if (pos != NULL && pos != buf) {
14958782Sru		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
150149511Swollman		    sizeof(names)));
151149511Swollman		(void)strlcat(names, ", ", sizeof(names));
152149511Swollman	}
153149511Swollman
15486218Swollman	/* Full host name. */
15514338Swollman	(void)strlcat(names, buf, sizeof(names));
15619876Swollman	(void)strlcat(names, ", ", sizeof(names));
157149511Swollman
158149511Swollman	/* Host UUID. */
15919876Swollman	bufsize = sizeof(buf);
16030708Swollman	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
16119876Swollman		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
162153667Swollman		return (-1);
16330708Swollman	}
16430708Swollman	(void)strlcat(names, buf, sizeof(names));
16530708Swollman
16630708Swollman	*namesp = names;
16730708Swollman
16830708Swollman	return (0);
16919876Swollman}
17019876Swollman
17130708Swollmanvoid
17230708Swollmanyyerror(const char *str)
17330708Swollman{
17458782Sru
17530708Swollman	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
17658782Sru	    lineno, yytext, str);
17758782Sru}
17830708Swollman
17930708Swollmanstruct hastd_config *
18075264Swollmanyy_config_parse(const char *config, bool exitonerror)
18130708Swollman{
18230708Swollman	int ret;
18330708Swollman
18458782Sru	curres = NULL;
18530708Swollman	mynode = false;
18630708Swollman	depth = 0;
18730708Swollman	lineno = 0;
18830708Swollman
18930708Swollman	depth0_timeout = HAST_TIMEOUT;
19075264Swollman	depth0_replication = HAST_REPLICATION_FULLSYNC;
19130708Swollman	depth0_checksum = HAST_CHECKSUM_NONE;
19275264Swollman	depth0_compression = HAST_COMPRESSION_HOLE;
19330708Swollman	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
19430708Swollman	TAILQ_INIT(&depth0_listen);
195153667Swollman	strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
196153667Swollman	    sizeof(depth0_listen_tcp4));
19743009Swollman	strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
19819876Swollman	    sizeof(depth0_listen_tcp6));
19919876Swollman	depth0_exec[0] = '\0';
200149511Swollman
20119876Swollman	lconfig = calloc(1, sizeof(*lconfig));
20219876Swollman	if (lconfig == NULL) {
203149511Swollman		pjdlog_error("Unable to allocate memory for configuration.");
20419876Swollman		if (exitonerror)
20519876Swollman			exit(EX_TEMPFAIL);
206149511Swollman		return (NULL);
207149511Swollman	}
20858782Sru
20958782Sru	TAILQ_INIT(&lconfig->hc_listen);
210153667Swollman	TAILQ_INIT(&lconfig->hc_resources);
21158782Sru
21258782Sru	yyin = fopen(config, "r");
21358782Sru	if (yyin == NULL) {
21458782Sru		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
21558782Sru		    config);
21675264Swollman		yy_config_free(lconfig);
21758782Sru		if (exitonerror)
21858782Sru			exit(EX_OSFILE);
21958782Sru		return (NULL);
22058782Sru	}
22158782Sru	yyrestart(yyin);
22219876Swollman	ret = yyparse();
22319876Swollman	fclose(yyin);
224149511Swollman	if (ret != 0) {
22519876Swollman		yy_config_free(lconfig);
22619876Swollman		if (exitonerror)
22730708Swollman			exit(EX_CONFIG);
22830708Swollman		return (NULL);
22919876Swollman	}
23019876Swollman
23119876Swollman	/*
23219876Swollman	 * Let's see if everything is set up.
23319876Swollman	 */
23419876Swollman	if (lconfig->hc_controladdr[0] == '\0') {
23519876Swollman		strlcpy(lconfig->hc_controladdr, depth0_control,
23619876Swollman		    sizeof(lconfig->hc_controladdr));
23719876Swollman	}
238171945Sedwin	if (!TAILQ_EMPTY(&depth0_listen))
23919876Swollman		TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
24019876Swollman	if (TAILQ_EMPTY(&lconfig->hc_listen)) {
241171945Sedwin		struct hastd_listen *lst;
242171945Sedwin
243171945Sedwin		if (family_supported(AF_INET)) {
24419876Swollman			lst = calloc(1, sizeof(*lst));
24519876Swollman			if (lst == NULL) {
24630708Swollman				pjdlog_error("Unable to allocate memory for listen address.");
24719876Swollman				yy_config_free(lconfig);
24819876Swollman				if (exitonerror)
24930708Swollman					exit(EX_TEMPFAIL);
25019876Swollman				return (NULL);
25119876Swollman			}
25258782Sru			(void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
25319876Swollman			    sizeof(lst->hl_addr));
25419876Swollman			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
25558782Sru		} else {
25658782Sru			pjdlog_debug(1,
257149511Swollman			    "No IPv4 support in the kernel, not listening on IPv4 address.");
258149511Swollman		}
25975264Swollman		if (family_supported(AF_INET6)) {
26075264Swollman			lst = calloc(1, sizeof(*lst));
26119876Swollman			if (lst == NULL) {
26275264Swollman				pjdlog_error("Unable to allocate memory for listen address.");
26375264Swollman				yy_config_free(lconfig);
26475264Swollman				if (exitonerror)
26575264Swollman					exit(EX_TEMPFAIL);
26675264Swollman				return (NULL);
26775264Swollman			}
26875264Swollman			(void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
26975264Swollman			    sizeof(lst->hl_addr));
27075264Swollman			TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
27175264Swollman		} else {
27275264Swollman			pjdlog_debug(1,
27375264Swollman			    "No IPv6 support in the kernel, not listening on IPv6 address.");
27475264Swollman		}
27575264Swollman		if (TAILQ_EMPTY(&lconfig->hc_listen)) {
27675264Swollman			pjdlog_error("No address to listen on.");
27786218Swollman			yy_config_free(lconfig);
27886218Swollman			if (exitonerror)
27986218Swollman				exit(EX_TEMPFAIL);
28086218Swollman			return (NULL);
28186218Swollman		}
28286218Swollman	}
28386218Swollman	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
28486218Swollman		PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
28586218Swollman		PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
28675264Swollman		PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
28775264Swollman
28875264Swollman		if (curres->hr_replication == -1) {
28919876Swollman			/*
290149511Swollman			 * Replication is not set at resource-level.
291149511Swollman			 * Use global or default setting.
29219876Swollman			 */
29319876Swollman			curres->hr_replication = depth0_replication;
29419876Swollman		}
29519876Swollman		if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
29619876Swollman		    curres->hr_replication == HAST_REPLICATION_ASYNC) {
29730708Swollman			pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
29858782Sru			    curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
29919876Swollman			    "memsync" : "async", "fullsync");
30019876Swollman			curres->hr_replication = HAST_REPLICATION_FULLSYNC;
30119876Swollman		}
30258782Sru		if (curres->hr_checksum == -1) {
30375264Swollman			/*
30475264Swollman			 * Checksum is not set at resource-level.
30575264Swollman			 * Use global or default setting.
30675264Swollman			 */
30775264Swollman			curres->hr_checksum = depth0_checksum;
30875264Swollman		}
30975264Swollman		if (curres->hr_compression == -1) {
31019876Swollman			/*
311114170Swollman			 * Compression is not set at resource-level.
312114170Swollman			 * Use global or default setting.
313114170Swollman			 */
314114170Swollman			curres->hr_compression = depth0_compression;
315114170Swollman		}
316114170Swollman		if (curres->hr_timeout == -1) {
317114170Swollman			/*
31819876Swollman			 * Timeout is not set at resource-level.
31919876Swollman			 * Use global or default setting.
32058782Sru			 */
32119876Swollman			curres->hr_timeout = depth0_timeout;
32219876Swollman		}
32319876Swollman		if (curres->hr_exec[0] == '\0') {
32420091Swollman			/*
32519876Swollman			 * Exec is not set at resource-level.
326149511Swollman			 * Use global or default setting.
32720091Swollman			 */
32820091Swollman			strlcpy(curres->hr_exec, depth0_exec,
32920091Swollman			    sizeof(curres->hr_exec));
33020091Swollman		}
33120091Swollman	}
33220091Swollman
33320091Swollman	return (lconfig);
33475264Swollman}
33520091Swollman
33620091Swollmanvoid
33720091Swollmanyy_config_free(struct hastd_config *config)
33820091Swollman{
33920091Swollman	struct hastd_listen *lst;
34075264Swollman	struct hast_resource *res;
34119876Swollman
34275264Swollman	while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
34319876Swollman		TAILQ_REMOVE(&depth0_listen, lst, hl_next);
34419876Swollman		free(lst);
34575264Swollman	}
34675264Swollman	while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
34719876Swollman		TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
34819876Swollman		free(lst);
34919876Swollman	}
35019876Swollman	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
351153667Swollman		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
35219876Swollman		free(res);
35319876Swollman	}
35419876Swollman	free(config);
35519876Swollman}
35619876Swollman%}
35719876Swollman
35819876Swollman%token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
35943009Swollman%token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
36043009Swollman%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
36143009Swollman%token NUM STR OB CB
36243009Swollman
36319876Swollman%type <str> remote_str
36419876Swollman%type <num> replication_type
36519876Swollman%type <num> checksum_type
36619876Swollman%type <num> compression_type
36719876Swollman
36819876Swollman%union
36919876Swollman{
37019876Swollman	int num;
37119876Swollman	char *str;
372}
373
374%token <num> NUM
375%token <str> STR
376
377%%
378
379statements:
380	|
381	statements statement
382	;
383
384statement:
385	control_statement
386	|
387	listen_statement
388	|
389	replication_statement
390	|
391	checksum_statement
392	|
393	compression_statement
394	|
395	timeout_statement
396	|
397	exec_statement
398	|
399	node_statement
400	|
401	resource_statement
402	;
403
404control_statement:	CONTROL STR
405	{
406		switch (depth) {
407		case 0:
408			if (strlcpy(depth0_control, $2,
409			    sizeof(depth0_control)) >=
410			    sizeof(depth0_control)) {
411				pjdlog_error("control argument is too long.");
412				free($2);
413				return (1);
414			}
415			break;
416		case 1:
417			if (!mynode)
418				break;
419			if (strlcpy(lconfig->hc_controladdr, $2,
420			    sizeof(lconfig->hc_controladdr)) >=
421			    sizeof(lconfig->hc_controladdr)) {
422				pjdlog_error("control argument is too long.");
423				free($2);
424				return (1);
425			}
426			break;
427		default:
428			PJDLOG_ABORT("control at wrong depth level");
429		}
430		free($2);
431	}
432	;
433
434listen_statement:	LISTEN STR
435	{
436		struct hastd_listen *lst;
437
438		lst = calloc(1, sizeof(*lst));
439		if (lst == NULL) {
440			pjdlog_error("Unable to allocate memory for listen address.");
441			free($2);
442			return (1);
443		}
444		if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
445		    sizeof(lst->hl_addr)) {
446			pjdlog_error("listen argument is too long.");
447			free($2);
448			free(lst);
449			return (1);
450		}
451		switch (depth) {
452		case 0:
453			TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
454			break;
455		case 1:
456			if (mynode)
457				TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
458			else
459				free(lst);
460			break;
461		default:
462			PJDLOG_ABORT("listen at wrong depth level");
463		}
464		free($2);
465	}
466	;
467
468replication_statement:	REPLICATION replication_type
469	{
470		switch (depth) {
471		case 0:
472			depth0_replication = $2;
473			break;
474		case 1:
475			PJDLOG_ASSERT(curres != NULL);
476			curres->hr_replication = $2;
477			break;
478		default:
479			PJDLOG_ABORT("replication at wrong depth level");
480		}
481	}
482	;
483
484replication_type:
485	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
486	|
487	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
488	|
489	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
490	;
491
492checksum_statement:	CHECKSUM checksum_type
493	{
494		switch (depth) {
495		case 0:
496			depth0_checksum = $2;
497			break;
498		case 1:
499			PJDLOG_ASSERT(curres != NULL);
500			curres->hr_checksum = $2;
501			break;
502		default:
503			PJDLOG_ABORT("checksum at wrong depth level");
504		}
505	}
506	;
507
508checksum_type:
509	NONE		{ $$ = HAST_CHECKSUM_NONE; }
510	|
511	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
512	|
513	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
514	;
515
516compression_statement:	COMPRESSION compression_type
517	{
518		switch (depth) {
519		case 0:
520			depth0_compression = $2;
521			break;
522		case 1:
523			PJDLOG_ASSERT(curres != NULL);
524			curres->hr_compression = $2;
525			break;
526		default:
527			PJDLOG_ABORT("compression at wrong depth level");
528		}
529	}
530	;
531
532compression_type:
533	NONE		{ $$ = HAST_COMPRESSION_NONE; }
534	|
535	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
536	|
537	LZF		{ $$ = HAST_COMPRESSION_LZF; }
538	;
539
540timeout_statement:	TIMEOUT NUM
541	{
542		if ($2 <= 0) {
543			pjdlog_error("Negative or zero timeout.");
544			return (1);
545		}
546		switch (depth) {
547		case 0:
548			depth0_timeout = $2;
549			break;
550		case 1:
551			PJDLOG_ASSERT(curres != NULL);
552			curres->hr_timeout = $2;
553			break;
554		default:
555			PJDLOG_ABORT("timeout at wrong depth level");
556		}
557	}
558	;
559
560exec_statement:		EXEC STR
561	{
562		switch (depth) {
563		case 0:
564			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
565			    sizeof(depth0_exec)) {
566				pjdlog_error("Exec path is too long.");
567				free($2);
568				return (1);
569			}
570			break;
571		case 1:
572			PJDLOG_ASSERT(curres != NULL);
573			if (strlcpy(curres->hr_exec, $2,
574			    sizeof(curres->hr_exec)) >=
575			    sizeof(curres->hr_exec)) {
576				pjdlog_error("Exec path is too long.");
577				free($2);
578				return (1);
579			}
580			break;
581		default:
582			PJDLOG_ABORT("exec at wrong depth level");
583		}
584		free($2);
585	}
586	;
587
588node_statement:		ON node_start OB node_entries CB
589	{
590		mynode = false;
591	}
592	;
593
594node_start:	STR
595	{
596		switch (isitme($1)) {
597		case -1:
598			free($1);
599			return (1);
600		case 0:
601			break;
602		case 1:
603			mynode = true;
604			break;
605		default:
606			PJDLOG_ABORT("invalid isitme() return value");
607		}
608		free($1);
609	}
610	;
611
612node_entries:
613	|
614	node_entries node_entry
615	;
616
617node_entry:
618	control_statement
619	|
620	listen_statement
621	;
622
623resource_statement:	RESOURCE resource_start OB resource_entries CB
624	{
625		if (curres != NULL) {
626			/*
627			 * There must be section for this node, at least with
628			 * remote address configuration.
629			 */
630			if (!hadmynode) {
631				char *names;
632
633				if (node_names(&names) != 0)
634					return (1);
635				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
636				    curres->hr_name, names);
637				return (1);
638			}
639
640			/*
641			 * Let's see if there are some resource-level settings
642			 * that we can use for node-level settings.
643			 */
644			if (curres->hr_provname[0] == '\0' &&
645			    depth1_provname[0] != '\0') {
646				/*
647				 * Provider name is not set at node-level,
648				 * but is set at resource-level, use it.
649				 */
650				strlcpy(curres->hr_provname, depth1_provname,
651				    sizeof(curres->hr_provname));
652			}
653			if (curres->hr_localpath[0] == '\0' &&
654			    depth1_localpath[0] != '\0') {
655				/*
656				 * Path to local provider is not set at
657				 * node-level, but is set at resource-level,
658				 * use it.
659				 */
660				strlcpy(curres->hr_localpath, depth1_localpath,
661				    sizeof(curres->hr_localpath));
662			}
663
664			/*
665			 * If provider name is not given, use resource name
666			 * as provider name.
667			 */
668			if (curres->hr_provname[0] == '\0') {
669				strlcpy(curres->hr_provname, curres->hr_name,
670				    sizeof(curres->hr_provname));
671			}
672
673			/*
674			 * Remote address has to be configured at this point.
675			 */
676			if (curres->hr_remoteaddr[0] == '\0') {
677				pjdlog_error("Remote address not configured for resource %s.",
678				    curres->hr_name);
679				return (1);
680			}
681			/*
682			 * Path to local provider has to be configured at this
683			 * point.
684			 */
685			if (curres->hr_localpath[0] == '\0') {
686				pjdlog_error("Path to local component not configured for resource %s.",
687				    curres->hr_name);
688				return (1);
689			}
690
691			/* Put it onto resource list. */
692			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
693			curres = NULL;
694		}
695	}
696	;
697
698resource_start:	STR
699	{
700		/* Check if there is no duplicate entry. */
701		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
702			if (strcmp(curres->hr_name, $1) == 0) {
703				pjdlog_error("Resource %s configured more than once.",
704				    curres->hr_name);
705				free($1);
706				return (1);
707			}
708		}
709
710		/*
711		 * Clear those, so we can tell if they were set at
712		 * resource-level or not.
713		 */
714		depth1_provname[0] = '\0';
715		depth1_localpath[0] = '\0';
716		hadmynode = false;
717
718		curres = calloc(1, sizeof(*curres));
719		if (curres == NULL) {
720			pjdlog_error("Unable to allocate memory for resource.");
721			free($1);
722			return (1);
723		}
724		if (strlcpy(curres->hr_name, $1,
725		    sizeof(curres->hr_name)) >=
726		    sizeof(curres->hr_name)) {
727			pjdlog_error("Resource name is too long.");
728			free($1);
729			return (1);
730		}
731		free($1);
732		curres->hr_role = HAST_ROLE_INIT;
733		curres->hr_previous_role = HAST_ROLE_INIT;
734		curres->hr_replication = -1;
735		curres->hr_checksum = -1;
736		curres->hr_compression = -1;
737		curres->hr_timeout = -1;
738		curres->hr_exec[0] = '\0';
739		curres->hr_provname[0] = '\0';
740		curres->hr_localpath[0] = '\0';
741		curres->hr_localfd = -1;
742		curres->hr_remoteaddr[0] = '\0';
743		curres->hr_sourceaddr[0] = '\0';
744		curres->hr_ggateunit = -1;
745	}
746	;
747
748resource_entries:
749	|
750	resource_entries resource_entry
751	;
752
753resource_entry:
754	replication_statement
755	|
756	checksum_statement
757	|
758	compression_statement
759	|
760	timeout_statement
761	|
762	exec_statement
763	|
764	name_statement
765	|
766	local_statement
767	|
768	resource_node_statement
769	;
770
771name_statement:		NAME STR
772	{
773		switch (depth) {
774		case 1:
775			if (strlcpy(depth1_provname, $2,
776			    sizeof(depth1_provname)) >=
777			    sizeof(depth1_provname)) {
778				pjdlog_error("name argument is too long.");
779				free($2);
780				return (1);
781			}
782			break;
783		case 2:
784			if (!mynode)
785				break;
786			PJDLOG_ASSERT(curres != NULL);
787			if (strlcpy(curres->hr_provname, $2,
788			    sizeof(curres->hr_provname)) >=
789			    sizeof(curres->hr_provname)) {
790				pjdlog_error("name argument is too long.");
791				free($2);
792				return (1);
793			}
794			break;
795		default:
796			PJDLOG_ABORT("name at wrong depth level");
797		}
798		free($2);
799	}
800	;
801
802local_statement:	LOCAL STR
803	{
804		switch (depth) {
805		case 1:
806			if (strlcpy(depth1_localpath, $2,
807			    sizeof(depth1_localpath)) >=
808			    sizeof(depth1_localpath)) {
809				pjdlog_error("local argument is too long.");
810				free($2);
811				return (1);
812			}
813			break;
814		case 2:
815			if (!mynode)
816				break;
817			PJDLOG_ASSERT(curres != NULL);
818			if (strlcpy(curres->hr_localpath, $2,
819			    sizeof(curres->hr_localpath)) >=
820			    sizeof(curres->hr_localpath)) {
821				pjdlog_error("local argument is too long.");
822				free($2);
823				return (1);
824			}
825			break;
826		default:
827			PJDLOG_ABORT("local at wrong depth level");
828		}
829		free($2);
830	}
831	;
832
833resource_node_statement:ON resource_node_start OB resource_node_entries CB
834	{
835		mynode = false;
836	}
837	;
838
839resource_node_start:	STR
840	{
841		if (curres != NULL) {
842			switch (isitme($1)) {
843			case -1:
844				free($1);
845				return (1);
846			case 0:
847				break;
848			case 1:
849				mynode = hadmynode = true;
850				break;
851			default:
852				PJDLOG_ABORT("invalid isitme() return value");
853			}
854		}
855		free($1);
856	}
857	;
858
859resource_node_entries:
860	|
861	resource_node_entries resource_node_entry
862	;
863
864resource_node_entry:
865	name_statement
866	|
867	local_statement
868	|
869	remote_statement
870	|
871	source_statement
872	;
873
874remote_statement:	REMOTE remote_str
875	{
876		PJDLOG_ASSERT(depth == 2);
877		if (mynode) {
878			PJDLOG_ASSERT(curres != NULL);
879			if (strlcpy(curres->hr_remoteaddr, $2,
880			    sizeof(curres->hr_remoteaddr)) >=
881			    sizeof(curres->hr_remoteaddr)) {
882				pjdlog_error("remote argument is too long.");
883				free($2);
884				return (1);
885			}
886		}
887		free($2);
888	}
889	;
890
891remote_str:
892	NONE		{ $$ = strdup("none"); }
893	|
894	STR		{ }
895	;
896
897source_statement:	SOURCE STR
898	{
899		PJDLOG_ASSERT(depth == 2);
900		if (mynode) {
901			PJDLOG_ASSERT(curres != NULL);
902			if (strlcpy(curres->hr_sourceaddr, $2,
903			    sizeof(curres->hr_sourceaddr)) >=
904			    sizeof(curres->hr_sourceaddr)) {
905				pjdlog_error("source argument is too long.");
906				free($2);
907				return (1);
908			}
909		}
910		free($2);
911	}
912	;
913