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