parse.y revision 220889
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 220889 2011-04-20 16:36:59Z 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 if ($2 <= 0) { 465 pjdlog_error("Negative or zero timeout."); 466 return (1); 467 } 468 switch (depth) { 469 case 0: 470 depth0_timeout = $2; 471 break; 472 case 1: 473 if (curres != NULL) 474 curres->hr_timeout = $2; 475 break; 476 default: 477 assert(!"timeout at wrong depth level"); 478 } 479 } 480 ; 481 482exec_statement: EXEC STR 483 { 484 switch (depth) { 485 case 0: 486 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 487 sizeof(depth0_exec)) { 488 pjdlog_error("Exec path is too long."); 489 free($2); 490 return (1); 491 } 492 break; 493 case 1: 494 if (curres == NULL) 495 break; 496 if (strlcpy(curres->hr_exec, $2, 497 sizeof(curres->hr_exec)) >= 498 sizeof(curres->hr_exec)) { 499 pjdlog_error("Exec path is too long."); 500 free($2); 501 return (1); 502 } 503 break; 504 default: 505 assert(!"exec at wrong depth level"); 506 } 507 free($2); 508 } 509 ; 510 511node_statement: ON node_start OB node_entries CB 512 { 513 mynode = false; 514 } 515 ; 516 517node_start: STR 518 { 519 switch (isitme($1)) { 520 case -1: 521 free($1); 522 return (1); 523 case 0: 524 break; 525 case 1: 526 mynode = true; 527 break; 528 default: 529 assert(!"invalid isitme() return value"); 530 } 531 free($1); 532 } 533 ; 534 535node_entries: 536 | 537 node_entries node_entry 538 ; 539 540node_entry: 541 control_statement 542 | 543 listen_statement 544 ; 545 546resource_statement: RESOURCE resource_start OB resource_entries CB 547 { 548 if (curres != NULL) { 549 /* 550 * There must be section for this node, at least with 551 * remote address configuration. 552 */ 553 if (!hadmynode) { 554 char *names; 555 556 if (node_names(&names) != 0) 557 return (1); 558 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).", 559 curres->hr_name, names); 560 return (1); 561 } 562 563 /* 564 * Let's see there are some resource-level settings 565 * that we can use for node-level settings. 566 */ 567 if (curres->hr_provname[0] == '\0' && 568 depth1_provname[0] != '\0') { 569 /* 570 * Provider name is not set at node-level, 571 * but is set at resource-level, use it. 572 */ 573 strlcpy(curres->hr_provname, depth1_provname, 574 sizeof(curres->hr_provname)); 575 } 576 if (curres->hr_localpath[0] == '\0' && 577 depth1_localpath[0] != '\0') { 578 /* 579 * Path to local provider is not set at 580 * node-level, but is set at resource-level, 581 * use it. 582 */ 583 strlcpy(curres->hr_localpath, depth1_localpath, 584 sizeof(curres->hr_localpath)); 585 } 586 587 /* 588 * If provider name is not given, use resource name 589 * as provider name. 590 */ 591 if (curres->hr_provname[0] == '\0') { 592 strlcpy(curres->hr_provname, curres->hr_name, 593 sizeof(curres->hr_provname)); 594 } 595 596 /* 597 * Remote address has to be configured at this point. 598 */ 599 if (curres->hr_remoteaddr[0] == '\0') { 600 pjdlog_error("Remote address not configured for resource %s.", 601 curres->hr_name); 602 return (1); 603 } 604 /* 605 * Path to local provider has to be configured at this 606 * point. 607 */ 608 if (curres->hr_localpath[0] == '\0') { 609 pjdlog_error("Path to local component not configured for resource %s.", 610 curres->hr_name); 611 return (1); 612 } 613 614 /* Put it onto resource list. */ 615 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 616 curres = NULL; 617 } 618 } 619 ; 620 621resource_start: STR 622 { 623 /* Check if there is no duplicate entry. */ 624 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 625 if (strcmp(curres->hr_name, $1) == 0) { 626 pjdlog_error("Resource %s configured more than once.", 627 curres->hr_name); 628 free($1); 629 return (1); 630 } 631 } 632 633 /* 634 * Clear those, so we can tell if they were set at 635 * resource-level or not. 636 */ 637 depth1_provname[0] = '\0'; 638 depth1_localpath[0] = '\0'; 639 hadmynode = false; 640 641 curres = calloc(1, sizeof(*curres)); 642 if (curres == NULL) { 643 pjdlog_error("Unable to allocate memory for resource."); 644 free($1); 645 return (1); 646 } 647 if (strlcpy(curres->hr_name, $1, 648 sizeof(curres->hr_name)) >= 649 sizeof(curres->hr_name)) { 650 pjdlog_error("Resource name is too long."); 651 free($1); 652 return (1); 653 } 654 free($1); 655 curres->hr_role = HAST_ROLE_INIT; 656 curres->hr_previous_role = HAST_ROLE_INIT; 657 curres->hr_replication = -1; 658 curres->hr_checksum = -1; 659 curres->hr_compression = -1; 660 curres->hr_timeout = -1; 661 curres->hr_exec[0] = '\0'; 662 curres->hr_provname[0] = '\0'; 663 curres->hr_localpath[0] = '\0'; 664 curres->hr_localfd = -1; 665 curres->hr_remoteaddr[0] = '\0'; 666 curres->hr_sourceaddr[0] = '\0'; 667 curres->hr_ggateunit = -1; 668 } 669 ; 670 671resource_entries: 672 | 673 resource_entries resource_entry 674 ; 675 676resource_entry: 677 replication_statement 678 | 679 checksum_statement 680 | 681 compression_statement 682 | 683 timeout_statement 684 | 685 exec_statement 686 | 687 name_statement 688 | 689 local_statement 690 | 691 resource_node_statement 692 ; 693 694name_statement: NAME STR 695 { 696 switch (depth) { 697 case 1: 698 if (strlcpy(depth1_provname, $2, 699 sizeof(depth1_provname)) >= 700 sizeof(depth1_provname)) { 701 pjdlog_error("name argument is too long."); 702 free($2); 703 return (1); 704 } 705 break; 706 case 2: 707 if (!mynode) 708 break; 709 assert(curres != NULL); 710 if (strlcpy(curres->hr_provname, $2, 711 sizeof(curres->hr_provname)) >= 712 sizeof(curres->hr_provname)) { 713 pjdlog_error("name argument is too long."); 714 free($2); 715 return (1); 716 } 717 break; 718 default: 719 assert(!"name at wrong depth level"); 720 } 721 free($2); 722 } 723 ; 724 725local_statement: LOCAL STR 726 { 727 switch (depth) { 728 case 1: 729 if (strlcpy(depth1_localpath, $2, 730 sizeof(depth1_localpath)) >= 731 sizeof(depth1_localpath)) { 732 pjdlog_error("local argument is too long."); 733 free($2); 734 return (1); 735 } 736 break; 737 case 2: 738 if (!mynode) 739 break; 740 assert(curres != NULL); 741 if (strlcpy(curres->hr_localpath, $2, 742 sizeof(curres->hr_localpath)) >= 743 sizeof(curres->hr_localpath)) { 744 pjdlog_error("local argument is too long."); 745 free($2); 746 return (1); 747 } 748 break; 749 default: 750 assert(!"local at wrong depth level"); 751 } 752 free($2); 753 } 754 ; 755 756resource_node_statement:ON resource_node_start OB resource_node_entries CB 757 { 758 mynode = false; 759 } 760 ; 761 762resource_node_start: STR 763 { 764 if (curres != NULL) { 765 switch (isitme($1)) { 766 case -1: 767 free($1); 768 return (1); 769 case 0: 770 break; 771 case 1: 772 mynode = hadmynode = true; 773 break; 774 default: 775 assert(!"invalid isitme() return value"); 776 } 777 } 778 free($1); 779 } 780 ; 781 782resource_node_entries: 783 | 784 resource_node_entries resource_node_entry 785 ; 786 787resource_node_entry: 788 name_statement 789 | 790 local_statement 791 | 792 remote_statement 793 | 794 source_statement 795 ; 796 797remote_statement: REMOTE STR 798 { 799 assert(depth == 2); 800 if (mynode) { 801 assert(curres != NULL); 802 if (strlcpy(curres->hr_remoteaddr, $2, 803 sizeof(curres->hr_remoteaddr)) >= 804 sizeof(curres->hr_remoteaddr)) { 805 pjdlog_error("remote argument is too long."); 806 free($2); 807 return (1); 808 } 809 } 810 free($2); 811 } 812 ; 813 814source_statement: SOURCE STR 815 { 816 assert(depth == 2); 817 if (mynode) { 818 assert(curres != NULL); 819 if (strlcpy(curres->hr_sourceaddr, $2, 820 sizeof(curres->hr_sourceaddr)) >= 821 sizeof(curres->hr_sourceaddr)) { 822 pjdlog_error("source argument is too long."); 823 free($2); 824 return (1); 825 } 826 } 827 free($2); 828 } 829 ; 830