1 %{ 2 /*- 3 * Copyright (c) 2009-2010 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <sys/param.h> /* MAXHOSTNAMELEN */ 34 #include <sys/queue.h> 35 #include <sys/sysctl.h> 36 37 #include <arpa/inet.h> 38 39 #include <assert.h> 40 #include <err.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <sysexits.h> 44 #include <unistd.h> 45 46 #include "hast.h" 47 48 extern int depth; 49 extern int lineno; 50 51 extern FILE *yyin; 52 extern char *yytext; 53 54 static struct hastd_config lconfig; 55 static struct hast_resource *curres; 56 static bool mynode; 57 58 static char depth0_control[HAST_ADDRSIZE]; 59 static char depth0_listen[HAST_ADDRSIZE]; 60 static int depth0_replication; 61 62 static char depth1_provname[PATH_MAX]; 63 static char depth1_localpath[PATH_MAX]; 64 65 static bool 66 isitme(const char *name) 67 { 68 char buf[MAXHOSTNAMELEN]; 69 char *pos; 70 size_t bufsize; 71 72 /* 73 * First check if the give name matches our full hostname. 74 */ 75 if (gethostname(buf, sizeof(buf)) < 0) 76 err(EX_OSERR, "gethostname() failed"); 77 if (strcmp(buf, name) == 0) 78 return (true); 79 80 /* 81 * Now check if it matches first part of the host name. 82 */ 83 pos = strchr(buf, '.'); 84 if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0) 85 return (true); 86 87 /* 88 * At the end check if name is equal to our host's UUID. 89 */ 90 bufsize = sizeof(buf); 91 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) 92 err(EX_OSERR, "sysctlbyname(kern.hostuuid) failed"); 93 if (strcasecmp(buf, name) == 0) 94 return (true); 95 96 /* 97 * Looks like this isn't about us. 98 */ 99 return (false); 100 } 101 102 void 103 yyerror(const char *str) 104 { 105 106 fprintf(stderr, "error at line %d near '%s': %s\n", 107 lineno, yytext, str); 108 } 109 110 struct hastd_config * 111 yy_config_parse(const char *config) 112 { 113 int ret; 114 115 curres = NULL; 116 mynode = false; 117 118 depth0_replication = HAST_REPLICATION_MEMSYNC; 119 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control)); 120 strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen)); 121 122 TAILQ_INIT(&lconfig.hc_resources); 123 124 yyin = fopen(config, "r"); 125 if (yyin == NULL) 126 err(EX_OSFILE, "cannot open configuration file %s", config); 127 ret = yyparse(); 128 fclose(yyin); 129 if (ret != 0) { 130 yy_config_free(&lconfig); 131 exit(EX_CONFIG); 132 } 133 134 /* 135 * Let's see if everything is set up. 136 */ 137 if (lconfig.hc_controladdr[0] == '\0') { 138 strlcpy(lconfig.hc_controladdr, depth0_control, 139 sizeof(lconfig.hc_controladdr)); 140 } 141 if (lconfig.hc_listenaddr[0] == '\0') { 142 strlcpy(lconfig.hc_listenaddr, depth0_listen, 143 sizeof(lconfig.hc_listenaddr)); 144 } 145 TAILQ_FOREACH(curres, &lconfig.hc_resources, hr_next) { 146 assert(curres->hr_provname[0] != '\0'); 147 assert(curres->hr_localpath[0] != '\0'); 148 assert(curres->hr_remoteaddr[0] != '\0'); 149 150 if (curres->hr_replication == -1) { 151 /* 152 * Replication is not set at resource-level. 153 * Use global or default setting. 154 */ 155 curres->hr_replication = depth0_replication; 156 } 157 } 158 159 return (&lconfig); 160 } 161 162 void 163 yy_config_free(struct hastd_config *config) 164 { 165 struct hast_resource *res; 166 167 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) { 168 TAILQ_REMOVE(&config->hc_resources, res, hr_next); 169 free(res); 170 } 171 } 172 %} 173 174 %token CONTROL LISTEN PORT REPLICATION EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON 175 %token FULLSYNC MEMSYNC ASYNC 176 %token NUM STR OB CB 177 178 %type <num> replication_type 179 180 %union 181 { 182 int num; 183 char *str; 184 } 185 186 %token <num> NUM 187 %token <str> STR 188 189 %% 190 191 statements: 192 | 193 statements statement 194 ; 195 196 statement: 197 control_statement 198 | 199 listen_statement 200 | 201 replication_statement 202 | 203 node_statement 204 | 205 resource_statement 206 ; 207 208 control_statement: CONTROL STR 209 { 210 switch (depth) { 211 case 0: 212 if (strlcpy(depth0_control, $2, 213 sizeof(depth0_control)) >= 214 sizeof(depth0_control)) { 215 errx(EX_CONFIG, "control argument too long"); 216 } 217 break; 218 case 1: 219 if (mynode) { 220 if (strlcpy(lconfig.hc_controladdr, $2, 221 sizeof(lconfig.hc_controladdr)) >= 222 sizeof(lconfig.hc_controladdr)) { 223 errx(EX_CONFIG, 224 "control argument too long"); 225 } 226 } 227 break; 228 default: 229 assert(!"control at wrong depth level"); 230 } 231 } 232 ; 233 234 listen_statement: LISTEN STR 235 { 236 switch (depth) { 237 case 0: 238 if (strlcpy(depth0_listen, $2, 239 sizeof(depth0_listen)) >= 240 sizeof(depth0_listen)) { 241 errx(EX_CONFIG, "listen argument too long"); 242 } 243 break; 244 case 1: 245 if (mynode) { 246 if (strlcpy(lconfig.hc_listenaddr, $2, 247 sizeof(lconfig.hc_listenaddr)) >= 248 sizeof(lconfig.hc_listenaddr)) { 249 errx(EX_CONFIG, 250 "listen argument too long"); 251 } 252 } 253 break; 254 default: 255 assert(!"listen at wrong depth level"); 256 } 257 } 258 ; 259 260 replication_statement: REPLICATION replication_type 261 { 262 switch (depth) { 263 case 0: 264 depth0_replication = $2; 265 break; 266 case 1: 267 if (curres != NULL) 268 curres->hr_replication = $2; 269 break; 270 default: 271 assert(!"replication at wrong depth level"); 272 } 273 } 274 ; 275 276 replication_type: 277 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 278 | 279 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 280 | 281 ASYNC { $$ = HAST_REPLICATION_ASYNC; } 282 ; 283 284 node_statement: ON node_start OB node_entries CB 285 { 286 mynode = false; 287 } 288 ; 289 290 node_start: STR 291 { 292 if (isitme($1)) 293 mynode = true; 294 } 295 ; 296 297 node_entries: 298 | 299 node_entries node_entry 300 ; 301 302 node_entry: 303 control_statement 304 | 305 listen_statement 306 ; 307 308 resource_statement: RESOURCE resource_start OB resource_entries CB 309 { 310 if (curres != NULL) { 311 /* 312 * Let's see there are some resource-level settings 313 * that we can use for node-level settings. 314 */ 315 if (curres->hr_provname[0] == '\0' && 316 depth1_provname[0] != '\0') { 317 /* 318 * Provider name is not set at node-level, 319 * but is set at resource-level, use it. 320 */ 321 strlcpy(curres->hr_provname, depth1_provname, 322 sizeof(curres->hr_provname)); 323 } 324 if (curres->hr_localpath[0] == '\0' && 325 depth1_localpath[0] != '\0') { 326 /* 327 * Path to local provider is not set at 328 * node-level, but is set at resource-level, 329 * use it. 330 */ 331 strlcpy(curres->hr_localpath, depth1_localpath, 332 sizeof(curres->hr_localpath)); 333 } 334 335 /* 336 * If provider name is not given, use resource name 337 * as provider name. 338 */ 339 if (curres->hr_provname[0] == '\0') { 340 strlcpy(curres->hr_provname, curres->hr_name, 341 sizeof(curres->hr_provname)); 342 } 343 344 /* 345 * Remote address has to be configured at this point. 346 */ 347 if (curres->hr_remoteaddr[0] == '\0') { 348 errx(EX_CONFIG, 349 "remote address not configured for resource %s", 350 curres->hr_name); 351 } 352 /* 353 * Path to local provider has to be configured at this 354 * point. 355 */ 356 if (curres->hr_localpath[0] == '\0') { 357 errx(EX_CONFIG, 358 "path local component not configured for resource %s", 359 curres->hr_name); 360 } 361 362 /* Put it onto resource list. */ 363 TAILQ_INSERT_TAIL(&lconfig.hc_resources, curres, hr_next); 364 curres = NULL; 365 } 366 } 367 ; 368 369 resource_start: STR 370 { 371 /* 372 * Clear those, so we can tell if they were set at 373 * resource-level or not. 374 */ 375 depth1_provname[0] = '\0'; 376 depth1_localpath[0] = '\0'; 377 378 curres = calloc(1, sizeof(*curres)); 379 if (curres == NULL) { 380 errx(EX_TEMPFAIL, 381 "cannot allocate memory for resource"); 382 } 383 if (strlcpy(curres->hr_name, $1, 384 sizeof(curres->hr_name)) >= 385 sizeof(curres->hr_name)) { 386 errx(EX_CONFIG, 387 "resource name (%s) too long", $1); 388 } 389 curres->hr_role = HAST_ROLE_INIT; 390 curres->hr_previous_role = HAST_ROLE_INIT; 391 curres->hr_replication = -1; 392 curres->hr_provname[0] = '\0'; 393 curres->hr_localpath[0] = '\0'; 394 curres->hr_localfd = -1; 395 curres->hr_remoteaddr[0] = '\0'; 396 curres->hr_ggateunit = -1; 397 } 398 ; 399 400 resource_entries: 401 | 402 resource_entries resource_entry 403 ; 404 405 resource_entry: 406 replication_statement 407 | 408 name_statement 409 | 410 local_statement 411 | 412 resource_node_statement 413 ; 414 415 name_statement: NAME STR 416 { 417 switch (depth) { 418 case 1: 419 if (strlcpy(depth1_provname, $2, 420 sizeof(depth1_provname)) >= 421 sizeof(depth1_provname)) { 422 errx(EX_CONFIG, "name argument too long"); 423 } 424 break; 425 case 2: 426 if (mynode) { 427 assert(curres != NULL); 428 if (strlcpy(curres->hr_provname, $2, 429 sizeof(curres->hr_provname)) >= 430 sizeof(curres->hr_provname)) { 431 errx(EX_CONFIG, 432 "name argument too long"); 433 } 434 } 435 break; 436 default: 437 assert(!"name at wrong depth level"); 438 } 439 } 440 ; 441 442 local_statement: LOCAL STR 443 { 444 switch (depth) { 445 case 1: 446 if (strlcpy(depth1_localpath, $2, 447 sizeof(depth1_localpath)) >= 448 sizeof(depth1_localpath)) { 449 errx(EX_CONFIG, "local argument too long"); 450 } 451 break; 452 case 2: 453 if (mynode) { 454 assert(curres != NULL); 455 if (strlcpy(curres->hr_localpath, $2, 456 sizeof(curres->hr_localpath)) >= 457 sizeof(curres->hr_localpath)) { 458 errx(EX_CONFIG, 459 "local argument too long"); 460 } 461 } 462 break; 463 default: 464 assert(!"local at wrong depth level"); 465 } 466 } 467 ; 468 469 resource_node_statement:ON resource_node_start OB resource_node_entries CB 470 { 471 mynode = false; 472 } 473 ; 474 475 resource_node_start: STR 476 { 477 if (curres != NULL && isitme($1)) 478 mynode = true; 479 } 480 ; 481 482 resource_node_entries: 483 | 484 resource_node_entries resource_node_entry 485 ; 486 487 resource_node_entry: 488 name_statement 489 | 490 local_statement 491 | 492 remote_statement 493 ; 494 495 remote_statement: REMOTE STR 496 { 497 assert(depth == 2); 498 if (mynode) { 499 assert(curres != NULL); 500 if (strlcpy(curres->hr_remoteaddr, $2, 501 sizeof(curres->hr_remoteaddr)) >= 502 sizeof(curres->hr_remoteaddr)) { 503 errx(EX_CONFIG, "remote argument too long"); 504 } 505 } 506 } 507 ; 508