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