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