parse.y revision 216721
1%{ 2/*- 3 * Copyright (c) 2009-2010 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD: head/sbin/hastd/parse.y 216721 2010-12-26 19:07:58Z pjd $ 31 */ 32 33#include <sys/param.h> /* MAXHOSTNAMELEN */ 34#include <sys/queue.h> 35#include <sys/sysctl.h> 36 37#include <arpa/inet.h> 38 39#include <assert.h> 40#include <err.h> 41#include <stdio.h> 42#include <string.h> 43#include <sysexits.h> 44#include <unistd.h> 45 46#include <pjdlog.h> 47 48#include "hast.h" 49 50extern int depth; 51extern int lineno; 52 53extern FILE *yyin; 54extern char *yytext; 55 56static struct hastd_config *lconfig; 57static struct hast_resource *curres; 58static bool mynode, hadmynode; 59 60static char depth0_control[HAST_ADDRSIZE]; 61static char depth0_listen[HAST_ADDRSIZE]; 62static int depth0_replication; 63static int depth0_timeout; 64static char depth0_exec[PATH_MAX]; 65 66static char depth1_provname[PATH_MAX]; 67static char depth1_localpath[PATH_MAX]; 68 69extern void yyrestart(FILE *); 70 71static int 72isitme(const char *name) 73{ 74 char buf[MAXHOSTNAMELEN]; 75 char *pos; 76 size_t bufsize; 77 78 /* 79 * First check if the give name matches our full hostname. 80 */ 81 if (gethostname(buf, sizeof(buf)) < 0) { 82 pjdlog_errno(LOG_ERR, "gethostname() failed"); 83 return (-1); 84 } 85 if (strcmp(buf, name) == 0) 86 return (1); 87 88 /* 89 * Now check if it matches first part of the host name. 90 */ 91 pos = strchr(buf, '.'); 92 if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0) 93 return (1); 94 95 /* 96 * At the end check if name is equal to our host's UUID. 97 */ 98 bufsize = sizeof(buf); 99 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) { 100 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed"); 101 return (-1); 102 } 103 if (strcasecmp(buf, name) == 0) 104 return (1); 105 106 /* 107 * Looks like this isn't about us. 108 */ 109 return (0); 110} 111 112static int 113node_names(char **namesp) 114{ 115 static char names[MAXHOSTNAMELEN * 3]; 116 char buf[MAXHOSTNAMELEN]; 117 char *pos; 118 size_t bufsize; 119 120 if (gethostname(buf, sizeof(buf)) < 0) { 121 pjdlog_errno(LOG_ERR, "gethostname() failed"); 122 return (-1); 123 } 124 125 /* First component of the host name. */ 126 pos = strchr(buf, '.'); 127 if (pos != NULL && pos != buf) { 128 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1), 129 sizeof(names))); 130 (void)strlcat(names, ", ", sizeof(names)); 131 } 132 133 /* Full host name. */ 134 (void)strlcat(names, buf, sizeof(names)); 135 (void)strlcat(names, ", ", sizeof(names)); 136 137 /* Host UUID. */ 138 bufsize = sizeof(buf); 139 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) { 140 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed"); 141 return (-1); 142 } 143 (void)strlcat(names, buf, sizeof(names)); 144 145 *namesp = names; 146 147 return (0); 148} 149 150void 151yyerror(const char *str) 152{ 153 154 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s", 155 lineno, yytext, str); 156} 157 158struct hastd_config * 159yy_config_parse(const char *config, bool exitonerror) 160{ 161 int ret; 162 163 curres = NULL; 164 mynode = false; 165 depth = 0; 166 lineno = 0; 167 168 depth0_timeout = HAST_TIMEOUT; 169 depth0_replication = HAST_REPLICATION_MEMSYNC; 170 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control)); 171 strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen)); 172 depth0_exec[0] = '\0'; 173 174 lconfig = calloc(1, sizeof(*lconfig)); 175 if (lconfig == NULL) { 176 pjdlog_error("Unable to allocate memory for configuration."); 177 if (exitonerror) 178 exit(EX_TEMPFAIL); 179 return (NULL); 180 } 181 182 TAILQ_INIT(&lconfig->hc_resources); 183 184 yyin = fopen(config, "r"); 185 if (yyin == NULL) { 186 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s", 187 config); 188 yy_config_free(lconfig); 189 if (exitonerror) 190 exit(EX_OSFILE); 191 return (NULL); 192 } 193 yyrestart(yyin); 194 ret = yyparse(); 195 fclose(yyin); 196 if (ret != 0) { 197 yy_config_free(lconfig); 198 if (exitonerror) 199 exit(EX_CONFIG); 200 return (NULL); 201 } 202 203 /* 204 * Let's see if everything is set up. 205 */ 206 if (lconfig->hc_controladdr[0] == '\0') { 207 strlcpy(lconfig->hc_controladdr, depth0_control, 208 sizeof(lconfig->hc_controladdr)); 209 } 210 if (lconfig->hc_listenaddr[0] == '\0') { 211 strlcpy(lconfig->hc_listenaddr, depth0_listen, 212 sizeof(lconfig->hc_listenaddr)); 213 } 214 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 215 assert(curres->hr_provname[0] != '\0'); 216 assert(curres->hr_localpath[0] != '\0'); 217 assert(curres->hr_remoteaddr[0] != '\0'); 218 219 if (curres->hr_replication == -1) { 220 /* 221 * Replication is not set at resource-level. 222 * Use global or default setting. 223 */ 224 curres->hr_replication = depth0_replication; 225 } 226 if (curres->hr_timeout == -1) { 227 /* 228 * Timeout is not set at resource-level. 229 * Use global or default setting. 230 */ 231 curres->hr_timeout = depth0_timeout; 232 } 233 if (curres->hr_exec[0] == '\0') { 234 /* 235 * Exec is not set at resource-level. 236 * Use global or default setting. 237 */ 238 strlcpy(curres->hr_exec, depth0_exec, 239 sizeof(curres->hr_exec)); 240 } 241 } 242 243 return (lconfig); 244} 245 246void 247yy_config_free(struct hastd_config *config) 248{ 249 struct hast_resource *res; 250 251 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) { 252 TAILQ_REMOVE(&config->hc_resources, res, hr_next); 253 free(res); 254 } 255 free(config); 256} 257%} 258 259%token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON 260%token FULLSYNC MEMSYNC ASYNC 261%token NUM STR OB CB 262 263%type <num> replication_type 264 265%union 266{ 267 int num; 268 char *str; 269} 270 271%token <num> NUM 272%token <str> STR 273 274%% 275 276statements: 277 | 278 statements statement 279 ; 280 281statement: 282 control_statement 283 | 284 listen_statement 285 | 286 replication_statement 287 | 288 timeout_statement 289 | 290 exec_statement 291 | 292 node_statement 293 | 294 resource_statement 295 ; 296 297control_statement: CONTROL STR 298 { 299 switch (depth) { 300 case 0: 301 if (strlcpy(depth0_control, $2, 302 sizeof(depth0_control)) >= 303 sizeof(depth0_control)) { 304 pjdlog_error("control argument is too long."); 305 free($2); 306 return (1); 307 } 308 break; 309 case 1: 310 if (!mynode) 311 break; 312 if (strlcpy(lconfig->hc_controladdr, $2, 313 sizeof(lconfig->hc_controladdr)) >= 314 sizeof(lconfig->hc_controladdr)) { 315 pjdlog_error("control argument is too long."); 316 free($2); 317 return (1); 318 } 319 break; 320 default: 321 assert(!"control at wrong depth level"); 322 } 323 free($2); 324 } 325 ; 326 327listen_statement: LISTEN STR 328 { 329 switch (depth) { 330 case 0: 331 if (strlcpy(depth0_listen, $2, 332 sizeof(depth0_listen)) >= 333 sizeof(depth0_listen)) { 334 pjdlog_error("listen argument is too long."); 335 free($2); 336 return (1); 337 } 338 break; 339 case 1: 340 if (!mynode) 341 break; 342 if (strlcpy(lconfig->hc_listenaddr, $2, 343 sizeof(lconfig->hc_listenaddr)) >= 344 sizeof(lconfig->hc_listenaddr)) { 345 pjdlog_error("listen argument is too long."); 346 free($2); 347 return (1); 348 } 349 break; 350 default: 351 assert(!"listen at wrong depth level"); 352 } 353 free($2); 354 } 355 ; 356 357replication_statement: REPLICATION replication_type 358 { 359 switch (depth) { 360 case 0: 361 depth0_replication = $2; 362 break; 363 case 1: 364 if (curres != NULL) 365 curres->hr_replication = $2; 366 break; 367 default: 368 assert(!"replication at wrong depth level"); 369 } 370 } 371 ; 372 373replication_type: 374 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 375 | 376 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 377 | 378 ASYNC { $$ = HAST_REPLICATION_ASYNC; } 379 ; 380 381timeout_statement: TIMEOUT NUM 382 { 383 switch (depth) { 384 case 0: 385 depth0_timeout = $2; 386 break; 387 case 1: 388 if (curres != NULL) 389 curres->hr_timeout = $2; 390 break; 391 default: 392 assert(!"timeout at wrong depth level"); 393 } 394 } 395 ; 396 397exec_statement: EXEC STR 398 { 399 switch (depth) { 400 case 0: 401 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 402 sizeof(depth0_exec)) { 403 pjdlog_error("Exec path is too long."); 404 free($2); 405 return (1); 406 } 407 break; 408 case 1: 409 if (curres == NULL) 410 break; 411 if (strlcpy(curres->hr_exec, $2, 412 sizeof(curres->hr_exec)) >= 413 sizeof(curres->hr_exec)) { 414 pjdlog_error("Exec path is too long."); 415 free($2); 416 return (1); 417 } 418 break; 419 default: 420 assert(!"exec at wrong depth level"); 421 } 422 free($2); 423 } 424 ; 425 426node_statement: ON node_start OB node_entries CB 427 { 428 mynode = false; 429 } 430 ; 431 432node_start: STR 433 { 434 switch (isitme($1)) { 435 case -1: 436 free($1); 437 return (1); 438 case 0: 439 break; 440 case 1: 441 mynode = true; 442 break; 443 default: 444 assert(!"invalid isitme() return value"); 445 } 446 free($1); 447 } 448 ; 449 450node_entries: 451 | 452 node_entries node_entry 453 ; 454 455node_entry: 456 control_statement 457 | 458 listen_statement 459 ; 460 461resource_statement: RESOURCE resource_start OB resource_entries CB 462 { 463 if (curres != NULL) { 464 /* 465 * There must be section for this node, at least with 466 * remote address configuration. 467 */ 468 if (!hadmynode) { 469 char *names; 470 471 if (node_names(&names) != 0) 472 return (1); 473 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).", 474 curres->hr_name, names); 475 return (1); 476 } 477 478 /* 479 * Let's see there are some resource-level settings 480 * that we can use for node-level settings. 481 */ 482 if (curres->hr_provname[0] == '\0' && 483 depth1_provname[0] != '\0') { 484 /* 485 * Provider name is not set at node-level, 486 * but is set at resource-level, use it. 487 */ 488 strlcpy(curres->hr_provname, depth1_provname, 489 sizeof(curres->hr_provname)); 490 } 491 if (curres->hr_localpath[0] == '\0' && 492 depth1_localpath[0] != '\0') { 493 /* 494 * Path to local provider is not set at 495 * node-level, but is set at resource-level, 496 * use it. 497 */ 498 strlcpy(curres->hr_localpath, depth1_localpath, 499 sizeof(curres->hr_localpath)); 500 } 501 502 /* 503 * If provider name is not given, use resource name 504 * as provider name. 505 */ 506 if (curres->hr_provname[0] == '\0') { 507 strlcpy(curres->hr_provname, curres->hr_name, 508 sizeof(curres->hr_provname)); 509 } 510 511 /* 512 * Remote address has to be configured at this point. 513 */ 514 if (curres->hr_remoteaddr[0] == '\0') { 515 pjdlog_error("Remote address not configured for resource %s.", 516 curres->hr_name); 517 return (1); 518 } 519 /* 520 * Path to local provider has to be configured at this 521 * point. 522 */ 523 if (curres->hr_localpath[0] == '\0') { 524 pjdlog_error("Path to local component not configured for resource %s.", 525 curres->hr_name); 526 return (1); 527 } 528 529 /* Put it onto resource list. */ 530 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 531 curres = NULL; 532 } 533 } 534 ; 535 536resource_start: STR 537 { 538 /* 539 * Clear those, so we can tell if they were set at 540 * resource-level or not. 541 */ 542 depth1_provname[0] = '\0'; 543 depth1_localpath[0] = '\0'; 544 hadmynode = false; 545 546 curres = calloc(1, sizeof(*curres)); 547 if (curres == NULL) { 548 pjdlog_error("Unable to allocate memory for resource."); 549 free($1); 550 return (1); 551 } 552 if (strlcpy(curres->hr_name, $1, 553 sizeof(curres->hr_name)) >= 554 sizeof(curres->hr_name)) { 555 pjdlog_error("Resource name is too long."); 556 free($1); 557 return (1); 558 } 559 free($1); 560 curres->hr_role = HAST_ROLE_INIT; 561 curres->hr_previous_role = HAST_ROLE_INIT; 562 curres->hr_replication = -1; 563 curres->hr_timeout = -1; 564 curres->hr_exec[0] = '\0'; 565 curres->hr_provname[0] = '\0'; 566 curres->hr_localpath[0] = '\0'; 567 curres->hr_localfd = -1; 568 curres->hr_remoteaddr[0] = '\0'; 569 curres->hr_ggateunit = -1; 570 } 571 ; 572 573resource_entries: 574 | 575 resource_entries resource_entry 576 ; 577 578resource_entry: 579 replication_statement 580 | 581 timeout_statement 582 | 583 exec_statement 584 | 585 name_statement 586 | 587 local_statement 588 | 589 resource_node_statement 590 ; 591 592name_statement: NAME STR 593 { 594 switch (depth) { 595 case 1: 596 if (strlcpy(depth1_provname, $2, 597 sizeof(depth1_provname)) >= 598 sizeof(depth1_provname)) { 599 pjdlog_error("name argument is too long."); 600 free($2); 601 return (1); 602 } 603 break; 604 case 2: 605 if (!mynode) 606 break; 607 assert(curres != NULL); 608 if (strlcpy(curres->hr_provname, $2, 609 sizeof(curres->hr_provname)) >= 610 sizeof(curres->hr_provname)) { 611 pjdlog_error("name argument is too long."); 612 free($2); 613 return (1); 614 } 615 break; 616 default: 617 assert(!"name at wrong depth level"); 618 } 619 free($2); 620 } 621 ; 622 623local_statement: LOCAL STR 624 { 625 switch (depth) { 626 case 1: 627 if (strlcpy(depth1_localpath, $2, 628 sizeof(depth1_localpath)) >= 629 sizeof(depth1_localpath)) { 630 pjdlog_error("local argument is too long."); 631 free($2); 632 return (1); 633 } 634 break; 635 case 2: 636 if (!mynode) 637 break; 638 assert(curres != NULL); 639 if (strlcpy(curres->hr_localpath, $2, 640 sizeof(curres->hr_localpath)) >= 641 sizeof(curres->hr_localpath)) { 642 pjdlog_error("local argument is too long."); 643 free($2); 644 return (1); 645 } 646 break; 647 default: 648 assert(!"local at wrong depth level"); 649 } 650 free($2); 651 } 652 ; 653 654resource_node_statement:ON resource_node_start OB resource_node_entries CB 655 { 656 mynode = false; 657 } 658 ; 659 660resource_node_start: STR 661 { 662 if (curres != NULL) { 663 switch (isitme($1)) { 664 case -1: 665 free($1); 666 return (1); 667 case 0: 668 break; 669 case 1: 670 mynode = hadmynode = true; 671 break; 672 default: 673 assert(!"invalid isitme() return value"); 674 } 675 } 676 free($1); 677 } 678 ; 679 680resource_node_entries: 681 | 682 resource_node_entries resource_node_entry 683 ; 684 685resource_node_entry: 686 name_statement 687 | 688 local_statement 689 | 690 remote_statement 691 ; 692 693remote_statement: REMOTE STR 694 { 695 assert(depth == 2); 696 if (mynode) { 697 assert(curres != NULL); 698 if (strlcpy(curres->hr_remoteaddr, $2, 699 sizeof(curres->hr_remoteaddr)) >= 700 sizeof(curres->hr_remoteaddr)) { 701 pjdlog_error("remote argument is too long."); 702 free($2); 703 return (1); 704 } 705 } 706 free($2); 707 } 708 ; 709