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