parse.y revision 221632
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 221632 2011-05-08 09:31:17Z trociny $ 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 <num> replication_type 293%type <num> checksum_type 294%type <num> compression_type 295 296%union 297{ 298 int num; 299 char *str; 300} 301 302%token <num> NUM 303%token <str> STR 304 305%% 306 307statements: 308 | 309 statements statement 310 ; 311 312statement: 313 control_statement 314 | 315 listen_statement 316 | 317 replication_statement 318 | 319 checksum_statement 320 | 321 compression_statement 322 | 323 timeout_statement 324 | 325 exec_statement 326 | 327 node_statement 328 | 329 resource_statement 330 ; 331 332control_statement: CONTROL STR 333 { 334 switch (depth) { 335 case 0: 336 if (strlcpy(depth0_control, $2, 337 sizeof(depth0_control)) >= 338 sizeof(depth0_control)) { 339 pjdlog_error("control argument is too long."); 340 free($2); 341 return (1); 342 } 343 break; 344 case 1: 345 if (!mynode) 346 break; 347 if (strlcpy(lconfig->hc_controladdr, $2, 348 sizeof(lconfig->hc_controladdr)) >= 349 sizeof(lconfig->hc_controladdr)) { 350 pjdlog_error("control argument is too long."); 351 free($2); 352 return (1); 353 } 354 break; 355 default: 356 assert(!"control at wrong depth level"); 357 } 358 free($2); 359 } 360 ; 361 362listen_statement: LISTEN STR 363 { 364 switch (depth) { 365 case 0: 366 if (strlcpy(depth0_listen, $2, 367 sizeof(depth0_listen)) >= 368 sizeof(depth0_listen)) { 369 pjdlog_error("listen argument is too long."); 370 free($2); 371 return (1); 372 } 373 break; 374 case 1: 375 if (!mynode) 376 break; 377 if (strlcpy(lconfig->hc_listenaddr, $2, 378 sizeof(lconfig->hc_listenaddr)) >= 379 sizeof(lconfig->hc_listenaddr)) { 380 pjdlog_error("listen argument is too long."); 381 free($2); 382 return (1); 383 } 384 break; 385 default: 386 assert(!"listen at wrong depth level"); 387 } 388 free($2); 389 } 390 ; 391 392replication_statement: REPLICATION replication_type 393 { 394 switch (depth) { 395 case 0: 396 depth0_replication = $2; 397 break; 398 case 1: 399 if (curres != NULL) 400 curres->hr_replication = $2; 401 break; 402 default: 403 assert(!"replication at wrong depth level"); 404 } 405 } 406 ; 407 408replication_type: 409 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 410 | 411 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 412 | 413 ASYNC { $$ = HAST_REPLICATION_ASYNC; } 414 ; 415 416checksum_statement: CHECKSUM checksum_type 417 { 418 switch (depth) { 419 case 0: 420 depth0_checksum = $2; 421 break; 422 case 1: 423 if (curres != NULL) 424 curres->hr_checksum = $2; 425 break; 426 default: 427 assert(!"checksum at wrong depth level"); 428 } 429 } 430 ; 431 432checksum_type: 433 NONE { $$ = HAST_CHECKSUM_NONE; } 434 | 435 CRC32 { $$ = HAST_CHECKSUM_CRC32; } 436 | 437 SHA256 { $$ = HAST_CHECKSUM_SHA256; } 438 ; 439 440compression_statement: COMPRESSION compression_type 441 { 442 switch (depth) { 443 case 0: 444 depth0_compression = $2; 445 break; 446 case 1: 447 if (curres != NULL) 448 curres->hr_compression = $2; 449 break; 450 default: 451 assert(!"compression at wrong depth level"); 452 } 453 } 454 ; 455 456compression_type: 457 NONE { $$ = HAST_COMPRESSION_NONE; } 458 | 459 HOLE { $$ = HAST_COMPRESSION_HOLE; } 460 | 461 LZF { $$ = HAST_COMPRESSION_LZF; } 462 ; 463 464timeout_statement: TIMEOUT NUM 465 { 466 if ($2 <= 0) { 467 pjdlog_error("Negative or zero timeout."); 468 return (1); 469 } 470 switch (depth) { 471 case 0: 472 depth0_timeout = $2; 473 break; 474 case 1: 475 if (curres != NULL) 476 curres->hr_timeout = $2; 477 break; 478 default: 479 assert(!"timeout at wrong depth level"); 480 } 481 } 482 ; 483 484exec_statement: EXEC STR 485 { 486 switch (depth) { 487 case 0: 488 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 489 sizeof(depth0_exec)) { 490 pjdlog_error("Exec path is too long."); 491 free($2); 492 return (1); 493 } 494 break; 495 case 1: 496 if (curres == NULL) 497 break; 498 if (strlcpy(curres->hr_exec, $2, 499 sizeof(curres->hr_exec)) >= 500 sizeof(curres->hr_exec)) { 501 pjdlog_error("Exec path is too long."); 502 free($2); 503 return (1); 504 } 505 break; 506 default: 507 assert(!"exec at wrong depth level"); 508 } 509 free($2); 510 } 511 ; 512 513node_statement: ON node_start OB node_entries CB 514 { 515 mynode = false; 516 } 517 ; 518 519node_start: STR 520 { 521 switch (isitme($1)) { 522 case -1: 523 free($1); 524 return (1); 525 case 0: 526 break; 527 case 1: 528 mynode = true; 529 break; 530 default: 531 assert(!"invalid isitme() return value"); 532 } 533 free($1); 534 } 535 ; 536 537node_entries: 538 | 539 node_entries node_entry 540 ; 541 542node_entry: 543 control_statement 544 | 545 listen_statement 546 ; 547 548resource_statement: RESOURCE resource_start OB resource_entries CB 549 { 550 if (curres != NULL) { 551 /* 552 * There must be section for this node, at least with 553 * remote address configuration. 554 */ 555 if (!hadmynode) { 556 char *names; 557 558 if (node_names(&names) != 0) 559 return (1); 560 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).", 561 curres->hr_name, names); 562 return (1); 563 } 564 565 /* 566 * Let's see there are some resource-level settings 567 * that we can use for node-level settings. 568 */ 569 if (curres->hr_provname[0] == '\0' && 570 depth1_provname[0] != '\0') { 571 /* 572 * Provider name is not set at node-level, 573 * but is set at resource-level, use it. 574 */ 575 strlcpy(curres->hr_provname, depth1_provname, 576 sizeof(curres->hr_provname)); 577 } 578 if (curres->hr_localpath[0] == '\0' && 579 depth1_localpath[0] != '\0') { 580 /* 581 * Path to local provider is not set at 582 * node-level, but is set at resource-level, 583 * use it. 584 */ 585 strlcpy(curres->hr_localpath, depth1_localpath, 586 sizeof(curres->hr_localpath)); 587 } 588 589 /* 590 * If provider name is not given, use resource name 591 * as provider name. 592 */ 593 if (curres->hr_provname[0] == '\0') { 594 strlcpy(curres->hr_provname, curres->hr_name, 595 sizeof(curres->hr_provname)); 596 } 597 598 /* 599 * Remote address has to be configured at this point. 600 */ 601 if (curres->hr_remoteaddr[0] == '\0') { 602 pjdlog_error("Remote address not configured for resource %s.", 603 curres->hr_name); 604 return (1); 605 } 606 /* 607 * Path to local provider has to be configured at this 608 * point. 609 */ 610 if (curres->hr_localpath[0] == '\0') { 611 pjdlog_error("Path to local component not configured for resource %s.", 612 curres->hr_name); 613 return (1); 614 } 615 616 /* Put it onto resource list. */ 617 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 618 curres = NULL; 619 } 620 } 621 ; 622 623resource_start: STR 624 { 625 /* Check if there is no duplicate entry. */ 626 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 627 if (strcmp(curres->hr_name, $1) == 0) { 628 pjdlog_error("Resource %s configured more than once.", 629 curres->hr_name); 630 free($1); 631 return (1); 632 } 633 } 634 635 /* 636 * Clear those, so we can tell if they were set at 637 * resource-level or not. 638 */ 639 depth1_provname[0] = '\0'; 640 depth1_localpath[0] = '\0'; 641 hadmynode = false; 642 643 curres = calloc(1, sizeof(*curres)); 644 if (curres == NULL) { 645 pjdlog_error("Unable to allocate memory for resource."); 646 free($1); 647 return (1); 648 } 649 if (strlcpy(curres->hr_name, $1, 650 sizeof(curres->hr_name)) >= 651 sizeof(curres->hr_name)) { 652 pjdlog_error("Resource name is too long."); 653 free($1); 654 return (1); 655 } 656 free($1); 657 curres->hr_role = HAST_ROLE_INIT; 658 curres->hr_previous_role = HAST_ROLE_INIT; 659 curres->hr_replication = -1; 660 curres->hr_checksum = -1; 661 curres->hr_compression = -1; 662 curres->hr_timeout = -1; 663 curres->hr_exec[0] = '\0'; 664 curres->hr_provname[0] = '\0'; 665 curres->hr_localpath[0] = '\0'; 666 curres->hr_localfd = -1; 667 curres->hr_remoteaddr[0] = '\0'; 668 curres->hr_sourceaddr[0] = '\0'; 669 curres->hr_ggateunit = -1; 670 } 671 ; 672 673resource_entries: 674 | 675 resource_entries resource_entry 676 ; 677 678resource_entry: 679 replication_statement 680 | 681 checksum_statement 682 | 683 compression_statement 684 | 685 timeout_statement 686 | 687 exec_statement 688 | 689 name_statement 690 | 691 local_statement 692 | 693 resource_node_statement 694 ; 695 696name_statement: NAME STR 697 { 698 switch (depth) { 699 case 1: 700 if (strlcpy(depth1_provname, $2, 701 sizeof(depth1_provname)) >= 702 sizeof(depth1_provname)) { 703 pjdlog_error("name argument is too long."); 704 free($2); 705 return (1); 706 } 707 break; 708 case 2: 709 if (!mynode) 710 break; 711 assert(curres != NULL); 712 if (strlcpy(curres->hr_provname, $2, 713 sizeof(curres->hr_provname)) >= 714 sizeof(curres->hr_provname)) { 715 pjdlog_error("name argument is too long."); 716 free($2); 717 return (1); 718 } 719 break; 720 default: 721 assert(!"name at wrong depth level"); 722 } 723 free($2); 724 } 725 ; 726 727local_statement: LOCAL STR 728 { 729 switch (depth) { 730 case 1: 731 if (strlcpy(depth1_localpath, $2, 732 sizeof(depth1_localpath)) >= 733 sizeof(depth1_localpath)) { 734 pjdlog_error("local argument is too long."); 735 free($2); 736 return (1); 737 } 738 break; 739 case 2: 740 if (!mynode) 741 break; 742 assert(curres != NULL); 743 if (strlcpy(curres->hr_localpath, $2, 744 sizeof(curres->hr_localpath)) >= 745 sizeof(curres->hr_localpath)) { 746 pjdlog_error("local argument is too long."); 747 free($2); 748 return (1); 749 } 750 break; 751 default: 752 assert(!"local at wrong depth level"); 753 } 754 free($2); 755 } 756 ; 757 758resource_node_statement:ON resource_node_start OB resource_node_entries CB 759 { 760 mynode = false; 761 } 762 ; 763 764resource_node_start: STR 765 { 766 if (curres != NULL) { 767 switch (isitme($1)) { 768 case -1: 769 free($1); 770 return (1); 771 case 0: 772 break; 773 case 1: 774 mynode = hadmynode = true; 775 break; 776 default: 777 assert(!"invalid isitme() return value"); 778 } 779 } 780 free($1); 781 } 782 ; 783 784resource_node_entries: 785 | 786 resource_node_entries resource_node_entry 787 ; 788 789resource_node_entry: 790 name_statement 791 | 792 local_statement 793 | 794 remote_statement 795 | 796 source_statement 797 ; 798 799remote_statement: REMOTE STR 800 { 801 assert(depth == 2); 802 if (mynode) { 803 assert(curres != NULL); 804 if (strlcpy(curres->hr_remoteaddr, $2, 805 sizeof(curres->hr_remoteaddr)) >= 806 sizeof(curres->hr_remoteaddr)) { 807 pjdlog_error("remote argument is too long."); 808 free($2); 809 return (1); 810 } 811 } 812 free($2); 813 } 814 ; 815 816source_statement: SOURCE STR 817 { 818 assert(depth == 2); 819 if (mynode) { 820 assert(curres != NULL); 821 if (strlcpy(curres->hr_sourceaddr, $2, 822 sizeof(curres->hr_sourceaddr)) >= 823 sizeof(curres->hr_sourceaddr)) { 824 pjdlog_error("source argument is too long."); 825 free($2); 826 return (1); 827 } 828 } 829 free($2); 830 } 831 ; 832