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 <pjdlog.h> 47 48 #include "hast.h" 49 50 extern int depth; 51 extern int lineno; 52 53 extern FILE *yyin; 54 extern char *yytext; 55 56 static struct hastd_config *lconfig; 57 static struct hast_resource *curres; 58 static bool mynode, hadmynode; 59 60 static char depth0_control[HAST_ADDRSIZE]; 61 static char depth0_listen[HAST_ADDRSIZE]; 62 static int depth0_replication; 63 static int depth0_timeout; 64 static char depth0_exec[PATH_MAX]; 65 66 static char depth1_provname[PATH_MAX]; 67 static char depth1_localpath[PATH_MAX]; 68 69 extern void yyrestart(FILE *); 70 71 static int 72 isitme(const char *name) 73 { 74 char buf[MAXHOSTNAMELEN]; 75 char *pos; 76 size_t bufsize; 77 78 /* 79 * First check if the give name matches our full hostname. 80 */ 81 if (gethostname(buf, sizeof(buf)) < 0) { 82 pjdlog_errno(LOG_ERR, "gethostname() failed"); 83 return (-1); 84 } 85 if (strcmp(buf, name) == 0) 86 return (1); 87 88 /* 89 * Now check if it matches first part of the host name. 90 */ 91 pos = strchr(buf, '.'); 92 if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0) 93 return (1); 94 95 /* 96 * At the end check if name is equal to our host's UUID. 97 */ 98 bufsize = sizeof(buf); 99 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) { 100 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed"); 101 return (-1); 102 } 103 if (strcasecmp(buf, name) == 0) 104 return (1); 105 106 /* 107 * Looks like this isn't about us. 108 */ 109 return (0); 110 } 111 112 static int 113 node_names(char **namesp) 114 { 115 static char names[MAXHOSTNAMELEN * 3]; 116 char buf[MAXHOSTNAMELEN]; 117 char *pos; 118 size_t bufsize; 119 120 if (gethostname(buf, sizeof(buf)) < 0) { 121 pjdlog_errno(LOG_ERR, "gethostname() failed"); 122 return (-1); 123 } 124 125 /* First component of the host name. */ 126 pos = strchr(buf, '.'); 127 if (pos != NULL && pos != buf) { 128 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1), 129 sizeof(names))); 130 (void)strlcat(names, ", ", sizeof(names)); 131 } 132 133 /* Full host name. */ 134 (void)strlcat(names, buf, sizeof(names)); 135 (void)strlcat(names, ", ", sizeof(names)); 136 137 /* Host UUID. */ 138 bufsize = sizeof(buf); 139 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) { 140 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed"); 141 return (-1); 142 } 143 (void)strlcat(names, buf, sizeof(names)); 144 145 *namesp = names; 146 147 return (0); 148 } 149 150 void 151 yyerror(const char *str) 152 { 153 154 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s", 155 lineno, yytext, str); 156 } 157 158 struct hastd_config * 159 yy_config_parse(const char *config, bool exitonerror) 160 { 161 int ret; 162 163 curres = NULL; 164 mynode = false; 165 depth = 0; 166 lineno = 0; 167 168 depth0_timeout = HAST_TIMEOUT; 169 depth0_replication = HAST_REPLICATION_MEMSYNC; 170 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control)); 171 strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen)); 172 depth0_exec[0] = '\0'; 173 174 lconfig = calloc(1, sizeof(*lconfig)); 175 if (lconfig == NULL) { 176 pjdlog_error("Unable to allocate memory for configuration."); 177 if (exitonerror) 178 exit(EX_TEMPFAIL); 179 return (NULL); 180 } 181 182 TAILQ_INIT(&lconfig->hc_resources); 183 184 yyin = fopen(config, "r"); 185 if (yyin == NULL) { 186 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s", 187 config); 188 yy_config_free(lconfig); 189 if (exitonerror) 190 exit(EX_OSFILE); 191 return (NULL); 192 } 193 yyrestart(yyin); 194 ret = yyparse(); 195 fclose(yyin); 196 if (ret != 0) { 197 yy_config_free(lconfig); 198 if (exitonerror) 199 exit(EX_CONFIG); 200 return (NULL); 201 } 202 203 /* 204 * Let's see if everything is set up. 205 */ 206 if (lconfig->hc_controladdr[0] == '\0') { 207 strlcpy(lconfig->hc_controladdr, depth0_control, 208 sizeof(lconfig->hc_controladdr)); 209 } 210 if (lconfig->hc_listenaddr[0] == '\0') { 211 strlcpy(lconfig->hc_listenaddr, depth0_listen, 212 sizeof(lconfig->hc_listenaddr)); 213 } 214 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 215 assert(curres->hr_provname[0] != '\0'); 216 assert(curres->hr_localpath[0] != '\0'); 217 assert(curres->hr_remoteaddr[0] != '\0'); 218 219 if (curres->hr_replication == -1) { 220 /* 221 * Replication is not set at resource-level. 222 * Use global or default setting. 223 */ 224 curres->hr_replication = depth0_replication; 225 } 226 if (curres->hr_timeout == -1) { 227 /* 228 * Timeout is not set at resource-level. 229 * Use global or default setting. 230 */ 231 curres->hr_timeout = depth0_timeout; 232 } 233 if (curres->hr_exec[0] == '\0') { 234 /* 235 * Exec is not set at resource-level. 236 * Use global or default setting. 237 */ 238 strlcpy(curres->hr_exec, depth0_exec, 239 sizeof(curres->hr_exec)); 240 } 241 } 242 243 return (lconfig); 244 } 245 246 void 247 yy_config_free(struct hastd_config *config) 248 { 249 struct hast_resource *res; 250 251 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) { 252 TAILQ_REMOVE(&config->hc_resources, res, hr_next); 253 free(res); 254 } 255 free(config); 256 } 257 %} 258 259 %token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON 260 %token FULLSYNC MEMSYNC ASYNC 261 %token NUM STR OB CB 262 263 %type <num> replication_type 264 265 %union 266 { 267 int num; 268 char *str; 269 } 270 271 %token <num> NUM 272 %token <str> STR 273 274 %% 275 276 statements: 277 | 278 statements statement 279 ; 280 281 statement: 282 control_statement 283 | 284 listen_statement 285 | 286 replication_statement 287 | 288 timeout_statement 289 | 290 exec_statement 291 | 292 node_statement 293 | 294 resource_statement 295 ; 296 297 control_statement: CONTROL STR 298 { 299 switch (depth) { 300 case 0: 301 if (strlcpy(depth0_control, $2, 302 sizeof(depth0_control)) >= 303 sizeof(depth0_control)) { 304 pjdlog_error("control argument is too long."); 305 free($2); 306 return (1); 307 } 308 break; 309 case 1: 310 if (!mynode) 311 break; 312 if (strlcpy(lconfig->hc_controladdr, $2, 313 sizeof(lconfig->hc_controladdr)) >= 314 sizeof(lconfig->hc_controladdr)) { 315 pjdlog_error("control argument is too long."); 316 free($2); 317 return (1); 318 } 319 break; 320 default: 321 assert(!"control at wrong depth level"); 322 } 323 free($2); 324 } 325 ; 326 327 listen_statement: LISTEN STR 328 { 329 switch (depth) { 330 case 0: 331 if (strlcpy(depth0_listen, $2, 332 sizeof(depth0_listen)) >= 333 sizeof(depth0_listen)) { 334 pjdlog_error("listen argument is too long."); 335 free($2); 336 return (1); 337 } 338 break; 339 case 1: 340 if (!mynode) 341 break; 342 if (strlcpy(lconfig->hc_listenaddr, $2, 343 sizeof(lconfig->hc_listenaddr)) >= 344 sizeof(lconfig->hc_listenaddr)) { 345 pjdlog_error("listen argument is too long."); 346 free($2); 347 return (1); 348 } 349 break; 350 default: 351 assert(!"listen at wrong depth level"); 352 } 353 free($2); 354 } 355 ; 356 357 replication_statement: REPLICATION replication_type 358 { 359 switch (depth) { 360 case 0: 361 depth0_replication = $2; 362 break; 363 case 1: 364 if (curres != NULL) 365 curres->hr_replication = $2; 366 break; 367 default: 368 assert(!"replication at wrong depth level"); 369 } 370 } 371 ; 372 373 replication_type: 374 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 375 | 376 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 377 | 378 ASYNC { $$ = HAST_REPLICATION_ASYNC; } 379 ; 380 381 timeout_statement: TIMEOUT NUM 382 { 383 switch (depth) { 384 case 0: 385 depth0_timeout = $2; 386 break; 387 case 1: 388 if (curres != NULL) 389 curres->hr_timeout = $2; 390 break; 391 default: 392 assert(!"timeout at wrong depth level"); 393 } 394 } 395 ; 396 397 exec_statement: EXEC STR 398 { 399 switch (depth) { 400 case 0: 401 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 402 sizeof(depth0_exec)) { 403 pjdlog_error("Exec path is too long."); 404 free($2); 405 return (1); 406 } 407 break; 408 case 1: 409 if (curres == NULL) 410 break; 411 if (strlcpy(curres->hr_exec, $2, 412 sizeof(curres->hr_exec)) >= 413 sizeof(curres->hr_exec)) { 414 pjdlog_error("Exec path is too long."); 415 free($2); 416 return (1); 417 } 418 break; 419 default: 420 assert(!"exec at wrong depth level"); 421 } 422 free($2); 423 } 424 ; 425 426 node_statement: ON node_start OB node_entries CB 427 { 428 mynode = false; 429 } 430 ; 431 432 node_start: STR 433 { 434 switch (isitme($1)) { 435 case -1: 436 free($1); 437 return (1); 438 case 0: 439 break; 440 case 1: 441 mynode = true; 442 break; 443 default: 444 assert(!"invalid isitme() return value"); 445 } 446 free($1); 447 } 448 ; 449 450 node_entries: 451 | 452 node_entries node_entry 453 ; 454 455 node_entry: 456 control_statement 457 | 458 listen_statement 459 ; 460 461 resource_statement: RESOURCE resource_start OB resource_entries CB 462 { 463 if (curres != NULL) { 464 /* 465 * There must be section for this node, at least with 466 * remote address configuration. 467 */ 468 if (!hadmynode) { 469 char *names; 470 471 if (node_names(&names) != 0) 472 return (1); 473 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).", 474 curres->hr_name, names); 475 return (1); 476 } 477 478 /* 479 * Let's see there are some resource-level settings 480 * that we can use for node-level settings. 481 */ 482 if (curres->hr_provname[0] == '\0' && 483 depth1_provname[0] != '\0') { 484 /* 485 * Provider name is not set at node-level, 486 * but is set at resource-level, use it. 487 */ 488 strlcpy(curres->hr_provname, depth1_provname, 489 sizeof(curres->hr_provname)); 490 } 491 if (curres->hr_localpath[0] == '\0' && 492 depth1_localpath[0] != '\0') { 493 /* 494 * Path to local provider is not set at 495 * node-level, but is set at resource-level, 496 * use it. 497 */ 498 strlcpy(curres->hr_localpath, depth1_localpath, 499 sizeof(curres->hr_localpath)); 500 } 501 502 /* 503 * If provider name is not given, use resource name 504 * as provider name. 505 */ 506 if (curres->hr_provname[0] == '\0') { 507 strlcpy(curres->hr_provname, curres->hr_name, 508 sizeof(curres->hr_provname)); 509 } 510 511 /* 512 * Remote address has to be configured at this point. 513 */ 514 if (curres->hr_remoteaddr[0] == '\0') { 515 pjdlog_error("Remote address not configured for resource %s.", 516 curres->hr_name); 517 return (1); 518 } 519 /* 520 * Path to local provider has to be configured at this 521 * point. 522 */ 523 if (curres->hr_localpath[0] == '\0') { 524 pjdlog_error("Path to local component not configured for resource %s.", 525 curres->hr_name); 526 return (1); 527 } 528 529 /* Put it onto resource list. */ 530 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 531 curres = NULL; 532 } 533 } 534 ; 535 536 resource_start: STR 537 { 538 /* Check if there is no duplicate entry. */ 539 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 540 if (strcmp(curres->hr_name, $1) == 0) { 541 pjdlog_error("Resource %s configured more than once.", 542 curres->hr_name); 543 free($1); 544 return (1); 545 } 546 } 547 548 /* 549 * Clear those, so we can tell if they were set at 550 * resource-level or not. 551 */ 552 depth1_provname[0] = '\0'; 553 depth1_localpath[0] = '\0'; 554 hadmynode = false; 555 556 curres = calloc(1, sizeof(*curres)); 557 if (curres == NULL) { 558 pjdlog_error("Unable to allocate memory for resource."); 559 free($1); 560 return (1); 561 } 562 if (strlcpy(curres->hr_name, $1, 563 sizeof(curres->hr_name)) >= 564 sizeof(curres->hr_name)) { 565 pjdlog_error("Resource name is too long."); 566 free($1); 567 return (1); 568 } 569 free($1); 570 curres->hr_role = HAST_ROLE_INIT; 571 curres->hr_previous_role = HAST_ROLE_INIT; 572 curres->hr_replication = -1; 573 curres->hr_timeout = -1; 574 curres->hr_exec[0] = '\0'; 575 curres->hr_provname[0] = '\0'; 576 curres->hr_localpath[0] = '\0'; 577 curres->hr_localfd = -1; 578 curres->hr_remoteaddr[0] = '\0'; 579 curres->hr_ggateunit = -1; 580 } 581 ; 582 583 resource_entries: 584 | 585 resource_entries resource_entry 586 ; 587 588 resource_entry: 589 replication_statement 590 | 591 timeout_statement 592 | 593 exec_statement 594 | 595 name_statement 596 | 597 local_statement 598 | 599 resource_node_statement 600 ; 601 602 name_statement: NAME STR 603 { 604 switch (depth) { 605 case 1: 606 if (strlcpy(depth1_provname, $2, 607 sizeof(depth1_provname)) >= 608 sizeof(depth1_provname)) { 609 pjdlog_error("name argument is too long."); 610 free($2); 611 return (1); 612 } 613 break; 614 case 2: 615 if (!mynode) 616 break; 617 assert(curres != NULL); 618 if (strlcpy(curres->hr_provname, $2, 619 sizeof(curres->hr_provname)) >= 620 sizeof(curres->hr_provname)) { 621 pjdlog_error("name argument is too long."); 622 free($2); 623 return (1); 624 } 625 break; 626 default: 627 assert(!"name at wrong depth level"); 628 } 629 free($2); 630 } 631 ; 632 633 local_statement: LOCAL STR 634 { 635 switch (depth) { 636 case 1: 637 if (strlcpy(depth1_localpath, $2, 638 sizeof(depth1_localpath)) >= 639 sizeof(depth1_localpath)) { 640 pjdlog_error("local argument is too long."); 641 free($2); 642 return (1); 643 } 644 break; 645 case 2: 646 if (!mynode) 647 break; 648 assert(curres != NULL); 649 if (strlcpy(curres->hr_localpath, $2, 650 sizeof(curres->hr_localpath)) >= 651 sizeof(curres->hr_localpath)) { 652 pjdlog_error("local argument is too long."); 653 free($2); 654 return (1); 655 } 656 break; 657 default: 658 assert(!"local at wrong depth level"); 659 } 660 free($2); 661 } 662 ; 663 664 resource_node_statement:ON resource_node_start OB resource_node_entries CB 665 { 666 mynode = false; 667 } 668 ; 669 670 resource_node_start: STR 671 { 672 if (curres != NULL) { 673 switch (isitme($1)) { 674 case -1: 675 free($1); 676 return (1); 677 case 0: 678 break; 679 case 1: 680 mynode = hadmynode = true; 681 break; 682 default: 683 assert(!"invalid isitme() return value"); 684 } 685 } 686 free($1); 687 } 688 ; 689 690 resource_node_entries: 691 | 692 resource_node_entries resource_node_entry 693 ; 694 695 resource_node_entry: 696 name_statement 697 | 698 local_statement 699 | 700 remote_statement 701 ; 702 703 remote_statement: REMOTE STR 704 { 705 assert(depth == 2); 706 if (mynode) { 707 assert(curres != NULL); 708 if (strlcpy(curres->hr_remoteaddr, $2, 709 sizeof(curres->hr_remoteaddr)) >= 710 sizeof(curres->hr_remoteaddr)) { 711 pjdlog_error("remote argument is too long."); 712 free($2); 713 return (1); 714 } 715 } 716 free($2); 717 } 718 ; 719