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