]> CyberLeo.Net >> Repos - FreeBSD/releng/9.1.git/blob - sbin/hastd/parse.y
MFC r239464:
[FreeBSD/releng/9.1.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 <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
52 extern int depth;
53 extern int lineno;
54
55 extern FILE *yyin;
56 extern char *yytext;
57
58 static struct hastd_config *lconfig;
59 static struct hast_resource *curres;
60 static bool mynode, hadmynode;
61
62 static char depth0_control[HAST_ADDRSIZE];
63 static char depth0_pidfile[PATH_MAX];
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 static int depth0_metaflush;
73
74 static char depth1_provname[PATH_MAX];
75 static char depth1_localpath[PATH_MAX];
76 static int depth1_metaflush;
77
78 extern void yyrestart(FILE *);
79
80 static int
81 isitme(const char *name)
82 {
83         char buf[MAXHOSTNAMELEN];
84         char *pos;
85         size_t bufsize;
86
87         /*
88          * First check if the given name matches our full hostname.
89          */
90         if (gethostname(buf, sizeof(buf)) < 0) {
91                 pjdlog_errno(LOG_ERR, "gethostname() failed");
92                 return (-1);
93         }
94         if (strcmp(buf, name) == 0)
95                 return (1);
96
97         /*
98          * Now check if it matches first part of the host name.
99          */
100         pos = strchr(buf, '.');
101         if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
102             strncmp(buf, name, pos - buf) == 0) {
103                 return (1);
104         }
105
106         /*
107          * At the end check if name is equal to our host's UUID.
108          */
109         bufsize = sizeof(buf);
110         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
111                 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
112                 return (-1);
113         }
114         if (strcasecmp(buf, name) == 0)
115                 return (1);
116
117         /*
118          * Looks like this isn't about us.
119          */
120         return (0);
121 }
122
123 static bool
124 family_supported(int family)
125 {
126         int sock;
127
128         sock = socket(family, SOCK_STREAM, 0);
129         if (sock == -1 && errno == EPROTONOSUPPORT)
130                 return (false);
131         if (sock >= 0)
132                 (void)close(sock);
133         return (true);
134 }
135
136 static int
137 node_names(char **namesp)
138 {
139         static char names[MAXHOSTNAMELEN * 3];
140         char buf[MAXHOSTNAMELEN];
141         char *pos;
142         size_t bufsize;
143
144         if (gethostname(buf, sizeof(buf)) < 0) {
145                 pjdlog_errno(LOG_ERR, "gethostname() failed");
146                 return (-1);
147         }
148
149         /* First component of the host name. */
150         pos = strchr(buf, '.');
151         if (pos != NULL && pos != buf) {
152                 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
153                     sizeof(names)));
154                 (void)strlcat(names, ", ", sizeof(names));
155         }
156
157         /* Full host name. */
158         (void)strlcat(names, buf, sizeof(names));
159         (void)strlcat(names, ", ", sizeof(names));
160
161         /* Host UUID. */
162         bufsize = sizeof(buf);
163         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
164                 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
165                 return (-1);
166         }
167         (void)strlcat(names, buf, sizeof(names));
168
169         *namesp = names;
170
171         return (0);
172 }
173
174 void
175 yyerror(const char *str)
176 {
177
178         pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
179             lineno, yytext, str);
180 }
181
182 struct hastd_config *
183 yy_config_parse(const char *config, bool exitonerror)
184 {
185         int ret;
186
187         curres = NULL;
188         mynode = false;
189         depth = 0;
190         lineno = 0;
191
192         depth0_timeout = HAST_TIMEOUT;
193         depth0_replication = HAST_REPLICATION_FULLSYNC;
194         depth0_checksum = HAST_CHECKSUM_NONE;
195         depth0_compression = HAST_COMPRESSION_HOLE;
196         strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
197         strlcpy(depth0_pidfile, HASTD_PIDFILE, sizeof(depth0_pidfile));
198         TAILQ_INIT(&depth0_listen);
199         strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
200             sizeof(depth0_listen_tcp4));
201         strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
202             sizeof(depth0_listen_tcp6));
203         depth0_exec[0] = '\0';
204         depth0_metaflush = 1;
205
206         lconfig = calloc(1, sizeof(*lconfig));
207         if (lconfig == NULL) {
208                 pjdlog_error("Unable to allocate memory for configuration.");
209                 if (exitonerror)
210                         exit(EX_TEMPFAIL);
211                 return (NULL);
212         }
213
214         TAILQ_INIT(&lconfig->hc_listen);
215         TAILQ_INIT(&lconfig->hc_resources);
216
217         yyin = fopen(config, "r");
218         if (yyin == NULL) {
219                 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
220                     config);
221                 yy_config_free(lconfig);
222                 if (exitonerror)
223                         exit(EX_OSFILE);
224                 return (NULL);
225         }
226         yyrestart(yyin);
227         ret = yyparse();
228         fclose(yyin);
229         if (ret != 0) {
230                 yy_config_free(lconfig);
231                 if (exitonerror)
232                         exit(EX_CONFIG);
233                 return (NULL);
234         }
235
236         /*
237          * Let's see if everything is set up.
238          */
239         if (lconfig->hc_controladdr[0] == '\0') {
240                 strlcpy(lconfig->hc_controladdr, depth0_control,
241                     sizeof(lconfig->hc_controladdr));
242         }
243         if (lconfig->hc_pidfile[0] == '\0') {
244                 strlcpy(lconfig->hc_pidfile, depth0_pidfile,
245                     sizeof(lconfig->hc_pidfile));
246         }
247         if (!TAILQ_EMPTY(&depth0_listen))
248                 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
249         if (TAILQ_EMPTY(&lconfig->hc_listen)) {
250                 struct hastd_listen *lst;
251
252                 if (family_supported(AF_INET)) {
253                         lst = calloc(1, sizeof(*lst));
254                         if (lst == NULL) {
255                                 pjdlog_error("Unable to allocate memory for listen address.");
256                                 yy_config_free(lconfig);
257                                 if (exitonerror)
258                                         exit(EX_TEMPFAIL);
259                                 return (NULL);
260                         }
261                         (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
262                             sizeof(lst->hl_addr));
263                         TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
264                 } else {
265                         pjdlog_debug(1,
266                             "No IPv4 support in the kernel, not listening on IPv4 address.");
267                 }
268                 if (family_supported(AF_INET6)) {
269                         lst = calloc(1, sizeof(*lst));
270                         if (lst == NULL) {
271                                 pjdlog_error("Unable to allocate memory for listen address.");
272                                 yy_config_free(lconfig);
273                                 if (exitonerror)
274                                         exit(EX_TEMPFAIL);
275                                 return (NULL);
276                         }
277                         (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
278                             sizeof(lst->hl_addr));
279                         TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
280                 } else {
281                         pjdlog_debug(1,
282                             "No IPv6 support in the kernel, not listening on IPv6 address.");
283                 }
284                 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
285                         pjdlog_error("No address to listen on.");
286                         yy_config_free(lconfig);
287                         if (exitonerror)
288                                 exit(EX_TEMPFAIL);
289                         return (NULL);
290                 }
291         }
292         TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
293                 PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
294                 PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
295                 PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
296
297                 if (curres->hr_replication == -1) {
298                         /*
299                          * Replication is not set at resource-level.
300                          * Use global or default setting.
301                          */
302                         curres->hr_replication = depth0_replication;
303                 }
304                 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC) {
305                         pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
306                             "memsync", "fullsync");
307                         curres->hr_replication = HAST_REPLICATION_FULLSYNC;
308                 }
309                 if (curres->hr_checksum == -1) {
310                         /*
311                          * Checksum is not set at resource-level.
312                          * Use global or default setting.
313                          */
314                         curres->hr_checksum = depth0_checksum;
315                 }
316                 if (curres->hr_compression == -1) {
317                         /*
318                          * Compression is not set at resource-level.
319                          * Use global or default setting.
320                          */
321                         curres->hr_compression = depth0_compression;
322                 }
323                 if (curres->hr_timeout == -1) {
324                         /*
325                          * Timeout is not set at resource-level.
326                          * Use global or default setting.
327                          */
328                         curres->hr_timeout = depth0_timeout;
329                 }
330                 if (curres->hr_exec[0] == '\0') {
331                         /*
332                          * Exec is not set at resource-level.
333                          * Use global or default setting.
334                          */
335                         strlcpy(curres->hr_exec, depth0_exec,
336                             sizeof(curres->hr_exec));
337                 }
338                 if (curres->hr_metaflush == -1) {
339                         /*
340                          * Metaflush is not set at resource-level.
341                          * Use global or default setting.
342                          */
343                         curres->hr_metaflush = depth0_metaflush;
344                 }
345         }
346
347         return (lconfig);
348 }
349
350 void
351 yy_config_free(struct hastd_config *config)
352 {
353         struct hastd_listen *lst;
354         struct hast_resource *res;
355
356         while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
357                 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
358                 free(lst);
359         }
360         while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
361                 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
362                 free(lst);
363         }
364         while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
365                 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
366                 free(res);
367         }
368         free(config);
369 }
370 %}
371
372 %token CONTROL PIDFILE LISTEN REPLICATION CHECKSUM COMPRESSION METAFLUSH
373 %token TIMEOUT EXEC RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
374 %token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
375 %token NUM STR OB CB
376
377 %type <str> remote_str
378 %type <num> replication_type
379 %type <num> checksum_type
380 %type <num> compression_type
381 %type <num> boolean
382
383 %union
384 {
385         int num;
386         char *str;
387 }
388
389 %token <num> NUM
390 %token <str> STR
391
392 %%
393
394 statements:
395         |
396         statements statement
397         ;
398
399 statement:
400         control_statement
401         |
402         pidfile_statement
403         |
404         listen_statement
405         |
406         replication_statement
407         |
408         checksum_statement
409         |
410         compression_statement
411         |
412         timeout_statement
413         |
414         exec_statement
415         |
416         metaflush_statement
417         |
418         node_statement
419         |
420         resource_statement
421         ;
422
423 control_statement:      CONTROL STR
424         {
425                 switch (depth) {
426                 case 0:
427                         if (strlcpy(depth0_control, $2,
428                             sizeof(depth0_control)) >=
429                             sizeof(depth0_control)) {
430                                 pjdlog_error("control argument is too long.");
431                                 free($2);
432                                 return (1);
433                         }
434                         break;
435                 case 1:
436                         if (!mynode)
437                                 break;
438                         if (strlcpy(lconfig->hc_controladdr, $2,
439                             sizeof(lconfig->hc_controladdr)) >=
440                             sizeof(lconfig->hc_controladdr)) {
441                                 pjdlog_error("control argument is too long.");
442                                 free($2);
443                                 return (1);
444                         }
445                         break;
446                 default:
447                         PJDLOG_ABORT("control at wrong depth level");
448                 }
449                 free($2);
450         }
451         ;
452
453 pidfile_statement:      PIDFILE STR
454         {
455                 switch (depth) {
456                 case 0:
457                         if (strlcpy(depth0_pidfile, $2,
458                             sizeof(depth0_pidfile)) >=
459                             sizeof(depth0_pidfile)) {
460                                 pjdlog_error("pidfile argument is too long.");
461                                 free($2);
462                                 return (1);
463                         }
464                         break;
465                 case 1:
466                         if (!mynode)
467                                 break;
468                         if (strlcpy(lconfig->hc_pidfile, $2,
469                             sizeof(lconfig->hc_pidfile)) >=
470                             sizeof(lconfig->hc_pidfile)) {
471                                 pjdlog_error("pidfile argument is too long.");
472                                 free($2);
473                                 return (1);
474                         }
475                         break;
476                 default:
477                         PJDLOG_ABORT("pidfile at wrong depth level");
478                 }
479                 free($2);
480         }
481         ;
482
483 listen_statement:       LISTEN STR
484         {
485                 struct hastd_listen *lst;
486
487                 lst = calloc(1, sizeof(*lst));
488                 if (lst == NULL) {
489                         pjdlog_error("Unable to allocate memory for listen address.");
490                         free($2);
491                         return (1);
492                 }
493                 if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
494                     sizeof(lst->hl_addr)) {
495                         pjdlog_error("listen argument is too long.");
496                         free($2);
497                         free(lst);
498                         return (1);
499                 }
500                 switch (depth) {
501                 case 0:
502                         TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
503                         break;
504                 case 1:
505                         if (mynode)
506                                 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
507                         else
508                                 free(lst);
509                         break;
510                 default:
511                         PJDLOG_ABORT("listen at wrong depth level");
512                 }
513                 free($2);
514         }
515         ;
516
517 replication_statement:  REPLICATION replication_type
518         {
519                 switch (depth) {
520                 case 0:
521                         depth0_replication = $2;
522                         break;
523                 case 1:
524                         PJDLOG_ASSERT(curres != NULL);
525                         curres->hr_replication = $2;
526                         break;
527                 default:
528                         PJDLOG_ABORT("replication at wrong depth level");
529                 }
530         }
531         ;
532
533 replication_type:
534         FULLSYNC        { $$ = HAST_REPLICATION_FULLSYNC; }
535         |
536         MEMSYNC         { $$ = HAST_REPLICATION_MEMSYNC; }
537         |
538         ASYNC           { $$ = HAST_REPLICATION_ASYNC; }
539         ;
540
541 checksum_statement:     CHECKSUM checksum_type
542         {
543                 switch (depth) {
544                 case 0:
545                         depth0_checksum = $2;
546                         break;
547                 case 1:
548                         PJDLOG_ASSERT(curres != NULL);
549                         curres->hr_checksum = $2;
550                         break;
551                 default:
552                         PJDLOG_ABORT("checksum at wrong depth level");
553                 }
554         }
555         ;
556
557 checksum_type:
558         NONE            { $$ = HAST_CHECKSUM_NONE; }
559         |
560         CRC32           { $$ = HAST_CHECKSUM_CRC32; }
561         |
562         SHA256          { $$ = HAST_CHECKSUM_SHA256; }
563         ;
564
565 compression_statement:  COMPRESSION compression_type
566         {
567                 switch (depth) {
568                 case 0:
569                         depth0_compression = $2;
570                         break;
571                 case 1:
572                         PJDLOG_ASSERT(curres != NULL);
573                         curres->hr_compression = $2;
574                         break;
575                 default:
576                         PJDLOG_ABORT("compression at wrong depth level");
577                 }
578         }
579         ;
580
581 compression_type:
582         NONE            { $$ = HAST_COMPRESSION_NONE; }
583         |
584         HOLE            { $$ = HAST_COMPRESSION_HOLE; }
585         |
586         LZF             { $$ = HAST_COMPRESSION_LZF; }
587         ;
588
589 timeout_statement:      TIMEOUT NUM
590         {
591                 if ($2 <= 0) {
592                         pjdlog_error("Negative or zero timeout.");
593                         return (1);
594                 }
595                 switch (depth) {
596                 case 0:
597                         depth0_timeout = $2;
598                         break;
599                 case 1:
600                         PJDLOG_ASSERT(curres != NULL);
601                         curres->hr_timeout = $2;
602                         break;
603                 default:
604                         PJDLOG_ABORT("timeout at wrong depth level");
605                 }
606         }
607         ;
608
609 exec_statement:         EXEC STR
610         {
611                 switch (depth) {
612                 case 0:
613                         if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
614                             sizeof(depth0_exec)) {
615                                 pjdlog_error("Exec path is too long.");
616                                 free($2);
617                                 return (1);
618                         }
619                         break;
620                 case 1:
621                         PJDLOG_ASSERT(curres != NULL);
622                         if (strlcpy(curres->hr_exec, $2,
623                             sizeof(curres->hr_exec)) >=
624                             sizeof(curres->hr_exec)) {
625                                 pjdlog_error("Exec path is too long.");
626                                 free($2);
627                                 return (1);
628                         }
629                         break;
630                 default:
631                         PJDLOG_ABORT("exec at wrong depth level");
632                 }
633                 free($2);
634         }
635         ;
636
637 metaflush_statement:    METAFLUSH boolean
638         {
639                 switch (depth) {
640                 case 0:
641                         depth0_metaflush = $2;
642                         break;
643                 case 1:
644                         PJDLOG_ASSERT(curres != NULL);
645                         depth1_metaflush = $2;
646                         break;
647                 case 2:
648                         if (!mynode)
649                                 break;
650                         PJDLOG_ASSERT(curres != NULL);
651                         curres->hr_metaflush = $2;
652                         break;
653                 default:
654                         PJDLOG_ABORT("metaflush at wrong depth level");
655                 }
656         }
657         ;
658
659 boolean:
660         ON              { $$ = 1; }
661         |
662         OFF             { $$ = 0; }
663         ;
664
665 node_statement:         ON node_start OB node_entries CB
666         {
667                 mynode = false;
668         }
669         ;
670
671 node_start:     STR
672         {
673                 switch (isitme($1)) {
674                 case -1:
675                         free($1);
676                         return (1);
677                 case 0:
678                         break;
679                 case 1:
680                         mynode = true;
681                         break;
682                 default:
683                         PJDLOG_ABORT("invalid isitme() return value");
684                 }
685                 free($1);
686         }
687         ;
688
689 node_entries:
690         |
691         node_entries node_entry
692         ;
693
694 node_entry:
695         control_statement
696         |
697         pidfile_statement
698         |
699         listen_statement
700         ;
701
702 resource_statement:     RESOURCE resource_start OB resource_entries CB
703         {
704                 if (curres != NULL) {
705                         /*
706                          * There must be section for this node, at least with
707                          * remote address configuration.
708                          */
709                         if (!hadmynode) {
710                                 char *names;
711
712                                 if (node_names(&names) != 0)
713                                         return (1);
714                                 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
715                                     curres->hr_name, names);
716                                 return (1);
717                         }
718
719                         /*
720                          * Let's see if there are some resource-level settings
721                          * that we can use for node-level settings.
722                          */
723                         if (curres->hr_provname[0] == '\0' &&
724                             depth1_provname[0] != '\0') {
725                                 /*
726                                  * Provider name is not set at node-level,
727                                  * but is set at resource-level, use it.
728                                  */
729                                 strlcpy(curres->hr_provname, depth1_provname,
730                                     sizeof(curres->hr_provname));
731                         }
732                         if (curres->hr_localpath[0] == '\0' &&
733                             depth1_localpath[0] != '\0') {
734                                 /*
735                                  * Path to local provider is not set at
736                                  * node-level, but is set at resource-level,
737                                  * use it.
738                                  */
739                                 strlcpy(curres->hr_localpath, depth1_localpath,
740                                     sizeof(curres->hr_localpath));
741                         }
742                         if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
743                                 /*
744                                  * Metaflush is not set at node-level,
745                                  * but is set at resource-level, use it.
746                                  */
747                                 curres->hr_metaflush = depth1_metaflush;
748                         }
749
750                         /*
751                          * If provider name is not given, use resource name
752                          * as provider name.
753                          */
754                         if (curres->hr_provname[0] == '\0') {
755                                 strlcpy(curres->hr_provname, curres->hr_name,
756                                     sizeof(curres->hr_provname));
757                         }
758
759                         /*
760                          * Remote address has to be configured at this point.
761                          */
762                         if (curres->hr_remoteaddr[0] == '\0') {
763                                 pjdlog_error("Remote address not configured for resource %s.",
764                                     curres->hr_name);
765                                 return (1);
766                         }
767                         /*
768                          * Path to local provider has to be configured at this
769                          * point.
770                          */
771                         if (curres->hr_localpath[0] == '\0') {
772                                 pjdlog_error("Path to local component not configured for resource %s.",
773                                     curres->hr_name);
774                                 return (1);
775                         }
776
777                         /* Put it onto resource list. */
778                         TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
779                         curres = NULL;
780                 }
781         }
782         ;
783
784 resource_start: STR
785         {
786                 /* Check if there is no duplicate entry. */
787                 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
788                         if (strcmp(curres->hr_name, $1) == 0) {
789                                 pjdlog_error("Resource %s configured more than once.",
790                                     curres->hr_name);
791                                 free($1);
792                                 return (1);
793                         }
794                 }
795
796                 /*
797                  * Clear those, so we can tell if they were set at
798                  * resource-level or not.
799                  */
800                 depth1_provname[0] = '\0';
801                 depth1_localpath[0] = '\0';
802                 depth1_metaflush = -1;
803                 hadmynode = false;
804
805                 curres = calloc(1, sizeof(*curres));
806                 if (curres == NULL) {
807                         pjdlog_error("Unable to allocate memory for resource.");
808                         free($1);
809                         return (1);
810                 }
811                 if (strlcpy(curres->hr_name, $1,
812                     sizeof(curres->hr_name)) >=
813                     sizeof(curres->hr_name)) {
814                         pjdlog_error("Resource name is too long.");
815                         free(curres);
816                         free($1);
817                         return (1);
818                 }
819                 free($1);
820                 curres->hr_role = HAST_ROLE_INIT;
821                 curres->hr_previous_role = HAST_ROLE_INIT;
822                 curres->hr_replication = -1;
823                 curres->hr_checksum = -1;
824                 curres->hr_compression = -1;
825                 curres->hr_timeout = -1;
826                 curres->hr_exec[0] = '\0';
827                 curres->hr_provname[0] = '\0';
828                 curres->hr_localpath[0] = '\0';
829                 curres->hr_localfd = -1;
830                 curres->hr_localflush = true;
831                 curres->hr_metaflush = -1;
832                 curres->hr_remoteaddr[0] = '\0';
833                 curres->hr_sourceaddr[0] = '\0';
834                 curres->hr_ggateunit = -1;
835         }
836         ;
837
838 resource_entries:
839         |
840         resource_entries resource_entry
841         ;
842
843 resource_entry:
844         replication_statement
845         |
846         checksum_statement
847         |
848         compression_statement
849         |
850         timeout_statement
851         |
852         exec_statement
853         |
854         metaflush_statement
855         |
856         name_statement
857         |
858         local_statement
859         |
860         resource_node_statement
861         ;
862
863 name_statement:         NAME STR
864         {
865                 switch (depth) {
866                 case 1:
867                         if (strlcpy(depth1_provname, $2,
868                             sizeof(depth1_provname)) >=
869                             sizeof(depth1_provname)) {
870                                 pjdlog_error("name argument is too long.");
871                                 free($2);
872                                 return (1);
873                         }
874                         break;
875                 case 2:
876                         if (!mynode)
877                                 break;
878                         PJDLOG_ASSERT(curres != NULL);
879                         if (strlcpy(curres->hr_provname, $2,
880                             sizeof(curres->hr_provname)) >=
881                             sizeof(curres->hr_provname)) {
882                                 pjdlog_error("name argument is too long.");
883                                 free($2);
884                                 return (1);
885                         }
886                         break;
887                 default:
888                         PJDLOG_ABORT("name at wrong depth level");
889                 }
890                 free($2);
891         }
892         ;
893
894 local_statement:        LOCAL STR
895         {
896                 switch (depth) {
897                 case 1:
898                         if (strlcpy(depth1_localpath, $2,
899                             sizeof(depth1_localpath)) >=
900                             sizeof(depth1_localpath)) {
901                                 pjdlog_error("local argument is too long.");
902                                 free($2);
903                                 return (1);
904                         }
905                         break;
906                 case 2:
907                         if (!mynode)
908                                 break;
909                         PJDLOG_ASSERT(curres != NULL);
910                         if (strlcpy(curres->hr_localpath, $2,
911                             sizeof(curres->hr_localpath)) >=
912                             sizeof(curres->hr_localpath)) {
913                                 pjdlog_error("local argument is too long.");
914                                 free($2);
915                                 return (1);
916                         }
917                         break;
918                 default:
919                         PJDLOG_ABORT("local at wrong depth level");
920                 }
921                 free($2);
922         }
923         ;
924
925 resource_node_statement:ON resource_node_start OB resource_node_entries CB
926         {
927                 mynode = false;
928         }
929         ;
930
931 resource_node_start:    STR
932         {
933                 if (curres != NULL) {
934                         switch (isitme($1)) {
935                         case -1:
936                                 free($1);
937                                 return (1);
938                         case 0:
939                                 break;
940                         case 1:
941                                 mynode = hadmynode = true;
942                                 break;
943                         default:
944                                 PJDLOG_ABORT("invalid isitme() return value");
945                         }
946                 }
947                 free($1);
948         }
949         ;
950
951 resource_node_entries:
952         |
953         resource_node_entries resource_node_entry
954         ;
955
956 resource_node_entry:
957         name_statement
958         |
959         local_statement
960         |
961         remote_statement
962         |
963         source_statement
964         |
965         metaflush_statement
966         ;
967
968 remote_statement:       REMOTE remote_str
969         {
970                 PJDLOG_ASSERT(depth == 2);
971                 if (mynode) {
972                         PJDLOG_ASSERT(curres != NULL);
973                         if (strlcpy(curres->hr_remoteaddr, $2,
974                             sizeof(curres->hr_remoteaddr)) >=
975                             sizeof(curres->hr_remoteaddr)) {
976                                 pjdlog_error("remote argument is too long.");
977                                 free($2);
978                                 return (1);
979                         }
980                 }
981                 free($2);
982         }
983         ;
984
985 remote_str:
986         NONE            { $$ = strdup("none"); }
987         |
988         STR             { }
989         ;
990
991 source_statement:       SOURCE STR
992         {
993                 PJDLOG_ASSERT(depth == 2);
994                 if (mynode) {
995                         PJDLOG_ASSERT(curres != NULL);
996                         if (strlcpy(curres->hr_sourceaddr, $2,
997                             sizeof(curres->hr_sourceaddr)) >=
998                             sizeof(curres->hr_sourceaddr)) {
999                                 pjdlog_error("source argument is too long.");
1000                                 free($2);
1001                                 return (1);
1002                         }
1003                 }
1004                 free($2);
1005         }
1006         ;