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