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