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