1%{
2/*-
3 * Copyright (c) 2009-2010 The FreeBSD Foundation
4 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sbin/hastd/parse.y 225784 2011-09-27 08:04:01Z pjd $
32 */
33
34#include <sys/param.h> /* MAXHOSTNAMELEN */
35#include <sys/queue.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38
39#include <arpa/inet.h>
40
41#include <err.h>
42#include <errno.h>
43#include <stdio.h>
44#include <string.h>
45#include <sysexits.h>
46#include <unistd.h>
47
48#include <pjdlog.h>
49
50#include "hast.h"
51
52extern int depth;
53extern int lineno;
54
55extern FILE *yyin;
56extern char *yytext;
57
58static struct hastd_config *lconfig;
59static struct hast_resource *curres;
60static bool mynode, hadmynode;
61
62static char depth0_control[HAST_ADDRSIZE];
63static char depth0_listen_tcp4[HAST_ADDRSIZE];
64static char depth0_listen_tcp6[HAST_ADDRSIZE];
65static TAILQ_HEAD(, hastd_listen) depth0_listen;
66static int depth0_replication;
67static int depth0_checksum;
68static int depth0_compression;
69static int depth0_timeout;
70static char depth0_exec[PATH_MAX];
71
72static char depth1_provname[PATH_MAX];
73static char depth1_localpath[PATH_MAX];
74
75extern void yyrestart(FILE *);
76
77static int
78isitme(const char *name)
79{
80 char buf[MAXHOSTNAMELEN];
81 char *pos;
82 size_t bufsize;
83
84 /*
85 * First check if the give name matches our full hostname.
86 */
87 if (gethostname(buf, sizeof(buf)) < 0) {
88 pjdlog_errno(LOG_ERR, "gethostname() failed");
89 return (-1);
90 }
91 if (strcmp(buf, name) == 0)
92 return (1);
93
94 /*
95 * Now check if it matches first part of the host name.
96 */
97 pos = strchr(buf, '.');
98 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
99 strncmp(buf, name, pos - buf) == 0) {
100 return (1);
101 }
102
103 /*
104 * At the end check if name is equal to our host's UUID.
105 */
106 bufsize = sizeof(buf);
107 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
108 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
109 return (-1);
110 }
111 if (strcasecmp(buf, name) == 0)
112 return (1);
113
114 /*
115 * Looks like this isn't about us.
116 */
117 return (0);
118}
119
120static bool
121family_supported(int family)
122{
123 int sock;
124
125 sock = socket(family, SOCK_STREAM, 0);
126 if (sock == -1 && errno == EPROTONOSUPPORT)
127 return (false);
128 if (sock >= 0)
129 (void)close(sock);
130 return (true);
131}
132
133static int
134node_names(char **namesp)
135{
136 static char names[MAXHOSTNAMELEN * 3];
137 char buf[MAXHOSTNAMELEN];
138 char *pos;
139 size_t bufsize;
140
141 if (gethostname(buf, sizeof(buf)) < 0) {
142 pjdlog_errno(LOG_ERR, "gethostname() failed");
143 return (-1);
144 }
145
146 /* First component of the host name. */
147 pos = strchr(buf, '.');
148 if (pos != NULL && pos != buf) {
149 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
150 sizeof(names)));
151 (void)strlcat(names, ", ", sizeof(names));
152 }
153
154 /* Full host name. */
155 (void)strlcat(names, buf, sizeof(names));
156 (void)strlcat(names, ", ", sizeof(names));
157
158 /* Host UUID. */
159 bufsize = sizeof(buf);
160 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
161 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
162 return (-1);
163 }
164 (void)strlcat(names, buf, sizeof(names));
165
166 *namesp = names;
167
168 return (0);
169}
170
171void
172yyerror(const char *str)
173{
174
175 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
176 lineno, yytext, str);
177}
178
179struct hastd_config *
180yy_config_parse(const char *config, bool exitonerror)
181{
182 int ret;
183
184 curres = NULL;
185 mynode = false;
186 depth = 0;
187 lineno = 0;
188
189 depth0_timeout = HAST_TIMEOUT;
190 depth0_replication = HAST_REPLICATION_FULLSYNC;
191 depth0_checksum = HAST_CHECKSUM_NONE;
192 depth0_compression = HAST_COMPRESSION_HOLE;
193 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
194 TAILQ_INIT(&depth0_listen);
195 strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
196 sizeof(depth0_listen_tcp4));
197 strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
198 sizeof(depth0_listen_tcp6));
199 depth0_exec[0] = '\0';
200
201 lconfig = calloc(1, sizeof(*lconfig));
202 if (lconfig == NULL) {
203 pjdlog_error("Unable to allocate memory for configuration.");
204 if (exitonerror)
205 exit(EX_TEMPFAIL);
206 return (NULL);
207 }
208
209 TAILQ_INIT(&lconfig->hc_listen);
210 TAILQ_INIT(&lconfig->hc_resources);
211
212 yyin = fopen(config, "r");
213 if (yyin == NULL) {
214 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
215 config);
216 yy_config_free(lconfig);
217 if (exitonerror)
218 exit(EX_OSFILE);
219 return (NULL);
220 }
221 yyrestart(yyin);
222 ret = yyparse();
223 fclose(yyin);
224 if (ret != 0) {
225 yy_config_free(lconfig);
226 if (exitonerror)
227 exit(EX_CONFIG);
228 return (NULL);
229 }
230
231 /*
232 * Let's see if everything is set up.
233 */
234 if (lconfig->hc_controladdr[0] == '\0') {
235 strlcpy(lconfig->hc_controladdr, depth0_control,
236 sizeof(lconfig->hc_controladdr));
237 }
238 if (!TAILQ_EMPTY(&depth0_listen))
239 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
240 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
241 struct hastd_listen *lst;
242
243 if (family_supported(AF_INET)) {
244 lst = calloc(1, sizeof(*lst));
245 if (lst == NULL) {
246 pjdlog_error("Unable to allocate memory for listen address.");
247 yy_config_free(lconfig);
248 if (exitonerror)
249 exit(EX_TEMPFAIL);
250 return (NULL);
251 }
252 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
253 sizeof(lst->hl_addr));
254 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
255 } else {
256 pjdlog_debug(1,
257 "No IPv4 support in the kernel, not listening on IPv4 address.");
258 }
259 if (family_supported(AF_INET6)) {
260 lst = calloc(1, sizeof(*lst));
261 if (lst == NULL) {
262 pjdlog_error("Unable to allocate memory for listen address.");
263 yy_config_free(lconfig);
264 if (exitonerror)
265 exit(EX_TEMPFAIL);
266 return (NULL);
267 }
268 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
269 sizeof(lst->hl_addr));
270 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
271 } else {
272 pjdlog_debug(1,
273 "No IPv6 support in the kernel, not listening on IPv6 address.");
274 }
275 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
276 pjdlog_error("No address to listen on.");
277 yy_config_free(lconfig);
278 if (exitonerror)
279 exit(EX_TEMPFAIL);
280 return (NULL);
281 }
282 }
283 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
284 PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
285 PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
286 PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
287
288 if (curres->hr_replication == -1) {
289 /*
290 * Replication is not set at resource-level.
291 * Use global or default setting.
292 */
293 curres->hr_replication = depth0_replication;
294 }
295 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
296 curres->hr_replication == HAST_REPLICATION_ASYNC) {
297 pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
298 curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
299 "memsync" : "async", "fullsync");
300 curres->hr_replication = HAST_REPLICATION_FULLSYNC;
301 }
302 if (curres->hr_checksum == -1) {
303 /*
304 * Checksum is not set at resource-level.
305 * Use global or default setting.
306 */
307 curres->hr_checksum = depth0_checksum;
308 }
309 if (curres->hr_compression == -1) {
310 /*
311 * Compression is not set at resource-level.
312 * Use global or default setting.
313 */
314 curres->hr_compression = depth0_compression;
315 }
316 if (curres->hr_timeout == -1) {
317 /*
318 * Timeout is not set at resource-level.
319 * Use global or default setting.
320 */
321 curres->hr_timeout = depth0_timeout;
322 }
323 if (curres->hr_exec[0] == '\0') {
324 /*
325 * Exec is not set at resource-level.
326 * Use global or default setting.
327 */
328 strlcpy(curres->hr_exec, depth0_exec,
329 sizeof(curres->hr_exec));
330 }
331 }
332
333 return (lconfig);
334}
335
336void
337yy_config_free(struct hastd_config *config)
338{
339 struct hastd_listen *lst;
340 struct hast_resource *res;
341
342 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
343 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
344 free(lst);
345 }
346 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
347 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
348 free(lst);
349 }
350 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
351 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
352 free(res);
353 }
354 free(config);
355}
356%}
357
358%token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
359%token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
360%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
361%token NUM STR OB CB
362
363%type <str> remote_str
364%type <num> replication_type
365%type <num> checksum_type
366%type <num> compression_type
367
368%union
369{
370 int num;
371 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 ;
2/*-
3 * Copyright (c) 2009-2010 The FreeBSD Foundation
4 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5 * All rights reserved.
6 *
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: head/sbin/hastd/parse.y 225784 2011-09-27 08:04:01Z pjd $
32 */
33
34#include <sys/param.h> /* MAXHOSTNAMELEN */
35#include <sys/queue.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38
39#include <arpa/inet.h>
40
41#include <err.h>
42#include <errno.h>
43#include <stdio.h>
44#include <string.h>
45#include <sysexits.h>
46#include <unistd.h>
47
48#include <pjdlog.h>
49
50#include "hast.h"
51
52extern int depth;
53extern int lineno;
54
55extern FILE *yyin;
56extern char *yytext;
57
58static struct hastd_config *lconfig;
59static struct hast_resource *curres;
60static bool mynode, hadmynode;
61
62static char depth0_control[HAST_ADDRSIZE];
63static char depth0_listen_tcp4[HAST_ADDRSIZE];
64static char depth0_listen_tcp6[HAST_ADDRSIZE];
65static TAILQ_HEAD(, hastd_listen) depth0_listen;
66static int depth0_replication;
67static int depth0_checksum;
68static int depth0_compression;
69static int depth0_timeout;
70static char depth0_exec[PATH_MAX];
71
72static char depth1_provname[PATH_MAX];
73static char depth1_localpath[PATH_MAX];
74
75extern void yyrestart(FILE *);
76
77static int
78isitme(const char *name)
79{
80 char buf[MAXHOSTNAMELEN];
81 char *pos;
82 size_t bufsize;
83
84 /*
85 * First check if the give name matches our full hostname.
86 */
87 if (gethostname(buf, sizeof(buf)) < 0) {
88 pjdlog_errno(LOG_ERR, "gethostname() failed");
89 return (-1);
90 }
91 if (strcmp(buf, name) == 0)
92 return (1);
93
94 /*
95 * Now check if it matches first part of the host name.
96 */
97 pos = strchr(buf, '.');
98 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
99 strncmp(buf, name, pos - buf) == 0) {
100 return (1);
101 }
102
103 /*
104 * At the end check if name is equal to our host's UUID.
105 */
106 bufsize = sizeof(buf);
107 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
108 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
109 return (-1);
110 }
111 if (strcasecmp(buf, name) == 0)
112 return (1);
113
114 /*
115 * Looks like this isn't about us.
116 */
117 return (0);
118}
119
120static bool
121family_supported(int family)
122{
123 int sock;
124
125 sock = socket(family, SOCK_STREAM, 0);
126 if (sock == -1 && errno == EPROTONOSUPPORT)
127 return (false);
128 if (sock >= 0)
129 (void)close(sock);
130 return (true);
131}
132
133static int
134node_names(char **namesp)
135{
136 static char names[MAXHOSTNAMELEN * 3];
137 char buf[MAXHOSTNAMELEN];
138 char *pos;
139 size_t bufsize;
140
141 if (gethostname(buf, sizeof(buf)) < 0) {
142 pjdlog_errno(LOG_ERR, "gethostname() failed");
143 return (-1);
144 }
145
146 /* First component of the host name. */
147 pos = strchr(buf, '.');
148 if (pos != NULL && pos != buf) {
149 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
150 sizeof(names)));
151 (void)strlcat(names, ", ", sizeof(names));
152 }
153
154 /* Full host name. */
155 (void)strlcat(names, buf, sizeof(names));
156 (void)strlcat(names, ", ", sizeof(names));
157
158 /* Host UUID. */
159 bufsize = sizeof(buf);
160 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
161 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
162 return (-1);
163 }
164 (void)strlcat(names, buf, sizeof(names));
165
166 *namesp = names;
167
168 return (0);
169}
170
171void
172yyerror(const char *str)
173{
174
175 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
176 lineno, yytext, str);
177}
178
179struct hastd_config *
180yy_config_parse(const char *config, bool exitonerror)
181{
182 int ret;
183
184 curres = NULL;
185 mynode = false;
186 depth = 0;
187 lineno = 0;
188
189 depth0_timeout = HAST_TIMEOUT;
190 depth0_replication = HAST_REPLICATION_FULLSYNC;
191 depth0_checksum = HAST_CHECKSUM_NONE;
192 depth0_compression = HAST_COMPRESSION_HOLE;
193 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
194 TAILQ_INIT(&depth0_listen);
195 strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
196 sizeof(depth0_listen_tcp4));
197 strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
198 sizeof(depth0_listen_tcp6));
199 depth0_exec[0] = '\0';
200
201 lconfig = calloc(1, sizeof(*lconfig));
202 if (lconfig == NULL) {
203 pjdlog_error("Unable to allocate memory for configuration.");
204 if (exitonerror)
205 exit(EX_TEMPFAIL);
206 return (NULL);
207 }
208
209 TAILQ_INIT(&lconfig->hc_listen);
210 TAILQ_INIT(&lconfig->hc_resources);
211
212 yyin = fopen(config, "r");
213 if (yyin == NULL) {
214 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
215 config);
216 yy_config_free(lconfig);
217 if (exitonerror)
218 exit(EX_OSFILE);
219 return (NULL);
220 }
221 yyrestart(yyin);
222 ret = yyparse();
223 fclose(yyin);
224 if (ret != 0) {
225 yy_config_free(lconfig);
226 if (exitonerror)
227 exit(EX_CONFIG);
228 return (NULL);
229 }
230
231 /*
232 * Let's see if everything is set up.
233 */
234 if (lconfig->hc_controladdr[0] == '\0') {
235 strlcpy(lconfig->hc_controladdr, depth0_control,
236 sizeof(lconfig->hc_controladdr));
237 }
238 if (!TAILQ_EMPTY(&depth0_listen))
239 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
240 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
241 struct hastd_listen *lst;
242
243 if (family_supported(AF_INET)) {
244 lst = calloc(1, sizeof(*lst));
245 if (lst == NULL) {
246 pjdlog_error("Unable to allocate memory for listen address.");
247 yy_config_free(lconfig);
248 if (exitonerror)
249 exit(EX_TEMPFAIL);
250 return (NULL);
251 }
252 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
253 sizeof(lst->hl_addr));
254 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
255 } else {
256 pjdlog_debug(1,
257 "No IPv4 support in the kernel, not listening on IPv4 address.");
258 }
259 if (family_supported(AF_INET6)) {
260 lst = calloc(1, sizeof(*lst));
261 if (lst == NULL) {
262 pjdlog_error("Unable to allocate memory for listen address.");
263 yy_config_free(lconfig);
264 if (exitonerror)
265 exit(EX_TEMPFAIL);
266 return (NULL);
267 }
268 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
269 sizeof(lst->hl_addr));
270 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
271 } else {
272 pjdlog_debug(1,
273 "No IPv6 support in the kernel, not listening on IPv6 address.");
274 }
275 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
276 pjdlog_error("No address to listen on.");
277 yy_config_free(lconfig);
278 if (exitonerror)
279 exit(EX_TEMPFAIL);
280 return (NULL);
281 }
282 }
283 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
284 PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
285 PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
286 PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
287
288 if (curres->hr_replication == -1) {
289 /*
290 * Replication is not set at resource-level.
291 * Use global or default setting.
292 */
293 curres->hr_replication = depth0_replication;
294 }
295 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
296 curres->hr_replication == HAST_REPLICATION_ASYNC) {
297 pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
298 curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
299 "memsync" : "async", "fullsync");
300 curres->hr_replication = HAST_REPLICATION_FULLSYNC;
301 }
302 if (curres->hr_checksum == -1) {
303 /*
304 * Checksum is not set at resource-level.
305 * Use global or default setting.
306 */
307 curres->hr_checksum = depth0_checksum;
308 }
309 if (curres->hr_compression == -1) {
310 /*
311 * Compression is not set at resource-level.
312 * Use global or default setting.
313 */
314 curres->hr_compression = depth0_compression;
315 }
316 if (curres->hr_timeout == -1) {
317 /*
318 * Timeout is not set at resource-level.
319 * Use global or default setting.
320 */
321 curres->hr_timeout = depth0_timeout;
322 }
323 if (curres->hr_exec[0] == '\0') {
324 /*
325 * Exec is not set at resource-level.
326 * Use global or default setting.
327 */
328 strlcpy(curres->hr_exec, depth0_exec,
329 sizeof(curres->hr_exec));
330 }
331 }
332
333 return (lconfig);
334}
335
336void
337yy_config_free(struct hastd_config *config)
338{
339 struct hastd_listen *lst;
340 struct hast_resource *res;
341
342 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
343 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
344 free(lst);
345 }
346 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
347 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
348 free(lst);
349 }
350 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
351 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
352 free(res);
353 }
354 free(config);
355}
356%}
357
358%token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
359%token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
360%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
361%token NUM STR OB CB
362
363%type <str> remote_str
364%type <num> replication_type
365%type <num> checksum_type
366%type <num> compression_type
367
368%union
369{
370 int num;
371 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 ;