]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sbin/hastd/parse.y
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sbin / hastd / parse.y
1 %{
2 /*-
3  * Copyright (c) 2009-2010 The FreeBSD Foundation
4  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5  * All rights reserved.
6  *
7  * This software was developed by Pawel Jakub Dawidek under sponsorship from
8  * the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33
34 #include <sys/param.h>  /* MAXHOSTNAMELEN */
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
38
39 #include <arpa/inet.h>
40
41 #include <assert.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48
49 #include <pjdlog.h>
50
51 #include "hast.h"
52
53 extern int depth;
54 extern int lineno;
55
56 extern FILE *yyin;
57 extern char *yytext;
58
59 static struct hastd_config *lconfig;
60 static struct hast_resource *curres;
61 static bool mynode, hadmynode;
62
63 static char depth0_control[HAST_ADDRSIZE];
64 static char depth0_listen_tcp4[HAST_ADDRSIZE];
65 static char depth0_listen_tcp6[HAST_ADDRSIZE];
66 static TAILQ_HEAD(, hastd_listen) depth0_listen;
67 static int depth0_replication;
68 static int depth0_checksum;
69 static int depth0_compression;
70 static int depth0_timeout;
71 static char depth0_exec[PATH_MAX];
72
73 static char depth1_provname[PATH_MAX];
74 static char depth1_localpath[PATH_MAX];
75
76 extern void yyrestart(FILE *);
77
78 static int
79 isitme(const char *name)
80 {
81         char buf[MAXHOSTNAMELEN];
82         char *pos;
83         size_t bufsize;
84
85         /*
86          * First check if the give name matches our full hostname.
87          */
88         if (gethostname(buf, sizeof(buf)) < 0) {
89                 pjdlog_errno(LOG_ERR, "gethostname() failed");
90                 return (-1);
91         }
92         if (strcmp(buf, name) == 0)
93                 return (1);
94
95         /*
96          * Now check if it matches first part of the host name.
97          */
98         pos = strchr(buf, '.');
99         if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
100             strncmp(buf, name, pos - buf) == 0) {
101                 return (1);
102         }
103
104         /*
105          * At the end check if name is equal to our host's UUID.
106          */
107         bufsize = sizeof(buf);
108         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
109                 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
110                 return (-1);
111         }
112         if (strcasecmp(buf, name) == 0)
113                 return (1);
114
115         /*
116          * Looks like this isn't about us.
117          */
118         return (0);
119 }
120
121 static bool
122 family_supported(int family)
123 {
124         int sock;
125
126         sock = socket(family, SOCK_STREAM, 0);
127         if (sock == -1 && errno == EPROTONOSUPPORT)
128                 return (false);
129         if (sock >= 0)
130                 (void)close(sock);
131         return (true);
132 }
133
134 static int
135 node_names(char **namesp)
136 {
137         static char names[MAXHOSTNAMELEN * 3];
138         char buf[MAXHOSTNAMELEN];
139         char *pos;
140         size_t bufsize;
141
142         if (gethostname(buf, sizeof(buf)) < 0) {
143                 pjdlog_errno(LOG_ERR, "gethostname() failed");
144                 return (-1);
145         }
146
147         /* First component of the host name. */
148         pos = strchr(buf, '.');
149         if (pos != NULL && pos != buf) {
150                 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
151                     sizeof(names)));
152                 (void)strlcat(names, ", ", sizeof(names));
153         }
154
155         /* Full host name. */
156         (void)strlcat(names, buf, sizeof(names));
157         (void)strlcat(names, ", ", sizeof(names));
158
159         /* Host UUID. */
160         bufsize = sizeof(buf);
161         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
162                 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
163                 return (-1);
164         }
165         (void)strlcat(names, buf, sizeof(names));
166
167         *namesp = names;
168
169         return (0);
170 }
171
172 void
173 yyerror(const char *str)
174 {
175
176         pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
177             lineno, yytext, str);
178 }
179
180 struct hastd_config *
181 yy_config_parse(const char *config, bool exitonerror)
182 {
183         int ret;
184
185         curres = NULL;
186         mynode = false;
187         depth = 0;
188         lineno = 0;
189
190         depth0_timeout = HAST_TIMEOUT;
191         depth0_replication = HAST_REPLICATION_FULLSYNC;
192         depth0_checksum = HAST_CHECKSUM_NONE;
193         depth0_compression = HAST_COMPRESSION_HOLE;
194         strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
195         TAILQ_INIT(&depth0_listen);
196         strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
197             sizeof(depth0_listen_tcp4));
198         strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
199             sizeof(depth0_listen_tcp6));
200         depth0_exec[0] = '\0';
201
202         lconfig = calloc(1, sizeof(*lconfig));
203         if (lconfig == NULL) {
204                 pjdlog_error("Unable to allocate memory for configuration.");
205                 if (exitonerror)
206                         exit(EX_TEMPFAIL);
207                 return (NULL);
208         }
209
210         TAILQ_INIT(&lconfig->hc_listen);
211         TAILQ_INIT(&lconfig->hc_resources);
212
213         yyin = fopen(config, "r");
214         if (yyin == NULL) {
215                 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
216                     config);
217                 yy_config_free(lconfig);
218                 if (exitonerror)
219                         exit(EX_OSFILE);
220                 return (NULL);
221         }
222         yyrestart(yyin);
223         ret = yyparse();
224         fclose(yyin);
225         if (ret != 0) {
226                 yy_config_free(lconfig);
227                 if (exitonerror)
228                         exit(EX_CONFIG);
229                 return (NULL);
230         }
231
232         /*
233          * Let's see if everything is set up.
234          */
235         if (lconfig->hc_controladdr[0] == '\0') {
236                 strlcpy(lconfig->hc_controladdr, depth0_control,
237                     sizeof(lconfig->hc_controladdr));
238         }
239         if (!TAILQ_EMPTY(&depth0_listen))
240                 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
241         if (TAILQ_EMPTY(&lconfig->hc_listen)) {
242                 struct hastd_listen *lst;
243
244                 if (family_supported(AF_INET)) {
245                         lst = calloc(1, sizeof(*lst));
246                         if (lst == NULL) {
247                                 pjdlog_error("Unable to allocate memory for listen address.");
248                                 yy_config_free(lconfig);
249                                 if (exitonerror)
250                                         exit(EX_TEMPFAIL);
251                                 return (NULL);
252                         }
253                         (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
254                             sizeof(lst->hl_addr));
255                         TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
256                 } else {
257                         pjdlog_debug(1,
258                             "No IPv4 support in the kernel, not listening on IPv4 address.");
259                 }
260                 if (family_supported(AF_INET6)) {
261                         lst = calloc(1, sizeof(*lst));
262                         if (lst == NULL) {
263                                 pjdlog_error("Unable to allocate memory for listen address.");
264                                 yy_config_free(lconfig);
265                                 if (exitonerror)
266                                         exit(EX_TEMPFAIL);
267                                 return (NULL);
268                         }
269                         (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
270                             sizeof(lst->hl_addr));
271                         TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
272                 } else {
273                         pjdlog_debug(1,
274                             "No IPv6 support in the kernel, not listening on IPv6 address.");
275                 }
276                 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
277                         pjdlog_error("No address to listen on.");
278                         yy_config_free(lconfig);
279                         if (exitonerror)
280                                 exit(EX_TEMPFAIL);
281                         return (NULL);
282                 }
283         }
284         TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
285                 assert(curres->hr_provname[0] != '\0');
286                 assert(curres->hr_localpath[0] != '\0');
287                 assert(curres->hr_remoteaddr[0] != '\0');
288
289                 if (curres->hr_replication == -1) {
290                         /*
291                          * Replication is not set at resource-level.
292                          * Use global or default setting.
293                          */
294                         curres->hr_replication = depth0_replication;
295                 }
296                 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
297                     curres->hr_replication == HAST_REPLICATION_ASYNC) {
298                         pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
299                             curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
300                             "memsync" : "async", "fullsync");
301                         curres->hr_replication = HAST_REPLICATION_FULLSYNC;
302                 }
303                 if (curres->hr_checksum == -1) {
304                         /*
305                          * Checksum is not set at resource-level.
306                          * Use global or default setting.
307                          */
308                         curres->hr_checksum = depth0_checksum;
309                 }
310                 if (curres->hr_compression == -1) {
311                         /*
312                          * Compression is not set at resource-level.
313                          * Use global or default setting.
314                          */
315                         curres->hr_compression = depth0_compression;
316                 }
317                 if (curres->hr_timeout == -1) {
318                         /*
319                          * Timeout is not set at resource-level.
320                          * Use global or default setting.
321                          */
322                         curres->hr_timeout = depth0_timeout;
323                 }
324                 if (curres->hr_exec[0] == '\0') {
325                         /*
326                          * Exec is not set at resource-level.
327                          * Use global or default setting.
328                          */
329                         strlcpy(curres->hr_exec, depth0_exec,
330                             sizeof(curres->hr_exec));
331                 }
332         }
333
334         return (lconfig);
335 }
336
337 void
338 yy_config_free(struct hastd_config *config)
339 {
340         struct hastd_listen *lst;
341         struct hast_resource *res;
342
343         while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
344                 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
345                 free(lst);
346         }
347         while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
348                 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
349                 free(lst);
350         }
351         while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
352                 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
353                 free(res);
354         }
355         free(config);
356 }
357 %}
358
359 %token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
360 %token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
361 %token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
362 %token NUM STR OB CB
363
364 %type <str> remote_str
365 %type <num> replication_type
366 %type <num> checksum_type
367 %type <num> compression_type
368
369 %union
370 {
371         int num;
372         char *str;
373 }
374
375 %token <num> NUM
376 %token <str> STR
377
378 %%
379
380 statements:
381         |
382         statements statement
383         ;
384
385 statement:
386         control_statement
387         |
388         listen_statement
389         |
390         replication_statement
391         |
392         checksum_statement
393         |
394         compression_statement
395         |
396         timeout_statement
397         |
398         exec_statement
399         |
400         node_statement
401         |
402         resource_statement
403         ;
404
405 control_statement:      CONTROL STR
406         {
407                 switch (depth) {
408                 case 0:
409                         if (strlcpy(depth0_control, $2,
410                             sizeof(depth0_control)) >=
411                             sizeof(depth0_control)) {
412                                 pjdlog_error("control argument is too long.");
413                                 free($2);
414                                 return (1);
415                         }
416                         break;
417                 case 1:
418                         if (!mynode)
419                                 break;
420                         if (strlcpy(lconfig->hc_controladdr, $2,
421                             sizeof(lconfig->hc_controladdr)) >=
422                             sizeof(lconfig->hc_controladdr)) {
423                                 pjdlog_error("control argument is too long.");
424                                 free($2);
425                                 return (1);
426                         }
427                         break;
428                 default:
429                         assert(!"control at wrong depth level");
430                 }
431                 free($2);
432         }
433         ;
434
435 listen_statement:       LISTEN STR
436         {
437                 struct hastd_listen *lst;
438
439                 lst = calloc(1, sizeof(*lst));
440                 if (lst == NULL) {
441                         pjdlog_error("Unable to allocate memory for listen address.");
442                         free($2);
443                         return (1);
444                 }
445                 if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
446                     sizeof(lst->hl_addr)) {
447                         pjdlog_error("listen argument is too long.");
448                         free($2);
449                         free(lst);
450                         return (1);
451                 }
452                 switch (depth) {
453                 case 0:
454                         TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
455                         break;
456                 case 1:
457                         if (mynode)
458                                 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
459                         else
460                                 free(lst);
461                         break;
462                 default:
463                         assert(!"listen at wrong depth level");
464                 }
465                 free($2);
466         }
467         ;
468
469 replication_statement:  REPLICATION replication_type
470         {
471                 switch (depth) {
472                 case 0:
473                         depth0_replication = $2;
474                         break;
475                 case 1:
476                         if (curres != NULL)
477                                 curres->hr_replication = $2;
478                         break;
479                 default:
480                         assert(!"replication at wrong depth level");
481                 }
482         }
483         ;
484
485 replication_type:
486         FULLSYNC        { $$ = HAST_REPLICATION_FULLSYNC; }
487         |
488         MEMSYNC         { $$ = HAST_REPLICATION_MEMSYNC; }
489         |
490         ASYNC           { $$ = HAST_REPLICATION_ASYNC; }
491         ;
492
493 checksum_statement:     CHECKSUM checksum_type
494         {
495                 switch (depth) {
496                 case 0:
497                         depth0_checksum = $2;
498                         break;
499                 case 1:
500                         if (curres != NULL)
501                                 curres->hr_checksum = $2;
502                         break;
503                 default:
504                         assert(!"checksum at wrong depth level");
505                 }
506         }
507         ;
508
509 checksum_type:
510         NONE            { $$ = HAST_CHECKSUM_NONE; }
511         |
512         CRC32           { $$ = HAST_CHECKSUM_CRC32; }
513         |
514         SHA256          { $$ = HAST_CHECKSUM_SHA256; }
515         ;
516
517 compression_statement:  COMPRESSION compression_type
518         {
519                 switch (depth) {
520                 case 0:
521                         depth0_compression = $2;
522                         break;
523                 case 1:
524                         if (curres != NULL)
525                                 curres->hr_compression = $2;
526                         break;
527                 default:
528                         assert(!"compression at wrong depth level");
529                 }
530         }
531         ;
532
533 compression_type:
534         NONE            { $$ = HAST_COMPRESSION_NONE; }
535         |
536         HOLE            { $$ = HAST_COMPRESSION_HOLE; }
537         |
538         LZF             { $$ = HAST_COMPRESSION_LZF; }
539         ;
540
541 timeout_statement:      TIMEOUT NUM
542         {
543                 if ($2 <= 0) {
544                         pjdlog_error("Negative or zero timeout.");
545                         return (1);
546                 }
547                 switch (depth) {
548                 case 0:
549                         depth0_timeout = $2;
550                         break;
551                 case 1:
552                         if (curres != NULL)
553                                 curres->hr_timeout = $2;
554                         break;
555                 default:
556                         assert(!"timeout at wrong depth level");
557                 }
558         }
559         ;
560
561 exec_statement:         EXEC STR
562         {
563                 switch (depth) {
564                 case 0:
565                         if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
566                             sizeof(depth0_exec)) {
567                                 pjdlog_error("Exec path is too long.");
568                                 free($2);
569                                 return (1);
570                         }
571                         break;
572                 case 1:
573                         if (curres == NULL)
574                                 break;
575                         if (strlcpy(curres->hr_exec, $2,
576                             sizeof(curres->hr_exec)) >=
577                             sizeof(curres->hr_exec)) {
578                                 pjdlog_error("Exec path is too long.");
579                                 free($2);
580                                 return (1);
581                         }
582                         break;
583                 default:
584                         assert(!"exec at wrong depth level");
585                 }
586                 free($2);
587         }
588         ;
589
590 node_statement:         ON node_start OB node_entries CB
591         {
592                 mynode = false;
593         }
594         ;
595
596 node_start:     STR
597         {
598                 switch (isitme($1)) {
599                 case -1:
600                         free($1);
601                         return (1);
602                 case 0:
603                         break;
604                 case 1:
605                         mynode = true;
606                         break;
607                 default:
608                         assert(!"invalid isitme() return value");
609                 }
610                 free($1);
611         }
612         ;
613
614 node_entries:
615         |
616         node_entries node_entry
617         ;
618
619 node_entry:
620         control_statement
621         |
622         listen_statement
623         ;
624
625 resource_statement:     RESOURCE resource_start OB resource_entries CB
626         {
627                 if (curres != NULL) {
628                         /*
629                          * There must be section for this node, at least with
630                          * remote address configuration.
631                          */
632                         if (!hadmynode) {
633                                 char *names;
634
635                                 if (node_names(&names) != 0)
636                                         return (1);
637                                 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
638                                     curres->hr_name, names);
639                                 return (1);
640                         }
641
642                         /*
643                          * Let's see there are some resource-level settings
644                          * that we can use for node-level settings.
645                          */
646                         if (curres->hr_provname[0] == '\0' &&
647                             depth1_provname[0] != '\0') {
648                                 /*
649                                  * Provider name is not set at node-level,
650                                  * but is set at resource-level, use it.
651                                  */
652                                 strlcpy(curres->hr_provname, depth1_provname,
653                                     sizeof(curres->hr_provname));
654                         }
655                         if (curres->hr_localpath[0] == '\0' &&
656                             depth1_localpath[0] != '\0') {
657                                 /*
658                                  * Path to local provider is not set at
659                                  * node-level, but is set at resource-level,
660                                  * use it.
661                                  */
662                                 strlcpy(curres->hr_localpath, depth1_localpath,
663                                     sizeof(curres->hr_localpath));
664                         }
665
666                         /*
667                          * If provider name is not given, use resource name
668                          * as provider name.
669                          */
670                         if (curres->hr_provname[0] == '\0') {
671                                 strlcpy(curres->hr_provname, curres->hr_name,
672                                     sizeof(curres->hr_provname));
673                         }
674
675                         /*
676                          * Remote address has to be configured at this point.
677                          */
678                         if (curres->hr_remoteaddr[0] == '\0') {
679                                 pjdlog_error("Remote address not configured for resource %s.",
680                                     curres->hr_name);
681                                 return (1);
682                         }
683                         /*
684                          * Path to local provider has to be configured at this
685                          * point.
686                          */
687                         if (curres->hr_localpath[0] == '\0') {
688                                 pjdlog_error("Path to local component not configured for resource %s.",
689                                     curres->hr_name);
690                                 return (1);
691                         }
692
693                         /* Put it onto resource list. */
694                         TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
695                         curres = NULL;
696                 }
697         }
698         ;
699
700 resource_start: STR
701         {
702                 /* Check if there is no duplicate entry. */
703                 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
704                         if (strcmp(curres->hr_name, $1) == 0) {
705                                 pjdlog_error("Resource %s configured more than once.",
706                                     curres->hr_name);
707                                 free($1);
708                                 return (1);
709                         }
710                 }
711
712                 /*
713                  * Clear those, so we can tell if they were set at
714                  * resource-level or not.
715                  */
716                 depth1_provname[0] = '\0';
717                 depth1_localpath[0] = '\0';
718                 hadmynode = false;
719
720                 curres = calloc(1, sizeof(*curres));
721                 if (curres == NULL) {
722                         pjdlog_error("Unable to allocate memory for resource.");
723                         free($1);
724                         return (1);
725                 }
726                 if (strlcpy(curres->hr_name, $1,
727                     sizeof(curres->hr_name)) >=
728                     sizeof(curres->hr_name)) {
729                         pjdlog_error("Resource name is too long.");
730                         free($1);
731                         return (1);
732                 }
733                 free($1);
734                 curres->hr_role = HAST_ROLE_INIT;
735                 curres->hr_previous_role = HAST_ROLE_INIT;
736                 curres->hr_replication = -1;
737                 curres->hr_checksum = -1;
738                 curres->hr_compression = -1;
739                 curres->hr_timeout = -1;
740                 curres->hr_exec[0] = '\0';
741                 curres->hr_provname[0] = '\0';
742                 curres->hr_localpath[0] = '\0';
743                 curres->hr_localfd = -1;
744                 curres->hr_remoteaddr[0] = '\0';
745                 curres->hr_sourceaddr[0] = '\0';
746                 curres->hr_ggateunit = -1;
747         }
748         ;
749
750 resource_entries:
751         |
752         resource_entries resource_entry
753         ;
754
755 resource_entry:
756         replication_statement
757         |
758         checksum_statement
759         |
760         compression_statement
761         |
762         timeout_statement
763         |
764         exec_statement
765         |
766         name_statement
767         |
768         local_statement
769         |
770         resource_node_statement
771         ;
772
773 name_statement:         NAME STR
774         {
775                 switch (depth) {
776                 case 1:
777                         if (strlcpy(depth1_provname, $2,
778                             sizeof(depth1_provname)) >=
779                             sizeof(depth1_provname)) {
780                                 pjdlog_error("name argument is too long.");
781                                 free($2);
782                                 return (1);
783                         }
784                         break;
785                 case 2:
786                         if (!mynode)
787                                 break;
788                         assert(curres != NULL);
789                         if (strlcpy(curres->hr_provname, $2,
790                             sizeof(curres->hr_provname)) >=
791                             sizeof(curres->hr_provname)) {
792                                 pjdlog_error("name argument is too long.");
793                                 free($2);
794                                 return (1);
795                         }
796                         break;
797                 default:
798                         assert(!"name at wrong depth level");
799                 }
800                 free($2);
801         }
802         ;
803
804 local_statement:        LOCAL STR
805         {
806                 switch (depth) {
807                 case 1:
808                         if (strlcpy(depth1_localpath, $2,
809                             sizeof(depth1_localpath)) >=
810                             sizeof(depth1_localpath)) {
811                                 pjdlog_error("local argument is too long.");
812                                 free($2);
813                                 return (1);
814                         }
815                         break;
816                 case 2:
817                         if (!mynode)
818                                 break;
819                         assert(curres != NULL);
820                         if (strlcpy(curres->hr_localpath, $2,
821                             sizeof(curres->hr_localpath)) >=
822                             sizeof(curres->hr_localpath)) {
823                                 pjdlog_error("local argument is too long.");
824                                 free($2);
825                                 return (1);
826                         }
827                         break;
828                 default:
829                         assert(!"local at wrong depth level");
830                 }
831                 free($2);
832         }
833         ;
834
835 resource_node_statement:ON resource_node_start OB resource_node_entries CB
836         {
837                 mynode = false;
838         }
839         ;
840
841 resource_node_start:    STR
842         {
843                 if (curres != NULL) {
844                         switch (isitme($1)) {
845                         case -1:
846                                 free($1);
847                                 return (1);
848                         case 0:
849                                 break;
850                         case 1:
851                                 mynode = hadmynode = true;
852                                 break;
853                         default:
854                                 assert(!"invalid isitme() return value");
855                         }
856                 }
857                 free($1);
858         }
859         ;
860
861 resource_node_entries:
862         |
863         resource_node_entries resource_node_entry
864         ;
865
866 resource_node_entry:
867         name_statement
868         |
869         local_statement
870         |
871         remote_statement
872         |
873         source_statement
874         ;
875
876 remote_statement:       REMOTE remote_str
877         {
878                 assert(depth == 2);
879                 if (mynode) {
880                         assert(curres != NULL);
881                         if (strlcpy(curres->hr_remoteaddr, $2,
882                             sizeof(curres->hr_remoteaddr)) >=
883                             sizeof(curres->hr_remoteaddr)) {
884                                 pjdlog_error("remote argument is too long.");
885                                 free($2);
886                                 return (1);
887                         }
888                 }
889                 free($2);
890         }
891         ;
892
893 remote_str:
894         NONE            { $$ = strdup("none"); }
895         |
896         STR             { }
897         ;
898
899 source_statement:       SOURCE STR
900         {
901                 assert(depth == 2);
902                 if (mynode) {
903                         assert(curres != NULL);
904                         if (strlcpy(curres->hr_sourceaddr, $2,
905                             sizeof(curres->hr_sourceaddr)) >=
906                             sizeof(curres->hr_sourceaddr)) {
907                                 pjdlog_error("source argument is too long.");
908                                 free($2);
909                                 return (1);
910                         }
911                 }
912                 free($2);
913         }
914         ;