3 * Copyright (c) 2009-2010 The FreeBSD Foundation
4 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
7 * This software was developed by Pawel Jakub Dawidek under sponsorship from
8 * the FreeBSD Foundation.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
34 #include <sys/param.h> /* MAXHOSTNAMELEN */
35 #include <sys/queue.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
39 #include <arpa/inet.h>
59 static struct hastd_config *lconfig;
60 static struct hast_resource *curres;
61 static bool mynode, hadmynode;
63 static char depth0_control[HAST_ADDRSIZE];
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];
73 static char depth1_provname[PATH_MAX];
74 static char depth1_localpath[PATH_MAX];
76 extern void yyrestart(FILE *);
79 isitme(const char *name)
81 char buf[MAXHOSTNAMELEN];
86 * First check if the give name matches our full hostname.
88 if (gethostname(buf, sizeof(buf)) < 0) {
89 pjdlog_errno(LOG_ERR, "gethostname() failed");
92 if (strcmp(buf, name) == 0)
96 * Now check if it matches first part of the host name.
98 pos = strchr(buf, '.');
99 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
100 strncmp(buf, name, pos - buf) == 0) {
105 * At the end check if name is equal to our host's UUID.
107 bufsize = sizeof(buf);
108 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
109 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
112 if (strcasecmp(buf, name) == 0)
116 * Looks like this isn't about us.
122 family_supported(int family)
126 sock = socket(family, SOCK_STREAM, 0);
127 if (sock == -1 && errno == EPROTONOSUPPORT)
135 node_names(char **namesp)
137 static char names[MAXHOSTNAMELEN * 3];
138 char buf[MAXHOSTNAMELEN];
142 if (gethostname(buf, sizeof(buf)) < 0) {
143 pjdlog_errno(LOG_ERR, "gethostname() failed");
147 /* First component of the host name. */
148 pos = strchr(buf, '.');
149 if (pos != NULL && pos != buf) {
150 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
152 (void)strlcat(names, ", ", sizeof(names));
155 /* Full host name. */
156 (void)strlcat(names, buf, sizeof(names));
157 (void)strlcat(names, ", ", sizeof(names));
160 bufsize = sizeof(buf);
161 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
162 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
165 (void)strlcat(names, buf, sizeof(names));
173 yyerror(const char *str)
176 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
177 lineno, yytext, str);
180 struct hastd_config *
181 yy_config_parse(const char *config, bool exitonerror)
190 depth0_timeout = HAST_TIMEOUT;
191 depth0_replication = HAST_REPLICATION_FULLSYNC;
192 depth0_checksum = HAST_CHECKSUM_NONE;
193 depth0_compression = HAST_COMPRESSION_HOLE;
194 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
195 TAILQ_INIT(&depth0_listen);
196 strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
197 sizeof(depth0_listen_tcp4));
198 strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
199 sizeof(depth0_listen_tcp6));
200 depth0_exec[0] = '\0';
202 lconfig = calloc(1, sizeof(*lconfig));
203 if (lconfig == NULL) {
204 pjdlog_error("Unable to allocate memory for configuration.");
210 TAILQ_INIT(&lconfig->hc_listen);
211 TAILQ_INIT(&lconfig->hc_resources);
213 yyin = fopen(config, "r");
215 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
217 yy_config_free(lconfig);
226 yy_config_free(lconfig);
233 * Let's see if everything is set up.
235 if (lconfig->hc_controladdr[0] == '\0') {
236 strlcpy(lconfig->hc_controladdr, depth0_control,
237 sizeof(lconfig->hc_controladdr));
239 if (!TAILQ_EMPTY(&depth0_listen))
240 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
241 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
242 struct hastd_listen *lst;
244 if (family_supported(AF_INET)) {
245 lst = calloc(1, sizeof(*lst));
247 pjdlog_error("Unable to allocate memory for listen address.");
248 yy_config_free(lconfig);
253 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
254 sizeof(lst->hl_addr));
255 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
258 "No IPv4 support in the kernel, not listening on IPv4 address.");
260 if (family_supported(AF_INET6)) {
261 lst = calloc(1, sizeof(*lst));
263 pjdlog_error("Unable to allocate memory for listen address.");
264 yy_config_free(lconfig);
269 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
270 sizeof(lst->hl_addr));
271 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
274 "No IPv6 support in the kernel, not listening on IPv6 address.");
276 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
277 pjdlog_error("No address to listen on.");
278 yy_config_free(lconfig);
284 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
285 assert(curres->hr_provname[0] != '\0');
286 assert(curres->hr_localpath[0] != '\0');
287 assert(curres->hr_remoteaddr[0] != '\0');
289 if (curres->hr_replication == -1) {
291 * Replication is not set at resource-level.
292 * Use global or default setting.
294 curres->hr_replication = depth0_replication;
296 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC ||
297 curres->hr_replication == HAST_REPLICATION_ASYNC) {
298 pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
299 curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
300 "memsync" : "async", "fullsync");
301 curres->hr_replication = HAST_REPLICATION_FULLSYNC;
303 if (curres->hr_checksum == -1) {
305 * Checksum is not set at resource-level.
306 * Use global or default setting.
308 curres->hr_checksum = depth0_checksum;
310 if (curres->hr_compression == -1) {
312 * Compression is not set at resource-level.
313 * Use global or default setting.
315 curres->hr_compression = depth0_compression;
317 if (curres->hr_timeout == -1) {
319 * Timeout is not set at resource-level.
320 * Use global or default setting.
322 curres->hr_timeout = depth0_timeout;
324 if (curres->hr_exec[0] == '\0') {
326 * Exec is not set at resource-level.
327 * Use global or default setting.
329 strlcpy(curres->hr_exec, depth0_exec,
330 sizeof(curres->hr_exec));
338 yy_config_free(struct hastd_config *config)
340 struct hastd_listen *lst;
341 struct hast_resource *res;
343 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
344 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
347 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
348 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
351 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
352 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
359 %token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
360 %token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
361 %token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
364 %type <str> remote_str
365 %type <num> replication_type
366 %type <num> checksum_type
367 %type <num> compression_type
390 replication_statement
394 compression_statement
405 control_statement: CONTROL STR
409 if (strlcpy(depth0_control, $2,
410 sizeof(depth0_control)) >=
411 sizeof(depth0_control)) {
412 pjdlog_error("control argument is too long.");
420 if (strlcpy(lconfig->hc_controladdr, $2,
421 sizeof(lconfig->hc_controladdr)) >=
422 sizeof(lconfig->hc_controladdr)) {
423 pjdlog_error("control argument is too long.");
429 assert(!"control at wrong depth level");
435 listen_statement: LISTEN STR
437 struct hastd_listen *lst;
439 lst = calloc(1, sizeof(*lst));
441 pjdlog_error("Unable to allocate memory for listen address.");
445 if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
446 sizeof(lst->hl_addr)) {
447 pjdlog_error("listen argument is too long.");
454 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
458 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
463 assert(!"listen at wrong depth level");
469 replication_statement: REPLICATION replication_type
473 depth0_replication = $2;
477 curres->hr_replication = $2;
480 assert(!"replication at wrong depth level");
486 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; }
488 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; }
490 ASYNC { $$ = HAST_REPLICATION_ASYNC; }
493 checksum_statement: CHECKSUM checksum_type
497 depth0_checksum = $2;
501 curres->hr_checksum = $2;
504 assert(!"checksum at wrong depth level");
510 NONE { $$ = HAST_CHECKSUM_NONE; }
512 CRC32 { $$ = HAST_CHECKSUM_CRC32; }
514 SHA256 { $$ = HAST_CHECKSUM_SHA256; }
517 compression_statement: COMPRESSION compression_type
521 depth0_compression = $2;
525 curres->hr_compression = $2;
528 assert(!"compression at wrong depth level");
534 NONE { $$ = HAST_COMPRESSION_NONE; }
536 HOLE { $$ = HAST_COMPRESSION_HOLE; }
538 LZF { $$ = HAST_COMPRESSION_LZF; }
541 timeout_statement: TIMEOUT NUM
544 pjdlog_error("Negative or zero timeout.");
553 curres->hr_timeout = $2;
556 assert(!"timeout at wrong depth level");
561 exec_statement: EXEC STR
565 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
566 sizeof(depth0_exec)) {
567 pjdlog_error("Exec path is too long.");
575 if (strlcpy(curres->hr_exec, $2,
576 sizeof(curres->hr_exec)) >=
577 sizeof(curres->hr_exec)) {
578 pjdlog_error("Exec path is too long.");
584 assert(!"exec at wrong depth level");
590 node_statement: ON node_start OB node_entries CB
598 switch (isitme($1)) {
608 assert(!"invalid isitme() return value");
616 node_entries node_entry
625 resource_statement: RESOURCE resource_start OB resource_entries CB
627 if (curres != NULL) {
629 * There must be section for this node, at least with
630 * remote address configuration.
635 if (node_names(&names) != 0)
637 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
638 curres->hr_name, names);
643 * Let's see there are some resource-level settings
644 * that we can use for node-level settings.
646 if (curres->hr_provname[0] == '\0' &&
647 depth1_provname[0] != '\0') {
649 * Provider name is not set at node-level,
650 * but is set at resource-level, use it.
652 strlcpy(curres->hr_provname, depth1_provname,
653 sizeof(curres->hr_provname));
655 if (curres->hr_localpath[0] == '\0' &&
656 depth1_localpath[0] != '\0') {
658 * Path to local provider is not set at
659 * node-level, but is set at resource-level,
662 strlcpy(curres->hr_localpath, depth1_localpath,
663 sizeof(curres->hr_localpath));
667 * If provider name is not given, use resource name
670 if (curres->hr_provname[0] == '\0') {
671 strlcpy(curres->hr_provname, curres->hr_name,
672 sizeof(curres->hr_provname));
676 * Remote address has to be configured at this point.
678 if (curres->hr_remoteaddr[0] == '\0') {
679 pjdlog_error("Remote address not configured for resource %s.",
684 * Path to local provider has to be configured at this
687 if (curres->hr_localpath[0] == '\0') {
688 pjdlog_error("Path to local component not configured for resource %s.",
693 /* Put it onto resource list. */
694 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
702 /* Check if there is no duplicate entry. */
703 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
704 if (strcmp(curres->hr_name, $1) == 0) {
705 pjdlog_error("Resource %s configured more than once.",
713 * Clear those, so we can tell if they were set at
714 * resource-level or not.
716 depth1_provname[0] = '\0';
717 depth1_localpath[0] = '\0';
720 curres = calloc(1, sizeof(*curres));
721 if (curres == NULL) {
722 pjdlog_error("Unable to allocate memory for resource.");
726 if (strlcpy(curres->hr_name, $1,
727 sizeof(curres->hr_name)) >=
728 sizeof(curres->hr_name)) {
729 pjdlog_error("Resource name is too long.");
734 curres->hr_role = HAST_ROLE_INIT;
735 curres->hr_previous_role = HAST_ROLE_INIT;
736 curres->hr_replication = -1;
737 curres->hr_checksum = -1;
738 curres->hr_compression = -1;
739 curres->hr_timeout = -1;
740 curres->hr_exec[0] = '\0';
741 curres->hr_provname[0] = '\0';
742 curres->hr_localpath[0] = '\0';
743 curres->hr_localfd = -1;
744 curres->hr_remoteaddr[0] = '\0';
745 curres->hr_sourceaddr[0] = '\0';
746 curres->hr_ggateunit = -1;
752 resource_entries resource_entry
756 replication_statement
760 compression_statement
770 resource_node_statement
773 name_statement: NAME STR
777 if (strlcpy(depth1_provname, $2,
778 sizeof(depth1_provname)) >=
779 sizeof(depth1_provname)) {
780 pjdlog_error("name argument is too long.");
788 assert(curres != NULL);
789 if (strlcpy(curres->hr_provname, $2,
790 sizeof(curres->hr_provname)) >=
791 sizeof(curres->hr_provname)) {
792 pjdlog_error("name argument is too long.");
798 assert(!"name at wrong depth level");
804 local_statement: LOCAL STR
808 if (strlcpy(depth1_localpath, $2,
809 sizeof(depth1_localpath)) >=
810 sizeof(depth1_localpath)) {
811 pjdlog_error("local argument is too long.");
819 assert(curres != NULL);
820 if (strlcpy(curres->hr_localpath, $2,
821 sizeof(curres->hr_localpath)) >=
822 sizeof(curres->hr_localpath)) {
823 pjdlog_error("local argument is too long.");
829 assert(!"local at wrong depth level");
835 resource_node_statement:ON resource_node_start OB resource_node_entries CB
841 resource_node_start: STR
843 if (curres != NULL) {
844 switch (isitme($1)) {
851 mynode = hadmynode = true;
854 assert(!"invalid isitme() return value");
861 resource_node_entries:
863 resource_node_entries resource_node_entry
876 remote_statement: REMOTE remote_str
880 assert(curres != NULL);
881 if (strlcpy(curres->hr_remoteaddr, $2,
882 sizeof(curres->hr_remoteaddr)) >=
883 sizeof(curres->hr_remoteaddr)) {
884 pjdlog_error("remote argument is too long.");
894 NONE { $$ = strdup("none"); }
899 source_statement: SOURCE STR
903 assert(curres != NULL);
904 if (strlcpy(curres->hr_sourceaddr, $2,
905 sizeof(curres->hr_sourceaddr)) >=
906 sizeof(curres->hr_sourceaddr)) {
907 pjdlog_error("source argument is too long.");