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>
58 static struct hastd_config *lconfig;
59 static struct hast_resource *curres;
60 static bool mynode, hadmynode;
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;
74 static char depth1_provname[PATH_MAX];
75 static char depth1_localpath[PATH_MAX];
76 static int depth1_metaflush;
78 extern void yyrestart(FILE *);
81 isitme(const char *name)
83 char buf[MAXHOSTNAMELEN];
88 * First check if the given name matches our full hostname.
90 if (gethostname(buf, sizeof(buf)) < 0) {
91 pjdlog_errno(LOG_ERR, "gethostname() failed");
94 if (strcmp(buf, name) == 0)
98 * Now check if it matches first part of the host name.
100 pos = strchr(buf, '.');
101 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
102 strncmp(buf, name, pos - buf) == 0) {
107 * At the end check if name is equal to our host's UUID.
109 bufsize = sizeof(buf);
110 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
111 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
114 if (strcasecmp(buf, name) == 0)
118 * Looks like this isn't about us.
124 family_supported(int family)
128 sock = socket(family, SOCK_STREAM, 0);
129 if (sock == -1 && errno == EPROTONOSUPPORT)
137 node_names(char **namesp)
139 static char names[MAXHOSTNAMELEN * 3];
140 char buf[MAXHOSTNAMELEN];
144 if (gethostname(buf, sizeof(buf)) < 0) {
145 pjdlog_errno(LOG_ERR, "gethostname() failed");
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),
154 (void)strlcat(names, ", ", sizeof(names));
157 /* Full host name. */
158 (void)strlcat(names, buf, sizeof(names));
159 (void)strlcat(names, ", ", sizeof(names));
162 bufsize = sizeof(buf);
163 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
164 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
167 (void)strlcat(names, buf, sizeof(names));
175 yyerror(const char *str)
178 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
179 lineno, yytext, str);
182 struct hastd_config *
183 yy_config_parse(const char *config, bool exitonerror)
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;
206 lconfig = calloc(1, sizeof(*lconfig));
207 if (lconfig == NULL) {
208 pjdlog_error("Unable to allocate memory for configuration.");
214 TAILQ_INIT(&lconfig->hc_listen);
215 TAILQ_INIT(&lconfig->hc_resources);
217 yyin = fopen(config, "r");
219 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
221 yy_config_free(lconfig);
230 yy_config_free(lconfig);
237 * Let's see if everything is set up.
239 if (lconfig->hc_controladdr[0] == '\0') {
240 strlcpy(lconfig->hc_controladdr, depth0_control,
241 sizeof(lconfig->hc_controladdr));
243 if (lconfig->hc_pidfile[0] == '\0') {
244 strlcpy(lconfig->hc_pidfile, depth0_pidfile,
245 sizeof(lconfig->hc_pidfile));
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;
252 if (family_supported(AF_INET)) {
253 lst = calloc(1, sizeof(*lst));
255 pjdlog_error("Unable to allocate memory for listen address.");
256 yy_config_free(lconfig);
261 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
262 sizeof(lst->hl_addr));
263 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
266 "No IPv4 support in the kernel, not listening on IPv4 address.");
268 if (family_supported(AF_INET6)) {
269 lst = calloc(1, sizeof(*lst));
271 pjdlog_error("Unable to allocate memory for listen address.");
272 yy_config_free(lconfig);
277 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
278 sizeof(lst->hl_addr));
279 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
282 "No IPv6 support in the kernel, not listening on IPv6 address.");
284 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
285 pjdlog_error("No address to listen on.");
286 yy_config_free(lconfig);
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');
297 if (curres->hr_replication == -1) {
299 * Replication is not set at resource-level.
300 * Use global or default setting.
302 curres->hr_replication = depth0_replication;
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;
309 if (curres->hr_checksum == -1) {
311 * Checksum is not set at resource-level.
312 * Use global or default setting.
314 curres->hr_checksum = depth0_checksum;
316 if (curres->hr_compression == -1) {
318 * Compression is not set at resource-level.
319 * Use global or default setting.
321 curres->hr_compression = depth0_compression;
323 if (curres->hr_timeout == -1) {
325 * Timeout is not set at resource-level.
326 * Use global or default setting.
328 curres->hr_timeout = depth0_timeout;
330 if (curres->hr_exec[0] == '\0') {
332 * Exec is not set at resource-level.
333 * Use global or default setting.
335 strlcpy(curres->hr_exec, depth0_exec,
336 sizeof(curres->hr_exec));
338 if (curres->hr_metaflush == -1) {
340 * Metaflush is not set at resource-level.
341 * Use global or default setting.
343 curres->hr_metaflush = depth0_metaflush;
351 yy_config_free(struct hastd_config *config)
353 struct hastd_listen *lst;
354 struct hast_resource *res;
356 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
357 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
360 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
361 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
364 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
365 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
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
377 %type <str> remote_str
378 %type <num> replication_type
379 %type <num> checksum_type
380 %type <num> compression_type
406 replication_statement
410 compression_statement
423 control_statement: CONTROL STR
427 if (strlcpy(depth0_control, $2,
428 sizeof(depth0_control)) >=
429 sizeof(depth0_control)) {
430 pjdlog_error("control argument is too long.");
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.");
447 PJDLOG_ABORT("control at wrong depth level");
453 pidfile_statement: PIDFILE STR
457 if (strlcpy(depth0_pidfile, $2,
458 sizeof(depth0_pidfile)) >=
459 sizeof(depth0_pidfile)) {
460 pjdlog_error("pidfile argument is too long.");
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.");
477 PJDLOG_ABORT("pidfile at wrong depth level");
483 listen_statement: LISTEN STR
485 struct hastd_listen *lst;
487 lst = calloc(1, sizeof(*lst));
489 pjdlog_error("Unable to allocate memory for listen address.");
493 if (strlcpy(lst->hl_addr, $2, sizeof(lst->hl_addr)) >=
494 sizeof(lst->hl_addr)) {
495 pjdlog_error("listen argument is too long.");
502 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
506 TAILQ_INSERT_TAIL(&depth0_listen, lst, hl_next);
511 PJDLOG_ABORT("listen at wrong depth level");
517 replication_statement: REPLICATION replication_type
521 depth0_replication = $2;
524 PJDLOG_ASSERT(curres != NULL);
525 curres->hr_replication = $2;
528 PJDLOG_ABORT("replication at wrong depth level");
534 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; }
536 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; }
538 ASYNC { $$ = HAST_REPLICATION_ASYNC; }
541 checksum_statement: CHECKSUM checksum_type
545 depth0_checksum = $2;
548 PJDLOG_ASSERT(curres != NULL);
549 curres->hr_checksum = $2;
552 PJDLOG_ABORT("checksum at wrong depth level");
558 NONE { $$ = HAST_CHECKSUM_NONE; }
560 CRC32 { $$ = HAST_CHECKSUM_CRC32; }
562 SHA256 { $$ = HAST_CHECKSUM_SHA256; }
565 compression_statement: COMPRESSION compression_type
569 depth0_compression = $2;
572 PJDLOG_ASSERT(curres != NULL);
573 curres->hr_compression = $2;
576 PJDLOG_ABORT("compression at wrong depth level");
582 NONE { $$ = HAST_COMPRESSION_NONE; }
584 HOLE { $$ = HAST_COMPRESSION_HOLE; }
586 LZF { $$ = HAST_COMPRESSION_LZF; }
589 timeout_statement: TIMEOUT NUM
592 pjdlog_error("Negative or zero timeout.");
600 PJDLOG_ASSERT(curres != NULL);
601 curres->hr_timeout = $2;
604 PJDLOG_ABORT("timeout at wrong depth level");
609 exec_statement: EXEC STR
613 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
614 sizeof(depth0_exec)) {
615 pjdlog_error("Exec path is too long.");
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.");
631 PJDLOG_ABORT("exec at wrong depth level");
637 metaflush_statement: METAFLUSH boolean
641 depth0_metaflush = $2;
644 PJDLOG_ASSERT(curres != NULL);
645 depth1_metaflush = $2;
650 PJDLOG_ASSERT(curres != NULL);
651 curres->hr_metaflush = $2;
654 PJDLOG_ABORT("metaflush at wrong depth level");
665 node_statement: ON node_start OB node_entries CB
673 switch (isitme($1)) {
683 PJDLOG_ABORT("invalid isitme() return value");
691 node_entries node_entry
702 resource_statement: RESOURCE resource_start OB resource_entries CB
704 if (curres != NULL) {
706 * There must be section for this node, at least with
707 * remote address configuration.
712 if (node_names(&names) != 0)
714 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
715 curres->hr_name, names);
720 * Let's see if there are some resource-level settings
721 * that we can use for node-level settings.
723 if (curres->hr_provname[0] == '\0' &&
724 depth1_provname[0] != '\0') {
726 * Provider name is not set at node-level,
727 * but is set at resource-level, use it.
729 strlcpy(curres->hr_provname, depth1_provname,
730 sizeof(curres->hr_provname));
732 if (curres->hr_localpath[0] == '\0' &&
733 depth1_localpath[0] != '\0') {
735 * Path to local provider is not set at
736 * node-level, but is set at resource-level,
739 strlcpy(curres->hr_localpath, depth1_localpath,
740 sizeof(curres->hr_localpath));
742 if (curres->hr_metaflush == -1 && depth1_metaflush != -1) {
744 * Metaflush is not set at node-level,
745 * but is set at resource-level, use it.
747 curres->hr_metaflush = depth1_metaflush;
751 * If provider name is not given, use resource name
754 if (curres->hr_provname[0] == '\0') {
755 strlcpy(curres->hr_provname, curres->hr_name,
756 sizeof(curres->hr_provname));
760 * Remote address has to be configured at this point.
762 if (curres->hr_remoteaddr[0] == '\0') {
763 pjdlog_error("Remote address not configured for resource %s.",
768 * Path to local provider has to be configured at this
771 if (curres->hr_localpath[0] == '\0') {
772 pjdlog_error("Path to local component not configured for resource %s.",
777 /* Put it onto resource list. */
778 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
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.",
797 * Clear those, so we can tell if they were set at
798 * resource-level or not.
800 depth1_provname[0] = '\0';
801 depth1_localpath[0] = '\0';
802 depth1_metaflush = -1;
805 curres = calloc(1, sizeof(*curres));
806 if (curres == NULL) {
807 pjdlog_error("Unable to allocate memory for resource.");
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.");
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;
840 resource_entries resource_entry
844 replication_statement
848 compression_statement
860 resource_node_statement
863 name_statement: NAME STR
867 if (strlcpy(depth1_provname, $2,
868 sizeof(depth1_provname)) >=
869 sizeof(depth1_provname)) {
870 pjdlog_error("name argument is too long.");
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.");
888 PJDLOG_ABORT("name at wrong depth level");
894 local_statement: LOCAL STR
898 if (strlcpy(depth1_localpath, $2,
899 sizeof(depth1_localpath)) >=
900 sizeof(depth1_localpath)) {
901 pjdlog_error("local argument is too long.");
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.");
919 PJDLOG_ABORT("local at wrong depth level");
925 resource_node_statement:ON resource_node_start OB resource_node_entries CB
931 resource_node_start: STR
933 if (curres != NULL) {
934 switch (isitme($1)) {
941 mynode = hadmynode = true;
944 PJDLOG_ABORT("invalid isitme() return value");
951 resource_node_entries:
953 resource_node_entries resource_node_entry
968 remote_statement: REMOTE remote_str
970 PJDLOG_ASSERT(depth == 2);
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.");
986 NONE { $$ = strdup("none"); }
991 source_statement: SOURCE STR
993 PJDLOG_ASSERT(depth == 2);
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.");