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