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