parse.y revision 211886
1255332Scy%{ 2255332Scy/*- 3255332Scy * Copyright (c) 2009-2010 The FreeBSD Foundation 4255332Scy * All rights reserved. 5255332Scy * 6255332Scy * This software was developed by Pawel Jakub Dawidek under sponsorship from 7255332Scy * the FreeBSD Foundation. 8255332Scy * 9255332Scy * Redistribution and use in source and binary forms, with or without 10255332Scy * modification, are permitted provided that the following conditions 11255332Scy * are met: 12255332Scy * 1. Redistributions of source code must retain the above copyright 13255332Scy * notice, this list of conditions and the following disclaimer. 14255332Scy * 2. Redistributions in binary form must reproduce the above copyright 15255332Scy * notice, this list of conditions and the following disclaimer in the 16255332Scy * documentation and/or other materials provided with the distribution. 17255332Scy * 18255332Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19255332Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20255332Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21255332Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22255332Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23255332Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24255332Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25255332Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26255332Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27255332Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28255332Scy * SUCH DAMAGE. 29255332Scy * 30255332Scy * $FreeBSD: head/sbin/hastd/parse.y 211886 2010-08-27 15:16:52Z pjd $ 31255332Scy */ 32255332Scy 33255332Scy#include <sys/param.h> /* MAXHOSTNAMELEN */ 34255332Scy#include <sys/queue.h> 35255332Scy#include <sys/sysctl.h> 36255332Scy 37255332Scy#include <arpa/inet.h> 38255332Scy 39255332Scy#include <assert.h> 40255332Scy#include <err.h> 41255332Scy#include <stdio.h> 42255332Scy#include <string.h> 43255332Scy#include <sysexits.h> 44255332Scy#include <unistd.h> 45255332Scy 46255332Scy#include <pjdlog.h> 47255332Scy 48255332Scy#include "hast.h" 49255332Scy 50255332Scyextern int depth; 51255332Scyextern int lineno; 52255332Scy 53255332Scyextern FILE *yyin; 54255332Scyextern char *yytext; 55255332Scy 56255332Scystatic struct hastd_config *lconfig; 57255332Scystatic struct hast_resource *curres; 58255332Scystatic bool mynode; 59255332Scy 60255332Scystatic char depth0_control[HAST_ADDRSIZE]; 61255332Scystatic char depth0_listen[HAST_ADDRSIZE]; 62255332Scystatic int depth0_replication; 63255332Scystatic int depth0_timeout; 64255332Scystatic char depth0_exec[PATH_MAX]; 65255332Scy 66255332Scystatic char depth1_provname[PATH_MAX]; 67255332Scystatic char depth1_localpath[PATH_MAX]; 68255332Scy 69255332Scyextern void yyrestart(FILE *); 70255332Scy 71255332Scystatic int 72255332Scyisitme(const char *name) 73255332Scy{ 74255332Scy char buf[MAXHOSTNAMELEN]; 75255332Scy char *pos; 76255332Scy size_t bufsize; 77255332Scy 78255332Scy /* 79255332Scy * First check if the give name matches our full hostname. 80255332Scy */ 81255332Scy if (gethostname(buf, sizeof(buf)) < 0) { 82255332Scy pjdlog_errno(LOG_ERR, "gethostname() failed"); 83255332Scy return (-1); 84255332Scy } 85255332Scy if (strcmp(buf, name) == 0) 86255332Scy return (1); 87255332Scy 88255332Scy /* 89255332Scy * Now check if it matches first part of the host name. 90255332Scy */ 91255332Scy pos = strchr(buf, '.'); 92255332Scy if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0) 93255332Scy return (1); 94255332Scy 95255332Scy /* 96255332Scy * At the end check if name is equal to our host's UUID. 97255332Scy */ 98255332Scy bufsize = sizeof(buf); 99255332Scy if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) { 100255332Scy pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed"); 101255332Scy return (-1); 102255332Scy } 103255332Scy if (strcasecmp(buf, name) == 0) 104255332Scy return (1); 105255332Scy 106255332Scy /* 107255332Scy * Looks like this isn't about us. 108255332Scy */ 109255332Scy return (0); 110255332Scy} 111255332Scy 112255332Scyvoid 113255332Scyyyerror(const char *str) 114255332Scy{ 115255332Scy 116255332Scy pjdlog_error("Unable to parse configuration file at line %d near '%s': %s", 117255332Scy lineno, yytext, str); 118255332Scy} 119255332Scy 120255332Scystruct hastd_config * 121255332Scyyy_config_parse(const char *config, bool exitonerror) 122255332Scy{ 123255332Scy int ret; 124255332Scy 125255332Scy curres = NULL; 126255332Scy mynode = false; 127255332Scy depth = 0; 128255332Scy lineno = 0; 129255332Scy 130255332Scy depth0_timeout = HAST_TIMEOUT; 131255332Scy depth0_replication = HAST_REPLICATION_MEMSYNC; 132255332Scy strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control)); 133255332Scy strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen)); 134255332Scy depth0_exec[0] = '\0'; 135255332Scy 136255332Scy lconfig = calloc(1, sizeof(*lconfig)); 137255332Scy if (lconfig == NULL) { 138255332Scy pjdlog_error("Unable to allocate memory for configuration."); 139255332Scy if (exitonerror) 140255332Scy exit(EX_TEMPFAIL); 141255332Scy return (NULL); 142255332Scy } 143255332Scy 144255332Scy TAILQ_INIT(&lconfig->hc_resources); 145255332Scy 146255332Scy yyin = fopen(config, "r"); 147255332Scy if (yyin == NULL) { 148255332Scy pjdlog_errno(LOG_ERR, "Unable to open configuration file %s", 149255332Scy config); 150255332Scy yy_config_free(lconfig); 151255332Scy if (exitonerror) 152255332Scy exit(EX_OSFILE); 153255332Scy return (NULL); 154255332Scy } 155255332Scy yyrestart(yyin); 156255332Scy ret = yyparse(); 157255332Scy fclose(yyin); 158255332Scy if (ret != 0) { 159255332Scy yy_config_free(lconfig); 160255332Scy if (exitonerror) 161255332Scy exit(EX_CONFIG); 162255332Scy return (NULL); 163255332Scy } 164255332Scy 165255332Scy /* 166255332Scy * Let's see if everything is set up. 167255332Scy */ 168255332Scy if (lconfig->hc_controladdr[0] == '\0') { 169255332Scy strlcpy(lconfig->hc_controladdr, depth0_control, 170255332Scy sizeof(lconfig->hc_controladdr)); 171255332Scy } 172255332Scy if (lconfig->hc_listenaddr[0] == '\0') { 173255332Scy strlcpy(lconfig->hc_listenaddr, depth0_listen, 174255332Scy sizeof(lconfig->hc_listenaddr)); 175255332Scy } 176255332Scy TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) { 177255332Scy assert(curres->hr_provname[0] != '\0'); 178255332Scy assert(curres->hr_localpath[0] != '\0'); 179255332Scy assert(curres->hr_remoteaddr[0] != '\0'); 180255332Scy 181255332Scy if (curres->hr_replication == -1) { 182255332Scy /* 183255332Scy * Replication is not set at resource-level. 184255332Scy * Use global or default setting. 185255332Scy */ 186255332Scy curres->hr_replication = depth0_replication; 187255332Scy } 188255332Scy if (curres->hr_timeout == -1) { 189255332Scy /* 190255332Scy * Timeout is not set at resource-level. 191255332Scy * Use global or default setting. 192255332Scy */ 193255332Scy curres->hr_timeout = depth0_timeout; 194255332Scy } 195255332Scy if (curres->hr_exec[0] == '\0') { 196255332Scy /* 197255332Scy * Exec is not set at resource-level. 198255332Scy * Use global or default setting. 199255332Scy */ 200255332Scy strlcpy(curres->hr_exec, depth0_exec, 201255332Scy sizeof(curres->hr_exec)); 202255332Scy } 203255332Scy } 204255332Scy 205255332Scy return (lconfig); 206255332Scy} 207255332Scy 208255332Scyvoid 209255332Scyyy_config_free(struct hastd_config *config) 210255332Scy{ 211255332Scy struct hast_resource *res; 212255332Scy 213255332Scy while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) { 214255332Scy TAILQ_REMOVE(&config->hc_resources, res, hr_next); 215255332Scy free(res); 216255332Scy } 217255332Scy free(config); 218255332Scy} 219255332Scy%} 220255332Scy 221255332Scy%token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON 222255332Scy%token FULLSYNC MEMSYNC ASYNC 223255332Scy%token NUM STR OB CB 224255332Scy 225255332Scy%type <num> replication_type 226255332Scy 227255332Scy%union 228255332Scy{ 229255332Scy int num; 230255332Scy char *str; 231255332Scy} 232255332Scy 233255332Scy%token <num> NUM 234255332Scy%token <str> STR 235255332Scy 236255332Scy%% 237255332Scy 238255332Scystatements: 239255332Scy | 240255332Scy statements statement 241255332Scy ; 242255332Scy 243255332Scystatement: 244255332Scy control_statement 245255332Scy | 246255332Scy listen_statement 247255332Scy | 248255332Scy replication_statement 249255332Scy | 250255332Scy timeout_statement 251255332Scy | 252255332Scy exec_statement 253255332Scy | 254255332Scy node_statement 255255332Scy | 256255332Scy resource_statement 257255332Scy ; 258255332Scy 259255332Scycontrol_statement: CONTROL STR 260255332Scy { 261255332Scy switch (depth) { 262255332Scy case 0: 263255332Scy if (strlcpy(depth0_control, $2, 264255332Scy sizeof(depth0_control)) >= 265255332Scy sizeof(depth0_control)) { 266255332Scy pjdlog_error("control argument is too long."); 267255332Scy return (1); 268255332Scy } 269255332Scy break; 270255332Scy case 1: 271255332Scy if (!mynode) 272255332Scy break; 273255332Scy if (strlcpy(lconfig->hc_controladdr, $2, 274255332Scy sizeof(lconfig->hc_controladdr)) >= 275255332Scy sizeof(lconfig->hc_controladdr)) { 276255332Scy pjdlog_error("control argument is too long."); 277255332Scy return (1); 278255332Scy } 279255332Scy break; 280255332Scy default: 281255332Scy assert(!"control at wrong depth level"); 282255332Scy } 283255332Scy } 284255332Scy ; 285255332Scy 286255332Scylisten_statement: LISTEN STR 287255332Scy { 288255332Scy switch (depth) { 289255332Scy case 0: 290255332Scy if (strlcpy(depth0_listen, $2, 291255332Scy sizeof(depth0_listen)) >= 292255332Scy sizeof(depth0_listen)) { 293255332Scy pjdlog_error("listen argument is too long."); 294255332Scy return (1); 295255332Scy } 296255332Scy break; 297255332Scy case 1: 298255332Scy if (!mynode) 299255332Scy break; 300255332Scy if (strlcpy(lconfig->hc_listenaddr, $2, 301255332Scy sizeof(lconfig->hc_listenaddr)) >= 302255332Scy sizeof(lconfig->hc_listenaddr)) { 303255332Scy pjdlog_error("listen argument is too long."); 304255332Scy return (1); 305255332Scy } 306255332Scy break; 307255332Scy default: 308255332Scy assert(!"listen at wrong depth level"); 309255332Scy } 310255332Scy } 311255332Scy ; 312255332Scy 313255332Scyreplication_statement: REPLICATION replication_type 314255332Scy { 315255332Scy switch (depth) { 316255332Scy case 0: 317255332Scy depth0_replication = $2; 318255332Scy break; 319255332Scy case 1: 320255332Scy if (curres != NULL) 321255332Scy curres->hr_replication = $2; 322255332Scy break; 323255332Scy default: 324255332Scy assert(!"replication at wrong depth level"); 325255332Scy } 326255332Scy } 327255332Scy ; 328255332Scy 329255332Scyreplication_type: 330255332Scy FULLSYNC { $$ = HAST_REPLICATION_FULLSYNC; } 331255332Scy | 332255332Scy MEMSYNC { $$ = HAST_REPLICATION_MEMSYNC; } 333255332Scy | 334255332Scy ASYNC { $$ = HAST_REPLICATION_ASYNC; } 335255332Scy ; 336255332Scy 337255332Scytimeout_statement: TIMEOUT NUM 338255332Scy { 339255332Scy switch (depth) { 340255332Scy case 0: 341255332Scy depth0_timeout = $2; 342255332Scy break; 343255332Scy case 1: 344255332Scy if (curres != NULL) 345255332Scy curres->hr_timeout = $2; 346255332Scy break; 347255332Scy default: 348255332Scy assert(!"timeout at wrong depth level"); 349255332Scy } 350255332Scy } 351255332Scy ; 352255332Scy 353255332Scyexec_statement: EXEC STR 354255332Scy { 355255332Scy switch (depth) { 356255332Scy case 0: 357255332Scy if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >= 358255332Scy sizeof(depth0_exec)) { 359255332Scy pjdlog_error("Exec path is too long."); 360255332Scy return (1); 361255332Scy } 362255332Scy break; 363255332Scy case 1: 364255332Scy if (curres == NULL) 365255332Scy break; 366255332Scy if (strlcpy(curres->hr_exec, $2, 367255332Scy sizeof(curres->hr_exec)) >= 368255332Scy sizeof(curres->hr_exec)) { 369255332Scy pjdlog_error("Exec path is too long."); 370255332Scy return (1); 371255332Scy } 372255332Scy break; 373255332Scy default: 374255332Scy assert(!"exec at wrong depth level"); 375255332Scy } 376255332Scy } 377255332Scy ; 378255332Scy 379255332Scynode_statement: ON node_start OB node_entries CB 380255332Scy { 381255332Scy mynode = false; 382255332Scy } 383255332Scy ; 384255332Scy 385255332Scynode_start: STR 386255332Scy { 387255332Scy switch (isitme($1)) { 388255332Scy case -1: 389255332Scy return (1); 390255332Scy case 0: 391255332Scy break; 392255332Scy case 1: 393255332Scy mynode = true; 394255332Scy break; 395255332Scy default: 396255332Scy assert(!"invalid isitme() return value"); 397255332Scy } 398255332Scy } 399255332Scy ; 400255332Scy 401255332Scynode_entries: 402255332Scy | 403255332Scy node_entries node_entry 404255332Scy ; 405255332Scy 406255332Scynode_entry: 407255332Scy control_statement 408255332Scy | 409255332Scy listen_statement 410255332Scy ; 411255332Scy 412255332Scyresource_statement: RESOURCE resource_start OB resource_entries CB 413255332Scy { 414255332Scy if (curres != NULL) { 415255332Scy /* 416255332Scy * Let's see there are some resource-level settings 417255332Scy * that we can use for node-level settings. 418255332Scy */ 419255332Scy if (curres->hr_provname[0] == '\0' && 420255332Scy depth1_provname[0] != '\0') { 421255332Scy /* 422255332Scy * Provider name is not set at node-level, 423255332Scy * but is set at resource-level, use it. 424255332Scy */ 425255332Scy strlcpy(curres->hr_provname, depth1_provname, 426255332Scy sizeof(curres->hr_provname)); 427255332Scy } 428255332Scy if (curres->hr_localpath[0] == '\0' && 429255332Scy depth1_localpath[0] != '\0') { 430255332Scy /* 431255332Scy * Path to local provider is not set at 432255332Scy * node-level, but is set at resource-level, 433255332Scy * use it. 434255332Scy */ 435255332Scy strlcpy(curres->hr_localpath, depth1_localpath, 436255332Scy sizeof(curres->hr_localpath)); 437255332Scy } 438255332Scy 439255332Scy /* 440255332Scy * If provider name is not given, use resource name 441255332Scy * as provider name. 442255332Scy */ 443255332Scy if (curres->hr_provname[0] == '\0') { 444255332Scy strlcpy(curres->hr_provname, curres->hr_name, 445255332Scy sizeof(curres->hr_provname)); 446255332Scy } 447255332Scy 448255332Scy /* 449255332Scy * Remote address has to be configured at this point. 450255332Scy */ 451255332Scy if (curres->hr_remoteaddr[0] == '\0') { 452255332Scy pjdlog_error("Remote address not configured for resource %s.", 453255332Scy curres->hr_name); 454255332Scy return (1); 455255332Scy } 456255332Scy /* 457255332Scy * Path to local provider has to be configured at this 458255332Scy * point. 459255332Scy */ 460255332Scy if (curres->hr_localpath[0] == '\0') { 461255332Scy pjdlog_error("Path to local component not configured for resource %s.", 462255332Scy curres->hr_name); 463255332Scy return (1); 464255332Scy } 465255332Scy 466255332Scy /* Put it onto resource list. */ 467255332Scy TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next); 468255332Scy curres = NULL; 469255332Scy } 470255332Scy } 471255332Scy ; 472255332Scy 473255332Scyresource_start: STR 474255332Scy { 475255332Scy /* 476255332Scy * Clear those, so we can tell if they were set at 477255332Scy * resource-level or not. 478255332Scy */ 479255332Scy depth1_provname[0] = '\0'; 480255332Scy depth1_localpath[0] = '\0'; 481255332Scy 482255332Scy curres = calloc(1, sizeof(*curres)); 483255332Scy if (curres == NULL) { 484255332Scy pjdlog_error("Unable to allocate memory for resource."); 485255332Scy return (1); 486255332Scy } 487255332Scy if (strlcpy(curres->hr_name, $1, 488255332Scy sizeof(curres->hr_name)) >= 489255332Scy sizeof(curres->hr_name)) { 490255332Scy pjdlog_error("Resource name is too long."); 491255332Scy return (1); 492255332Scy } 493255332Scy curres->hr_role = HAST_ROLE_INIT; 494255332Scy curres->hr_previous_role = HAST_ROLE_INIT; 495255332Scy curres->hr_replication = -1; 496255332Scy curres->hr_timeout = -1; 497255332Scy curres->hr_exec[0] = '\0'; 498255332Scy curres->hr_provname[0] = '\0'; 499255332Scy curres->hr_localpath[0] = '\0'; 500255332Scy curres->hr_localfd = -1; 501255332Scy curres->hr_remoteaddr[0] = '\0'; 502255332Scy curres->hr_ggateunit = -1; 503255332Scy } 504255332Scy ; 505255332Scy 506255332Scyresource_entries: 507255332Scy | 508255332Scy resource_entries resource_entry 509255332Scy ; 510255332Scy 511255332Scyresource_entry: 512255332Scy replication_statement 513255332Scy | 514255332Scy timeout_statement 515255332Scy | 516255332Scy exec_statement 517255332Scy | 518255332Scy name_statement 519255332Scy | 520255332Scy local_statement 521255332Scy | 522255332Scy resource_node_statement 523255332Scy ; 524255332Scy 525255332Scyname_statement: NAME STR 526255332Scy { 527255332Scy switch (depth) { 528255332Scy case 1: 529255332Scy if (strlcpy(depth1_provname, $2, 530 sizeof(depth1_provname)) >= 531 sizeof(depth1_provname)) { 532 pjdlog_error("name argument is too long."); 533 return (1); 534 } 535 break; 536 case 2: 537 if (!mynode) 538 break; 539 assert(curres != NULL); 540 if (strlcpy(curres->hr_provname, $2, 541 sizeof(curres->hr_provname)) >= 542 sizeof(curres->hr_provname)) { 543 pjdlog_error("name argument is too long."); 544 return (1); 545 } 546 break; 547 default: 548 assert(!"name at wrong depth level"); 549 } 550 } 551 ; 552 553local_statement: LOCAL STR 554 { 555 switch (depth) { 556 case 1: 557 if (strlcpy(depth1_localpath, $2, 558 sizeof(depth1_localpath)) >= 559 sizeof(depth1_localpath)) { 560 pjdlog_error("local argument is too long."); 561 return (1); 562 } 563 break; 564 case 2: 565 if (!mynode) 566 break; 567 assert(curres != NULL); 568 if (strlcpy(curres->hr_localpath, $2, 569 sizeof(curres->hr_localpath)) >= 570 sizeof(curres->hr_localpath)) { 571 pjdlog_error("local argument is too long."); 572 return (1); 573 } 574 break; 575 default: 576 assert(!"local at wrong depth level"); 577 } 578 } 579 ; 580 581resource_node_statement:ON resource_node_start OB resource_node_entries CB 582 { 583 mynode = false; 584 } 585 ; 586 587resource_node_start: STR 588 { 589 if (curres != NULL) { 590 switch (isitme($1)) { 591 case -1: 592 return (1); 593 case 0: 594 break; 595 case 1: 596 mynode = true; 597 break; 598 default: 599 assert(!"invalid isitme() return value"); 600 } 601 } 602 } 603 ; 604 605resource_node_entries: 606 | 607 resource_node_entries resource_node_entry 608 ; 609 610resource_node_entry: 611 name_statement 612 | 613 local_statement 614 | 615 remote_statement 616 ; 617 618remote_statement: REMOTE STR 619 { 620 assert(depth == 2); 621 if (mynode) { 622 assert(curres != NULL); 623 if (strlcpy(curres->hr_remoteaddr, $2, 624 sizeof(curres->hr_remoteaddr)) >= 625 sizeof(curres->hr_remoteaddr)) { 626 pjdlog_error("remote argument is too long."); 627 return (1); 628 } 629 } 630 } 631 ; 632