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