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$ 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 50 extern int depth; 51 extern int lineno; 52 53 extern FILE *yyin; 54 extern char *yytext; 55 56 static struct hastd_config *lconfig; 57 static struct hast_resource *curres; 58 static bool mynode; 59 60 static char depth0_control[HAST_ADDRSIZE]; 61 static char depth0_listen[HAST_ADDRSIZE]; 62 static int depth0_replication; 63 static int depth0_timeout; 64 static char depth0_exec[PATH_MAX]; 65 66 static char depth1_provname[PATH_MAX]; 67 static char depth1_localpath[PATH_MAX]; 68 69 extern void yyrestart(FILE *); 70 71 static int 72 isitme(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 112 void 113 yyerror(const char *str) 114 { 115 116 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s", 117 lineno, yytext, str); 118 } 119 120 struct hastd_config * 121 yy_config_parse(const char *config, bool exitonerror) 122 { 123 int ret; 124 125 curres = NULL; 126 mynode = false; 127 depth = 0; 128 lineno = 0; 129 130 depth0_timeout = HAST_TIMEOUT; 131 depth0_replication = HAST_REPLICATION_MEMSYNC; 132 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control)); 133 strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen)); 134 depth0_exec[0] = '\0'; 135 136 lconfig = calloc(1, sizeof(*lconfig)); 137 if (lconfig == NULL) { 138 pjdlog_error("Unable to allocate memory for configuration."); 139 if (exitonerror) 140 exit(EX_TEMPFAIL); 141 return (NULL); 142 } 143 144 TAILQ_INIT(&lconfig->hc_resources); 145 146 yyin = fopen(config, "r"); 147 if (yyin == NULL) { 148 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s", 149 config); 150 yy_config_free(lconfig); 151 if (exitonerror) 152 exit(EX_OSFILE); 153 return (NULL); 154 } 155 yyrestart(yyin); 156 ret = yyparse(); 157 fclose(yyin); 158 if (ret != 0) { 159 yy_config_free(lconfig); 160 if (exitonerror) 161 exit(EX_CONFIG); 162 return (NULL); 163 } 164 165 /* 166 * Let's see if everything is set up. 167 */ 168 if (lconfig->hc_controladdr[0] == '\0') { 169 strlcpy(lconfig->hc_controladdr, depth0_control, 170 sizeof(lconfig->hc_controladdr)); 171 } 172 if (lconfig->hc_listenaddr[0] == '\0') { 173 strlcpy(lconfig->hc_listenaddr, depth0_listen, 174 sizeof(lconfig->hc_listenaddr)); 175 } 176 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 177 assert(curres->hr_provname[0] != '\0'); 178 assert(curres->hr_localpath[0] != '\0'); 179 assert(curres->hr_remoteaddr[0] != '\0'); 180 181 if (curres->hr_replication == -1) { 182 /* 183 * Replication is not set at resource-level. 184 * Use global or default setting. 185 */ 186 curres->hr_replication = depth0_replication; 187 } 188 if (curres->hr_timeout == -1) { 189 /* 190 * Timeout is not set at resource-level. 191 * Use global or default setting. 192 */ 193 curres->hr_timeout = depth0_timeout; 194 } 195 if (curres->hr_exec[0] == '\0') { 196 /* 197 * Exec is not set at resource-level. 198 * Use global or default setting. 199 */ 200 strlcpy(curres->hr_exec, depth0_exec, 201 sizeof(curres->hr_exec)); 202 } 203 } 204 205 return (lconfig); 206 } 207 208 void 209 yy_config_free(struct hastd_config *config) 210 { 211 struct hast_resource *res; 212 213 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) { 214 TAILQ_REMOVE(&config->hc_resources, res, hr_next); 215 free(res); 216 } 217 free(config); 218 } 219 %} 220 221 %token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON 222 %token FULLSYNC MEMSYNC ASYNC 223 %token NUM STR OB CB 224 225 %type <num> replication_type 226 227 %union 228 { 229 int num; 230 char *str; 231 } 232 233 %token <num> NUM 234 %token <str> STR 235 236 %% 237 238 statements: 239 | 240 statements statement 241 ; 242 243 statement: 244 control_statement 245 | 246 listen_statement 247 | 248 replication_statement 249 | 250 timeout_statement 251 | 252 exec_statement 253 | 254 node_statement 255 | 256 resource_statement 257 ; 258 259 control_statement: CONTROL STR 260 { 261 switch (depth) { 262 case 0: 263 if (strlcpy(depth0_control, $2, 264 sizeof(depth0_control)) >= 265 sizeof(depth0_control)) { 266 pjdlog_error("control argument is too long."); 267 free($2); 268 return (1); 269 } 270 break; 271 case 1: 272 if (!mynode) 273 break; 274 if (strlcpy(lconfig->hc_controladdr, $2, 275 sizeof(lconfig->hc_controladdr)) >= 276 sizeof(lconfig->hc_controladdr)) { 277 pjdlog_error("control argument is too long."); 278 free($2); 279 return (1); 280 } 281 break; 282 default: 283 assert(!"control at wrong depth level"); 284 } 285 free($2); 286 } 287 ; 288 289 listen_statement: LISTEN STR 290 { 291 switch (depth) { 292 case 0: 293 if (strlcpy(depth0_listen, $2, 294 sizeof(depth0_listen)) >= 295 sizeof(depth0_listen)) { 296 pjdlog_error("listen argument is too long."); 297 free($2); 298 return (1); 299 } 300 break; 301 case 1: 302 if (!mynode) 303 break; 304 if (strlcpy(lconfig->hc_listenaddr, $2, 305 sizeof(lconfig->hc_listenaddr)) >= 306 sizeof(lconfig->hc_listenaddr)) { 307 pjdlog_error("listen argument is too long."); 308 free($2); 309 return (1); 310 } 311 break; 312 default: 313 assert(!"listen at wrong depth level"); 314 } 315 free($2); 316 } 317 ; 318 319 replication_statement: REPLICATION replication_type 320 { 321 switch (depth) { 322 case 0: 323 depth0_replication = $2; 324 break; 325 case 1: 326 if (curres != NULL) 327 curres->hr_replication = $2; 328 break; 329 default: 330 assert(!"replication at wrong depth level"); 331 } 332 } 333 ; 334 335 replication_type: 336 FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 337 | 338 MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 339 | 340 ASYNC { $$ = HAST_REPLICATION_ASYNC; } 341 ; 342 343 timeout_statement: TIMEOUT NUM 344 { 345 switch (depth) { 346 case 0: 347 depth0_timeout = $2; 348 break; 349 case 1: 350 if (curres != NULL) 351 curres->hr_timeout = $2; 352 break; 353 default: 354 assert(!"timeout at wrong depth level"); 355 } 356 } 357 ; 358 359 exec_statement: EXEC STR 360 { 361 switch (depth) { 362 case 0: 363 if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 364 sizeof(depth0_exec)) { 365 pjdlog_error("Exec path is too long."); 366 free($2); 367 return (1); 368 } 369 break; 370 case 1: 371 if (curres == NULL) 372 break; 373 if (strlcpy(curres->hr_exec, $2, 374 sizeof(curres->hr_exec)) >= 375 sizeof(curres->hr_exec)) { 376 pjdlog_error("Exec path is too long."); 377 free($2); 378 return (1); 379 } 380 break; 381 default: 382 assert(!"exec at wrong depth level"); 383 } 384 free($2); 385 } 386 ; 387 388 node_statement: ON node_start OB node_entries CB 389 { 390 mynode = false; 391 } 392 ; 393 394 node_start: STR 395 { 396 switch (isitme($1)) { 397 case -1: 398 free($1); 399 return (1); 400 case 0: 401 break; 402 case 1: 403 mynode = true; 404 break; 405 default: 406 assert(!"invalid isitme() return value"); 407 } 408 free($1); 409 } 410 ; 411 412 node_entries: 413 | 414 node_entries node_entry 415 ; 416 417 node_entry: 418 control_statement 419 | 420 listen_statement 421 ; 422 423 resource_statement: RESOURCE resource_start OB resource_entries CB 424 { 425 if (curres != NULL) { 426 /* 427 * Let's see there are some resource-level settings 428 * that we can use for node-level settings. 429 */ 430 if (curres->hr_provname[0] == '\0' && 431 depth1_provname[0] != '\0') { 432 /* 433 * Provider name is not set at node-level, 434 * but is set at resource-level, use it. 435 */ 436 strlcpy(curres->hr_provname, depth1_provname, 437 sizeof(curres->hr_provname)); 438 } 439 if (curres->hr_localpath[0] == '\0' && 440 depth1_localpath[0] != '\0') { 441 /* 442 * Path to local provider is not set at 443 * node-level, but is set at resource-level, 444 * use it. 445 */ 446 strlcpy(curres->hr_localpath, depth1_localpath, 447 sizeof(curres->hr_localpath)); 448 } 449 450 /* 451 * If provider name is not given, use resource name 452 * as provider name. 453 */ 454 if (curres->hr_provname[0] == '\0') { 455 strlcpy(curres->hr_provname, curres->hr_name, 456 sizeof(curres->hr_provname)); 457 } 458 459 /* 460 * Remote address has to be configured at this point. 461 */ 462 if (curres->hr_remoteaddr[0] == '\0') { 463 pjdlog_error("Remote address not configured for resource %s.", 464 curres->hr_name); 465 return (1); 466 } 467 /* 468 * Path to local provider has to be configured at this 469 * point. 470 */ 471 if (curres->hr_localpath[0] == '\0') { 472 pjdlog_error("Path to local component not configured for resource %s.", 473 curres->hr_name); 474 return (1); 475 } 476 477 /* Put it onto resource list. */ 478 TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 479 curres = NULL; 480 } 481 } 482 ; 483 484 resource_start: STR 485 { 486 /* 487 * Clear those, so we can tell if they were set at 488 * resource-level or not. 489 */ 490 depth1_provname[0] = '\0'; 491 depth1_localpath[0] = '\0'; 492 493 curres = calloc(1, sizeof(*curres)); 494 if (curres == NULL) { 495 pjdlog_error("Unable to allocate memory for resource."); 496 free($1); 497 return (1); 498 } 499 if (strlcpy(curres->hr_name, $1, 500 sizeof(curres->hr_name)) >= 501 sizeof(curres->hr_name)) { 502 pjdlog_error("Resource name is too long."); 503 free($1); 504 return (1); 505 } 506 free($1); 507 curres->hr_role = HAST_ROLE_INIT; 508 curres->hr_previous_role = HAST_ROLE_INIT; 509 curres->hr_replication = -1; 510 curres->hr_timeout = -1; 511 curres->hr_exec[0] = '\0'; 512 curres->hr_provname[0] = '\0'; 513 curres->hr_localpath[0] = '\0'; 514 curres->hr_localfd = -1; 515 curres->hr_remoteaddr[0] = '\0'; 516 curres->hr_ggateunit = -1; 517 } 518 ; 519 520 resource_entries: 521 | 522 resource_entries resource_entry 523 ; 524 525 resource_entry: 526 replication_statement 527 | 528 timeout_statement 529 | 530 exec_statement 531 | 532 name_statement 533 | 534 local_statement 535 | 536 resource_node_statement 537 ; 538 539 name_statement: NAME STR 540 { 541 switch (depth) { 542 case 1: 543 if (strlcpy(depth1_provname, $2, 544 sizeof(depth1_provname)) >= 545 sizeof(depth1_provname)) { 546 pjdlog_error("name argument is too long."); 547 free($2); 548 return (1); 549 } 550 break; 551 case 2: 552 if (!mynode) 553 break; 554 assert(curres != NULL); 555 if (strlcpy(curres->hr_provname, $2, 556 sizeof(curres->hr_provname)) >= 557 sizeof(curres->hr_provname)) { 558 pjdlog_error("name argument is too long."); 559 free($2); 560 return (1); 561 } 562 break; 563 default: 564 assert(!"name at wrong depth level"); 565 } 566 free($2); 567 } 568 ; 569 570 local_statement: LOCAL STR 571 { 572 switch (depth) { 573 case 1: 574 if (strlcpy(depth1_localpath, $2, 575 sizeof(depth1_localpath)) >= 576 sizeof(depth1_localpath)) { 577 pjdlog_error("local argument is too long."); 578 free($2); 579 return (1); 580 } 581 break; 582 case 2: 583 if (!mynode) 584 break; 585 assert(curres != NULL); 586 if (strlcpy(curres->hr_localpath, $2, 587 sizeof(curres->hr_localpath)) >= 588 sizeof(curres->hr_localpath)) { 589 pjdlog_error("local argument is too long."); 590 free($2); 591 return (1); 592 } 593 break; 594 default: 595 assert(!"local at wrong depth level"); 596 } 597 free($2); 598 } 599 ; 600 601 resource_node_statement:ON resource_node_start OB resource_node_entries CB 602 { 603 mynode = false; 604 } 605 ; 606 607 resource_node_start: STR 608 { 609 if (curres != NULL) { 610 switch (isitme($1)) { 611 case -1: 612 free($1); 613 return (1); 614 case 0: 615 break; 616 case 1: 617 mynode = true; 618 break; 619 default: 620 assert(!"invalid isitme() return value"); 621 } 622 } 623 free($1); 624 } 625 ; 626 627 resource_node_entries: 628 | 629 resource_node_entries resource_node_entry 630 ; 631 632 resource_node_entry: 633 name_statement 634 | 635 local_statement 636 | 637 remote_statement 638 ; 639 640 remote_statement: REMOTE STR 641 { 642 assert(depth == 2); 643 if (mynode) { 644 assert(curres != NULL); 645 if (strlcpy(curres->hr_remoteaddr, $2, 646 sizeof(curres->hr_remoteaddr)) >= 647 sizeof(curres->hr_remoteaddr)) { 648 pjdlog_error("remote argument is too long."); 649 free($2); 650 return (1); 651 } 652 } 653 free($2); 654 } 655 ; 656