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