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