1 %{
2 /*-
3  * Copyright (c) 2009-2010 The FreeBSD Foundation
4  * Copyright (c) 2010 Mikolaj Golub <to.my.trociny@gmail.com>
5  * All rights reserved.
6  *
7  * This software was developed by Mikolaj Golub. The source is derived
8  * from HAST developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $FreeBSD$
33  */
34 
35 #include <sys/param.h>	/* MAXHOSTNAMELEN */
36 #ifdef HAVE_DEFINE_TAILQ_FOREACH_SAFE_SYS_QUEUE_H
37 #include <sys/queue.h>
38 #else
39 #include "queue.h"
40 #endif
41 #include <sys/sysctl.h>
42 
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 
46 #include <err.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <sysexits.h>
50 #include <unistd.h>
51 
52 #include <pjdlog.h>
53 
54 #include "auth.h"
55 #include "hast.h"
56 
57 extern int depth;
58 extern int lineno;
59 
60 extern FILE *yyin;
61 extern char *yytext;
62 
63 static struct hastmon_config *lconfig;
64 static struct hast_resource *curres;
65 static bool mynode, hadmynode;
66 
67 static char depth0_control[HAST_ADDRSIZE];
68 static char depth0_listen[HAST_ADDRSIZE];
69 static char depth0_exec[PATH_MAX];
70 static int depth0_timeout;
71 static int depth0_attempts;
72 static int depth0_heartbeat_interval;
73 static int depth0_complaint_count;
74 static int depth0_complaint_interval;
75 static int depth0_role_on_start;
76 static struct hast_auth depth0_key;
77 
78 extern void yyrestart(FILE *);
79 
80 static int
isitme(const char * name)81 isitme(const char *name)
82 {
83 	char buf[MAXHOSTNAMELEN];
84 	char *pos;
85 #ifdef _KERN_HOSTUUID
86 	size_t bufsize;
87 #endif
88 
89 	/*
90 	 * First check if the give name matches our full hostname.
91 	 */
92 	if (gethostname(buf, sizeof(buf)) < 0) {
93 		pjdlog_errno(LOG_ERR, "gethostname() failed");
94 		return (-1);
95 	}
96  	if (strcmp(buf, name) == 0)
97 		return (1);
98 
99 	/*
100 	 * Now check if it matches first part of the host name.
101 	 */
102 	pos = strchr(buf, '.');
103 	if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
104 		return (1);
105 
106 #ifdef _KERN_HOSTUUID
107 	/*
108 	 * At the end check if name is equal to our host's UUID.
109 	 */
110 	bufsize = sizeof(buf);
111 	if ((sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) == 0) &&
112 	    (strcasecmp(buf, name) == 0))
113 		return (1);
114 #endif
115 
116 	/*
117 	 * Looks like this isn't about us.
118 	 */
119 	return (0);
120 }
121 
122 static int
node_names(char ** namesp)123 node_names(char **namesp)
124 {
125 	static char names[MAXHOSTNAMELEN * 3];
126 	char buf[MAXHOSTNAMELEN];
127 	char *pos;
128 	size_t bufsize;
129 
130 	if (gethostname(buf, sizeof(buf)) < 0) {
131 		pjdlog_errno(LOG_ERR, "gethostname() failed");
132 		return (-1);
133 	}
134 
135 	/* First component of the host name. */
136 	pos = strchr(buf, '.');
137 	if (pos != NULL && pos != buf) {
138 		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
139 		    sizeof(names)));
140 		(void)strlcat(names, ", ", sizeof(names));
141 	}
142 
143 	/* Full host name. */
144 	(void)strlcat(names, buf, sizeof(names));
145 
146 #ifdef _KERN_HOSTUUID
147 	/* Host UUID. */
148 	bufsize = sizeof(buf);
149 	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) == 0) {
150 		(void)strlcat(names, ", ", sizeof(names));
151 		(void)strlcat(names, buf, sizeof(names));
152 	}
153 #endif
154 
155 	*namesp = names;
156 
157 	return (0);
158 }
159 
160 void
yyerror(const char * str)161 yyerror(const char *str)
162 {
163 
164  	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
165 	    lineno, yytext, str);
166 }
167 
168 struct hastmon_config *
yy_config_parse(const char * config,bool exitonerror)169 yy_config_parse(const char *config, bool exitonerror)
170 {
171 	int ret;
172 
173 	curres = NULL;
174 	mynode = false;
175 	depth = 0;
176 	lineno = 0;
177 
178 	depth0_timeout = HAST_TIMEOUT;
179 	depth0_heartbeat_interval = HAST_HBEAT_INT;
180 	depth0_complaint_count = HAST_CMPLNT_CNT;
181 	depth0_complaint_interval = HAST_CMPLNT_INT;
182 	depth0_role_on_start = HAST_ROLE_INIT;
183 	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
184 	strlcpy(depth0_listen, HAST_LISTEN, sizeof(depth0_listen));
185 	depth0_exec[0] = '\0';
186 	depth0_key.au_algo = HAST_AUTH_UNDEF;
187 	depth0_key.au_secret[0] = '\0';
188 
189 	lconfig = calloc(1, sizeof(*lconfig));
190 	if (lconfig == NULL) {
191 		pjdlog_error("Unable to allocate memory for configuration.");
192 		if (exitonerror)
193 			exit(EX_TEMPFAIL);
194 		return (NULL);
195 	}
196 	TAILQ_INIT(&lconfig->hc_friends);
197 	TAILQ_INIT(&lconfig->hc_resources);
198 
199 	yyin = fopen(config, "r");
200 	if (yyin == NULL) {
201 		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
202 		    config);
203 		yy_config_free(lconfig);
204 		if (exitonerror)
205 			exit(EX_OSFILE);
206 		return (NULL);
207 	}
208 	yyrestart(yyin);
209 
210 	ret = yyparse();
211 	fclose(yyin);
212 	if (ret != 0) {
213 		yy_config_free(lconfig);
214 		if (exitonerror)
215 			exit(EX_CONFIG);
216 		return (NULL);
217 	}
218 
219 	/*
220 	 * Let's see if everything is set up.
221 	 */
222 	if (lconfig->hc_controladdr[0] == '\0') {
223 		strlcpy(lconfig->hc_controladdr, depth0_control,
224 		    sizeof(lconfig->hc_controladdr));
225 	}
226 	if (lconfig->hc_listenaddr[0] == '\0') {
227 		strlcpy(lconfig->hc_listenaddr, depth0_listen,
228 		    sizeof(lconfig->hc_listenaddr));
229 	}
230 	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
231 		PJDLOG_ASSERT(TAILQ_FIRST(&curres->hr_remote) != NULL);
232 
233 		if (curres->hr_timeout == -1) {
234 			/*
235 			 * Timeout is not set at resource-level.
236 			 * Use global or default setting.
237 			 */
238 			curres->hr_timeout = depth0_timeout;
239 		}
240 		if (curres->hr_heartbeat_interval == -1) {
241 			/*
242 			 * Heartbeat interval is not set at resource-level.
243 			 * Use global or default setting.
244 			 */
245 			curres->hr_heartbeat_interval = depth0_heartbeat_interval;
246 		}
247 		if (curres->hr_key.au_algo == HAST_AUTH_UNDEF) {
248 			/*
249 			 * Key is not set at resource-level.
250 			 * Use global setting.
251 			 */
252 			curres->hr_key = depth0_key;
253 		}
254 		if (curres->hr_complaint_critical_cnt == -1) {
255 			/*
256 			 * Complaint count is not set at resource-level.
257 			 * Use global or default setting.
258 			 */
259 			curres->hr_complaint_critical_cnt = depth0_complaint_count;
260 		}
261 		if (curres->hr_complaint_interval == -1) {
262 			/*
263 			 * Complaint interval is not set at resource-level.
264 			 * Use global or default setting.
265 			 */
266 			curres->hr_complaint_interval = depth0_complaint_interval;
267 		}
268 		if (curres->hr_role_on_start == -1) {
269 			/*
270 			 * Role on start is not set at resource-level.
271 			 * Use global or default setting.
272 			 */
273 			curres->hr_role_on_start = depth0_role_on_start;
274 		}
275 		if (curres->hr_exec[0] == '\0') {
276 			/*
277 			 * Exec is not set at resource-level.
278 			 * Use global or default setting.
279 			 */
280 			strlcpy(curres->hr_exec, depth0_exec,
281 			    sizeof(curres->hr_exec));
282 		}
283 	}
284 
285 	return (lconfig);
286 }
287 
288 void
yy_resource_free(struct hast_resource * res)289 yy_resource_free(struct hast_resource *res)
290 {
291 	struct hast_address *addr;
292 	struct hast_remote *remote;
293 
294 	while ((addr = TAILQ_FIRST(&res->hr_friends)) != NULL) {
295 		TAILQ_REMOVE(&res->hr_friends, addr, a_next);
296 		free(addr);
297 	}
298 	while ((remote = TAILQ_FIRST(&res->hr_remote)) != NULL) {
299 		TAILQ_REMOVE(&res->hr_remote, remote, r_next);
300 		free(remote);
301 	}
302 	free(res);
303 }
304 
305 void
yy_config_free(struct hastmon_config * config)306 yy_config_free(struct hastmon_config *config)
307 {
308 	struct hast_resource *res;
309 	struct hast_address *addr;
310 
311 	while ((addr = TAILQ_FIRST(&config->hc_friends)) != NULL) {
312 		TAILQ_REMOVE(&config->hc_friends, addr, a_next);
313 		free(addr);
314 	}
315 	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
316 		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
317 		yy_resource_free(res);
318 	}
319 	free(config);
320 }
321 %}
322 
323 %token ALGORITHM ATTEMPTS CB COMPLAINT_COUNT COMPLAINT_INTERVAL CONTROL EXEC
324 %token FRIENDS HEARTBEAT_INTERVAL KEY LISTEN NUM ON OB PORT PRIORITY
325 %token REMOTE RESOURCE ROLE_ON_START SECRET STR TIMEOUT
326 %token INIT PRIMARY SECONDARY WATCHDOG
327 
328 %type <num> role
329 
330 %union
331 {
332 	int num;
333 	char *str;
334 }
335 
336 %token <num> NUM
337 %token <str> STR
338 
339 %%
340 
341 statements:
342 	|
343 	statements statement
344 	;
345 
346 statement:
347 	control_statement
348 	|
349 	listen_statement
350 	|
351 	timeout_statement
352 	|
353 	attempts_statement
354 	|
355 	friends_statement
356 	|
357 	heartbeat_interval_statement
358 	|
359 	key_statement
360 	|
361 	complaint_count_statement
362 	|
363 	complaint_interval_statement
364 	|
365 	role_on_start_statement
366 	|
367 	exec_statement
368 	|
369 	node_statement
370 	|
371 	resource_statement
372 	;
373 
374 control_statement:	CONTROL STR
375 	{
376 		switch (depth) {
377 		case 0:
378 			if (strlcpy(depth0_control, $2,
379 			    sizeof(depth0_control)) >=
380 			    sizeof(depth0_control)) {
381 				pjdlog_error("control argument is too long.");
382 				free($2);
383 				return (1);
384 			}
385 			break;
386 		case 1:
387 			if (!mynode)
388 				break;
389 			if (strlcpy(lconfig->hc_controladdr, $2,
390 			    sizeof(lconfig->hc_controladdr)) >=
391 			    sizeof(lconfig->hc_controladdr)) {
392 				pjdlog_error("control argument is too long.");
393 				free($2);
394 				return (1);
395  			}
396  			break;
397 		default:
398 			PJDLOG_ABORT("control at wrong depth level");
399 		}
400 		free($2);
401 	}
402 	;
403 
404 listen_statement:	LISTEN STR
405 	{
406 		switch (depth) {
407 		case 0:
408 			if (strlcpy(depth0_listen, $2,
409 			    sizeof(depth0_listen)) >=
410 			    sizeof(depth0_listen)) {
411 				pjdlog_error("listen 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_listenaddr, $2,
420 			    sizeof(lconfig->hc_listenaddr)) >=
421 			    sizeof(lconfig->hc_listenaddr)) {
422 				pjdlog_error("listen argument is too long.");
423 				free($2);
424 				return (1);
425 			}
426 			break;
427 		default:
428 			PJDLOG_ABORT("listen at wrong depth level");
429 		}
430 		free($2);
431 	}
432 	;
433 
434 timeout_statement:	TIMEOUT NUM
435 	{
436 		switch (depth) {
437 		case 0:
438 			depth0_timeout = $2;
439 			break;
440 		case 1:
441 			if (curres != NULL)
442 				curres->hr_timeout = $2;
443 			break;
444 		default:
445 			PJDLOG_ABORT("timeout at wrong depth level");
446 		}
447 	}
448 	;
449 
450 attempts_statement:	ATTEMPTS NUM
451 	{
452 		switch (depth) {
453 		case 0:
454 			depth0_attempts = $2;
455 			break;
456 		case 2:
457 			if (!mynode)
458 				break;
459 			/* FALLTHROUGH */
460 		case 1:
461 			if (curres != NULL)
462 				curres->hr_local_attempts_max = $2;
463 			break;
464 		default:
465 			PJDLOG_ABORT("attempts at wrong depth level");
466 		}
467 	}
468 	;
469 
470 heartbeat_interval_statement:	HEARTBEAT_INTERVAL NUM
471 	{
472 		switch (depth) {
473 		case 0:
474 			depth0_heartbeat_interval = $2;
475 			break;
476 		case 2:
477 			if (!mynode)
478 				break;
479 			/* FALLTHROUGH */
480 		case 1:
481 			if (curres != NULL)
482 				curres->hr_heartbeat_interval = $2;
483 			break;
484 		default:
485 			PJDLOG_ABORT("heartbeat_interval at wrong depth level");
486 		}
487 	}
488 	;
489 
490 complaint_count_statement:	COMPLAINT_COUNT NUM
491 	{
492 		switch (depth) {
493 		case 0:
494 			depth0_complaint_count = $2;
495 			break;
496 		case 2:
497 			if (!mynode)
498 				break;
499 			/* FALLTHROUGH */
500 		case 1:
501 			if (curres != NULL)
502 				curres->hr_complaint_critical_cnt = $2;
503 			break;
504 		default:
505 			PJDLOG_ABORT("complaint_count at wrong depth level");
506 		}
507 	}
508 	;
509 
510 complaint_interval_statement:	COMPLAINT_INTERVAL NUM
511 	{
512 		switch (depth) {
513 		case 0:
514 			depth0_complaint_interval = $2;
515 			break;
516 		case 2:
517 			if (!mynode)
518 				break;
519 			/* FALLTHROUGH */
520 		case 1:
521 			if (curres != NULL)
522 				curres->hr_complaint_interval = $2;
523 			break;
524 		default:
525 			PJDLOG_ABORT("complaint_interval at wrong depth level");
526 		}
527 	}
528 	;
529 
530 role_on_start_statement:	ROLE_ON_START role
531 	{
532 		switch (depth) {
533 		case 0:
534 			depth0_role_on_start = $2;
535 			break;
536 		case 2:
537 			if (!mynode)
538 				break;
539 			/* FALLTHROUGH */
540 		case 1:
541 			if (curres != NULL)
542 				curres->hr_role_on_start = $2;
543 			break;
544 		default:
545 			PJDLOG_ABORT("role_on_start at wrong depth level");
546 		}
547 	}
548 	;
549 
550 role:
551 	INIT		{ $$ = HAST_ROLE_INIT; }
552 	|
553 	PRIMARY		{ $$ = HAST_ROLE_PRIMARY; }
554 	|
555 	SECONDARY	{ $$ = HAST_ROLE_SECONDARY; }
556 	|
557 	WATCHDOG	{ $$ = HAST_ROLE_WATCHDOG; }
558 	;
559 
560 exec_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 2:
572 			if (!mynode)
573 				break;
574 			/* FALLTHROUGH */
575 		case 1:
576 			if (curres == NULL)
577 				break;
578 			if (strlcpy(curres->hr_exec, $2,
579 			    sizeof(curres->hr_exec)) >=
580 			    sizeof(curres->hr_exec)) {
581 				pjdlog_error("Exec path is too long.");
582 				free($2);
583 				return (1);
584 			}
585 			break;
586 		default:
587 			PJDLOG_ABORT("exec at wrong depth level");
588 		}
589 		free($2);
590 	}
591 	;
592 
593 node_statement:		ON node_start OB node_entries CB
594 	{
595 		mynode = false;
596 	}
597 	;
598 
599 node_start:	STR
600 	{
601 		switch (isitme($1)) {
602 		case -1:
603 			free($1);
604 			return (1);
605 		case 0:
606 			break;
607 		case 1:
608 			mynode = true;
609 			break;
610 		default:
611 			PJDLOG_ABORT("invalid isitme() return value");
612 		}
613 		free($1);
614 	}
615 	;
616 
617 node_entries:
618 	|
619 	node_entries node_entry
620 	;
621 
622 node_entry:
623 	control_statement
624 	|
625 	listen_statement
626 	|
627 	attempts_statement
628 	|
629 	friends_statement
630 	|
631 	priority_statement
632 	|
633 	heartbeat_interval_statement
634 	|
635 	complaint_count_statement
636 	|
637 	complaint_interval_statement
638 	|
639 	role_on_start_statement
640 	;
641 
642 key_statement:		KEY OB key_entries CB
643 	;
644 
645 key_entries:
646 	|
647 	key_entries key_entry
648 	;
649 
650 key_entry:
651 	algorithm_statement
652 	|
653 	secret_statement
654 	;
655 
656 algorithm_statement:		ALGORITHM STR
657 	{
658 		switch (depth) {
659 		case 1:
660 			if ((depth0_key.au_algo = str2algo($2)) == HAST_AUTH_UNDEF) {
661 				pjdlog_error("Unknown algorithm: %s.", $2);
662 				free($2);
663 				return (1);
664 			}
665 			break;
666 		case 2:
667 			if (curres == NULL)
668 				break;
669 			if ((curres->hr_key.au_algo = str2algo($2)) == HAST_AUTH_UNDEF) {
670 				pjdlog_error("Unknown algorithm: %s.", $2);
671 				free($2);
672 				return (1);
673 			}
674 			break;
675 		default:
676 			PJDLOG_ABORT("key at wrong depth level");
677 		}
678 		free($2);
679 	}
680 	;
681 
682 secret_statement:		SECRET STR
683 	{
684 		switch (depth) {
685 		case 1:
686 			if (strlcpy(depth0_key.au_secret, $2,
687 				sizeof(depth0_key.au_secret)) >=
688 			    sizeof(depth0_key.au_secret)) {
689 				pjdlog_error("Secret is too long.");
690 				free($2);
691 				return (1);
692 			}
693 			break;
694 		case 2:
695 			if (curres == NULL)
696 				break;
697 			if (strlcpy(curres->hr_key.au_secret, $2,
698 				sizeof(depth0_key.au_secret)) >=
699 			    sizeof(depth0_key.au_secret)) {
700 				pjdlog_error("Secret is too long.");
701 				free($2);
702 				return (1);
703 			}
704 			break;
705 		default:
706 			PJDLOG_ABORT("key at wrong depth level");
707 		}
708 		free($2);
709 	}
710 	;
711 
712 resource_statement:	RESOURCE resource_start OB resource_entries CB
713 	{
714 		if (curres != NULL) {
715  			/*
716 			 * There must be section for this node, at least with
717 			 * remote address configuration.
718 			 */
719 			if (!hadmynode) {
720 				char *names;
721 
722 				if (node_names(&names) != 0)
723 					return (1);
724 				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
725 				    curres->hr_name, names);
726 				return (1);
727 			}
728 
729 			/*
730 			 * Let's see there are some resource-level settings
731 			 * that we can use for node-level settings.
732 			 */
733 
734 			/*
735 			 * Remote address has to be configured at this point.
736 			 */
737 			if (TAILQ_FIRST(&curres->hr_remote) == NULL) {
738 				pjdlog_error("Remote address not configured for resource %s.",
739 				    curres->hr_name);
740 				return (1);
741 			}
742 
743 			/*
744 			 *  Exec has to be configured at this point.
745 			 */
746 			if (curres->hr_exec[0] == '\0') {
747 				pjdlog_error("Exec not configured for resource %s.",
748 				    curres->hr_name);
749 				return (1);
750 			}
751 
752 			/* Put it onto resource list. */
753 			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
754 			curres = NULL;
755 		}
756 	}
757 	;
758 
759 resource_start:	STR
760 	{
761 		/* Check if there is no duplicate entry. */
762 		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
763 			if (strcmp(curres->hr_name, $1) == 0) {
764 				pjdlog_error("Resource %s configured more than once.",
765 				    curres->hr_name);
766 				free($1);
767 				return (1);
768 			}
769 		}
770 
771 		/*
772 		 * Clear those, so we can tell if they were set at
773 		 * resource-level or not.
774 		 */
775 		hadmynode = false;
776 
777 		curres = calloc(1, sizeof(*curres));
778 		if (curres == NULL) {
779 			pjdlog_error("Unable to allocate memory for resource.");
780 			free($1);
781 			return (1);
782 		}
783 		if (strlcpy(curres->hr_name, $1,
784 		    sizeof(curres->hr_name)) >=
785 		    sizeof(curres->hr_name)) {
786 			pjdlog_error("Resource name is too long.");
787 			free($1);
788 			return (1);
789 		}
790 		free($1);
791 		curres->hr_role = HAST_ROLE_INIT;
792 		curres->hr_previous_role = HAST_ROLE_INIT;
793 		curres->hr_role_on_start = -1;
794 		curres->hr_timeout = -1;
795 		curres->hr_priority = 100;
796 		curres->hr_heartbeat_interval = -1;
797 		curres->hr_exec[0] = '\0';
798 		curres->hr_local_attempts_max = HAST_ATTEMPTS;
799 		TAILQ_INIT(&curres->hr_friends);
800 		curres->hr_remote_cnt = 0;
801 		TAILQ_INIT(&curres->hr_remote);
802 		curres->hr_key.au_algo = HAST_AUTH_UNDEF;
803 		curres->hr_key.au_secret[0] = '\0';
804 		curres->hr_complaint_critical_cnt = -1;
805 		curres->hr_complaint_interval = -1;
806 		TAILQ_INIT(&curres->hr_complaints);
807 	}
808 	;
809 
810 resource_entries:
811 	|
812 	resource_entries resource_entry
813 	;
814 
815 resource_entry:
816 	timeout_statement
817 	|
818 	attempts_statement
819 	|
820 	friends_statement
821 	|
822 	heartbeat_interval_statement
823 	|
824 	key_statement
825 	|
826 	complaint_count_statement
827 	|
828 	complaint_interval_statement
829 	|
830 	role_on_start_statement
831 	|
832 	exec_statement
833 	|
834 	resource_node_statement
835 	;
836 
837 resource_node_statement:ON resource_node_start OB resource_node_entries CB
838 	{
839 		mynode = false;
840 	}
841 	;
842 
843 resource_node_start:	STR
844 	{
845 		if (curres != NULL) {
846 			switch (isitme($1)) {
847 			case -1:
848 				free($1);
849 				return (1);
850 			case 0:
851 				break;
852 			case 1:
853 				mynode = hadmynode = true;
854 				break;
855 			default:
856 				PJDLOG_ABORT("invalid isitme() return value");
857 			}
858 			free($1);
859 		}
860 	}
861 	;
862 
863 resource_node_entries:
864 	|
865 	resource_node_entries resource_node_entry
866 	;
867 
868 resource_node_entry:
869 	attempts_statement
870 	|
871 	friends_statement
872 	|
873 	remote_statement
874 	|
875 	priority_statement
876 	|
877 	heartbeat_interval_statement
878 	|
879 	complaint_count_statement
880 	|
881 	complaint_interval_statement
882 	|
883 	exec_statement
884 	|
885 	role_on_start_statement
886 	;
887 
888 remote_statement:	REMOTE remote_addresses
889 	;
890 
891 remote_addresses:
892 	|
893 	remote_addresses remote_address
894 	;
895 
896 remote_address:		STR
897 	{
898 		struct hast_remote *remote;
899 
900 		PJDLOG_ASSERT(depth == 2);
901 		if (mynode) {
902 			PJDLOG_ASSERT(curres != NULL);
903 			remote = calloc(1, sizeof(*remote));
904 			if (remote == NULL) {
905 				errx(EX_TEMPFAIL,
906 				     "cannot allocate memory for resource");
907 			}
908 			if (strlcpy(remote->r_addr, $1,
909 				    sizeof(remote->r_addr)) >=
910 			    sizeof(remote->r_addr)) {
911 				pjdlog_error("remote argument too long");
912 				free($1);
913 				return (1);
914 			}
915 			free($1);
916 			remote->r_res = curres;
917 			remote->r_ncomp = curres->hr_remote_cnt;
918 			TAILQ_INSERT_TAIL(&curres->hr_remote, remote, r_next);
919 			curres->hr_remote_cnt++;
920 		}
921 	}
922 	;
923 
924 friends_statement:	FRIENDS friend_addresses
925 	;
926 
927 friend_addresses:
928 	|
929 	friend_addresses friend_address
930 	;
931 
932 friend_address:		STR
933 	{
934 		struct hast_address *addr;
935 
936 		switch (depth) {
937 		case 0:
938 		case 1:
939 			addr = calloc(1, sizeof(*addr));
940 			if (addr == NULL) {
941 				errx(EX_TEMPFAIL,
942 				     "cannot allocate memory for resource");
943 			}
944 			if (strlcpy(addr->a_addr, $1,
945 				    sizeof(addr->a_addr)) >=
946 			    sizeof(addr->a_addr)) {
947 				pjdlog_error("address argument too long");
948 				free($1);
949 				return (1);
950 			}
951 			free($1);
952 			TAILQ_INSERT_TAIL(&lconfig->hc_friends, addr, a_next);
953 			break;
954 		case 2:
955 			if (mynode) {
956 				PJDLOG_ASSERT(curres != NULL);
957 				addr = calloc(1, sizeof(*addr));
958 				if (addr == NULL) {
959 					errx(EX_TEMPFAIL,
960 					     "cannot allocate memory for resource");
961 				}
962 				if (strlcpy(addr->a_addr, $1,
963 					    sizeof(addr->a_addr)) >=
964 				    sizeof(addr->a_addr)) {
965 					pjdlog_error("address argument too long");
966 					free($1);
967 					return (1);
968 				}
969 				free($1);
970 				TAILQ_INSERT_TAIL(&curres->hr_friends, addr, a_next);
971 			}
972 			break;
973 		default:
974 			PJDLOG_ABORT("friends at wrong depth level");
975 		}
976 	}
977 	;
978 
979 priority_statement:	PRIORITY NUM
980 	{
981 		PJDLOG_ASSERT(depth == 2);
982 		if (mynode) {
983 			curres->hr_priority = $2;
984 			if (curres->hr_priority < 0) {
985 				pjdlog_error("priority should be greater or equal zero");
986 				return (1);
987 			}
988 		}
989 	}
990 	;
991