| 70 | | |
| 71 | | struct host { |
| 72 | | VTAILQ_ENTRY(host) list; |
| 73 | | struct token *name; |
| 74 | | char *vgcname; |
| 75 | | }; |
| 76 | | |
| 77 | | static const char * |
| 78 | | CheckHostPort(const char *host, const char *port) |
| 79 | | { |
| 80 | | struct addrinfo *res, hint; |
| 81 | | int error; |
| 82 | | |
| 83 | | memset(&hint, 0, sizeof hint); |
| 84 | | hint.ai_family = PF_UNSPEC; |
| 85 | | hint.ai_socktype = SOCK_STREAM; |
| 86 | | error = getaddrinfo(host, port, &hint, &res); |
| 87 | | if (error) |
| 88 | | return (gai_strerror(error)); |
| 89 | | freeaddrinfo(res); |
| 90 | | return (NULL); |
| 91 | | } |
| 92 | | |
| 93 | | /*-------------------------------------------------------------------- |
| 94 | | * Struct sockaddr is not really designed to be a compile time |
| 95 | | * initialized data structure, so we encode it as a byte-string |
| 96 | | * and put it in an official sockaddr when we load the VCL. |
| 97 | | */ |
| 98 | | |
| 99 | | static void |
| 100 | | Emit_Sockaddr(struct tokenlist *tl, const struct token *t_host, |
| 101 | | const char *port) |
| 102 | | { |
| 103 | | struct addrinfo *res, *res0, *res1, hint; |
| 104 | | int n4, n6, len, error, retval; |
| 105 | | const char *emit, *multiple; |
| 106 | | unsigned char *u; |
| 107 | | char hbuf[NI_MAXHOST]; |
| 108 | | |
| 109 | | AN(t_host->dec); |
| 110 | | retval = 0; |
| 111 | | memset(&hint, 0, sizeof hint); |
| 112 | | hint.ai_family = PF_UNSPEC; |
| 113 | | hint.ai_socktype = SOCK_STREAM; |
| 114 | | error = getaddrinfo(t_host->dec, port, &hint, &res0); |
| 115 | | AZ(error); |
| 116 | | n4 = n6 = 0; |
| 117 | | multiple = NULL; |
| 118 | | for (res = res0; res; res = res->ai_next) { |
| 119 | | emit = NULL; |
| 120 | | if (res->ai_family == PF_INET) { |
| 121 | | if (n4++ == 0) |
| 122 | | emit = "ipv4_sockaddr"; |
| 123 | | else |
| 124 | | multiple = "IPv4"; |
| 125 | | } else if (res->ai_family == PF_INET6) { |
| 126 | | if (n6++ == 0) |
| 127 | | emit = "ipv6_sockaddr"; |
| 128 | | else |
| 129 | | multiple = "IPv6"; |
| 130 | | } else |
| 131 | | continue; |
| 132 | | |
| 133 | | if (multiple != NULL) { |
| 134 | | vsb_printf(tl->sb, |
| 135 | | "Backend host %.*s: resolves to " |
| 136 | | "multiple %s addresses.\n" |
| 137 | | "Only one address is allowed.\n" |
| 138 | | "Please specify which exact address " |
| 139 | | "you want to use, we found these:\n", |
| 140 | | PF(t_host), multiple); |
| 141 | | for (res1 = res0; res1 != NULL; res1 = res1->ai_next) { |
| 142 | | error = getnameinfo(res1->ai_addr, |
| 143 | | res1->ai_addrlen, hbuf, sizeof hbuf, |
| 144 | | NULL, 0, NI_NUMERICHOST); |
| 145 | | AZ(error); |
| 146 | | vsb_printf(tl->sb, "\t%s\n", hbuf); |
| 147 | | } |
| 148 | | vcc_ErrWhere(tl, t_host); |
| 149 | | return; |
| 150 | | } |
| 151 | | AN(emit); |
| 152 | | AN(res->ai_addr); |
| 153 | | AN(res->ai_addrlen); |
| 154 | | assert(res->ai_addrlen < 256); |
| 155 | | Fh(tl, 0, "\nstatic const unsigned char sockaddr%u[%d] = {\n", |
| 156 | | tl->nsockaddr, res->ai_addrlen + 1); |
| 157 | | Fh(tl, 0, " %3u, /* Length */\n", res->ai_addrlen); |
| 158 | | u = (void*)res->ai_addr; |
| 159 | | for (len = 0; len < res->ai_addrlen; len++) { |
| 160 | | if ((len % 8) == 0) |
| 161 | | Fh(tl, 0, " "); |
| 162 | | Fh(tl, 0, " %3u", u[len]); |
| 163 | | if (len + 1 < res->ai_addrlen) |
| 164 | | Fh(tl, 0, ","); |
| 165 | | if ((len % 8) == 7) |
| 166 | | Fh(tl, 0, "\n"); |
| 167 | | } |
| 168 | | Fh(tl, 0, "\n};\n"); |
| 169 | | Fb(tl, 0, "\t.%s = sockaddr%u,\n", emit, tl->nsockaddr++); |
| 170 | | retval++; |
| 171 | | } |
| 172 | | freeaddrinfo(res0); |
| 173 | | if (retval == 0) { |
| 174 | | vsb_printf(tl->sb, |
| 175 | | "Backend host '%.*s': resolves to " |
| 176 | | "neither IPv4 nor IPv6 addresses.\n", |
| 177 | | PF(t_host) ); |
| 178 | | vcc_ErrWhere(tl, t_host); |
| 179 | | } |
| 180 | | } |
| 181 | | |
| 182 | | /*-------------------------------------------------------------------- |
| 183 | | * When a new VCL is loaded, it is likely to contain backend declarations |
| 184 | | * identical to other loaded VCL programs, and we want to reuse the state |
| 185 | | * of those in order to not have to relearn statistics, DNS etc. |
| 186 | | * |
| 187 | | * This function emits a space separated text-string of the tokens which |
| 188 | | * define a given backend which can be used to determine "identical backend" |
| 189 | | * in that context. |
| 190 | | */ |
| 191 | | |
| 192 | | static void |
| 193 | | vcc_EmitBeIdent(const struct tokenlist *tl, struct vsb *v, |
| 194 | | int serial, const struct token *first, const struct token *last) |
| 195 | | { |
| 196 | | |
| 197 | | assert(first != last); |
| 198 | | vsb_printf(v, "\t.ident ="); |
| 199 | | if (serial >= 0) { |
| 200 | | vsb_printf(v, "\n\t \"%.*s %.*s [%d] \"", |
| 201 | | PF(tl->t_policy), PF(tl->t_dir), serial); |
| 202 | | } else { |
| 203 | | vsb_printf(v, "\n\t \"%.*s %.*s \"", |
| 204 | | PF(tl->t_policy), PF(tl->t_dir)); |
| 205 | | } |
| 206 | | while (1) { |
| 207 | | if (first->dec != NULL) |
| 208 | | vsb_printf(v, "\n\t \"\\\"\" %.*s \"\\\" \"", |
| 209 | | PF(first)); |
| 210 | | else |
| 211 | | vsb_printf(v, "\n\t \"%.*s \"", PF(first)); |
| 212 | | if (first == last) |
| 213 | | break; |
| 214 | | first = VTAILQ_NEXT(first, list); |
| 215 | | AN(first); |
| 216 | | } |
| 217 | | vsb_printf(v, ",\n"); |
| 218 | | } |
| 318 | | |
| 319 | | /*-------------------------------------------------------------------- |
| 320 | | * Parse a backend probe specification |
| 321 | | */ |
| 322 | | |
| 323 | | static void |
| 324 | | vcc_ProbeRedef(struct tokenlist *tl, struct token **t_did, |
| 325 | | struct token *t_field) |
| 326 | | { |
| 327 | | /* .url and .request are mutually exclusive */ |
| 328 | | |
| 329 | | if (*t_did != NULL) { |
| 330 | | vsb_printf(tl->sb, |
| 331 | | "Probe request redefinition at:\n"); |
| 332 | | vcc_ErrWhere(tl, t_field); |
| 333 | | vsb_printf(tl->sb, |
| 334 | | "Previous definition:\n"); |
| 335 | | vcc_ErrWhere(tl, *t_did); |
| 336 | | return; |
| 337 | | } |
| 338 | | *t_did = t_field; |
| 339 | | } |
| 340 | | |
| 341 | | static void |
| 342 | | vcc_ParseProbe(struct tokenlist *tl) |
| 343 | | { |
| 344 | | struct fld_spec *fs; |
| 345 | | struct token *t_field; |
| 346 | | struct token *t_did = NULL, *t_window = NULL, *t_threshold = NULL; |
| 347 | | struct token *t_initial = NULL; |
| 348 | | unsigned window, threshold, initial, status; |
| 349 | | |
| 350 | | fs = vcc_FldSpec(tl, |
| 351 | | "?url", |
| 352 | | "?request", |
| 353 | | "?expected_response", |
| 354 | | "?timeout", |
| 355 | | "?interval", |
| 356 | | "?window", |
| 357 | | "?threshold", |
| 358 | | "?initial", |
| 359 | | NULL); |
| 360 | | |
| 361 | | ExpectErr(tl, '{'); |
| 362 | | vcc_NextToken(tl); |
| 363 | | |
| 364 | | window = 0; |
| 365 | | threshold = 0; |
| 366 | | initial = 0; |
| 367 | | status = 0; |
| 368 | | Fb(tl, 0, "\t.probe = {\n"); |
| 369 | | while (tl->t->tok != '}') { |
| 370 | | |
| 371 | | vcc_IsField(tl, &t_field, fs); |
| 372 | | ERRCHK(tl); |
| 373 | | if (vcc_IdIs(t_field, "url")) { |
| 374 | | vcc_ProbeRedef(tl, &t_did, t_field); |
| 375 | | ERRCHK(tl); |
| 376 | | ExpectErr(tl, CSTR); |
| 377 | | Fb(tl, 0, "\t\t.url = "); |
| 378 | | EncToken(tl->fb, tl->t); |
| 379 | | Fb(tl, 0, ",\n"); |
| 380 | | vcc_NextToken(tl); |
| 381 | | } else if (vcc_IdIs(t_field, "request")) { |
| 382 | | vcc_ProbeRedef(tl, &t_did, t_field); |
| 383 | | ERRCHK(tl); |
| 384 | | ExpectErr(tl, CSTR); |
| 385 | | Fb(tl, 0, "\t\t.request =\n"); |
| 386 | | while (tl->t->tok == CSTR) { |
| 387 | | Fb(tl, 0, "\t\t\t"); |
| 388 | | EncToken(tl->fb, tl->t); |
| 389 | | Fb(tl, 0, " \"\\r\\n\"\n"); |
| 390 | | vcc_NextToken(tl); |
| 391 | | } |
| 392 | | Fb(tl, 0, "\t\t\t\"\\r\\n\",\n"); |
| 393 | | } else if (vcc_IdIs(t_field, "timeout")) { |
| 394 | | Fb(tl, 0, "\t\t.timeout = "); |
| 395 | | vcc_TimeVal(tl); |
| 396 | | ERRCHK(tl); |
| 397 | | Fb(tl, 0, ",\n"); |
| 398 | | } else if (vcc_IdIs(t_field, "interval")) { |
| 399 | | Fb(tl, 0, "\t\t.interval = "); |
| 400 | | vcc_TimeVal(tl); |
| 401 | | ERRCHK(tl); |
| 402 | | Fb(tl, 0, ",\n"); |
| 403 | | } else if (vcc_IdIs(t_field, "window")) { |
| 404 | | t_window = tl->t; |
| 405 | | window = vcc_UintVal(tl); |
| 406 | | vcc_NextToken(tl); |
| 407 | | ERRCHK(tl); |
| 408 | | } else if (vcc_IdIs(t_field, "initial")) { |
| 409 | | t_initial = tl->t; |
| 410 | | initial = vcc_UintVal(tl); |
| 411 | | vcc_NextToken(tl); |
| 412 | | ERRCHK(tl); |
| 413 | | } else if (vcc_IdIs(t_field, "expected_response")) { |
| 414 | | status = vcc_UintVal(tl); |
| 415 | | if (status < 100 || status > 999) { |
| 416 | | vsb_printf(tl->sb, |
| 417 | | "Must specify .status with exactly three " |
| 418 | | " digits (100 <= x <= 999)\n"); |
| 419 | | vcc_ErrWhere(tl, tl->t); |
| 420 | | return; |
| 421 | | } |
| 422 | | vcc_NextToken(tl); |
| 423 | | ERRCHK(tl); |
| 424 | | } else if (vcc_IdIs(t_field, "threshold")) { |
| 425 | | t_threshold = tl->t; |
| 426 | | threshold = vcc_UintVal(tl); |
| 427 | | vcc_NextToken(tl); |
| 428 | | ERRCHK(tl); |
| 429 | | } else { |
| 430 | | vcc_ErrToken(tl, t_field); |
| 431 | | vcc_ErrWhere(tl, t_field); |
| 432 | | ErrInternal(tl); |
| 433 | | return; |
| 434 | | } |
| 435 | | |
| 436 | | ExpectErr(tl, ';'); |
| 437 | | vcc_NextToken(tl); |
| 438 | | } |
| 439 | | |
| 440 | | if (t_threshold != NULL || t_window != NULL) { |
| 441 | | if (t_threshold == NULL && t_window != NULL) { |
| 442 | | vsb_printf(tl->sb, |
| 443 | | "Must specify .threshold with .window\n"); |
| 444 | | vcc_ErrWhere(tl, t_window); |
| 445 | | return; |
| 446 | | } else if (t_threshold != NULL && t_window == NULL) { |
| 447 | | if (threshold > 64) { |
| 448 | | vsb_printf(tl->sb, |
| 449 | | "Threshold must be 64 or less.\n"); |
| 450 | | vcc_ErrWhere(tl, t_threshold); |
| 451 | | return; |
| 452 | | } |
| 453 | | window = threshold + 1; |
| 454 | | } else if (window > 64) { |
| 455 | | AN(t_window); |
| 456 | | vsb_printf(tl->sb, "Window must be 64 or less.\n"); |
| 457 | | vcc_ErrWhere(tl, t_window); |
| 458 | | return; |
| 459 | | } |
| 460 | | if (threshold > window ) { |
| 461 | | vsb_printf(tl->sb, |
| 462 | | "Threshold can not be greater than window.\n"); |
| 463 | | AN(t_threshold); |
| 464 | | vcc_ErrWhere(tl, t_threshold); |
| 465 | | AN(t_window); |
| 466 | | vcc_ErrWhere(tl, t_window); |
| 467 | | } |
| 468 | | Fb(tl, 0, "\t\t.window = %u,\n", window); |
| 469 | | Fb(tl, 0, "\t\t.threshold = %u,\n", threshold); |
| 470 | | } |
| 471 | | if (t_initial != NULL) |
| 472 | | Fb(tl, 0, "\t\t.initial = %u,\n", initial); |
| 473 | | else |
| 474 | | Fb(tl, 0, "\t\t.initial = ~0U,\n", initial); |
| 475 | | if (status > 0) |
| 476 | | Fb(tl, 0, "\t\t.exp_status = %u,\n", status); |
| 477 | | Fb(tl, 0, "\t},\n"); |
| 478 | | ExpectErr(tl, '}'); |
| 479 | | vcc_NextToken(tl); |
| 480 | | } |
| 481 | | |
| 482 | | /*-------------------------------------------------------------------- |
| 483 | | * Parse and emit a backend host definition |
| 484 | | * |
| 485 | | * The struct vrt_backend is emitted to Fh(). |
| 486 | | */ |
| 487 | | |
| 488 | | static void |
| 489 | | vcc_ParseHostDef(struct tokenlist *tl, int serial, const char *vgcname) |
| 490 | | { |
| 491 | | struct token *t_field; |
| 492 | | struct token *t_first; |
| 493 | | struct token *t_host = NULL; |
| 494 | | struct token *t_port = NULL; |
| 495 | | struct token *t_hosthdr = NULL; |
| 496 | | unsigned saint = UINT_MAX; |
| 497 | | const char *ep; |
| 498 | | struct fld_spec *fs; |
| 499 | | struct vsb *vsb; |
| 500 | | unsigned u; |
| 501 | | |
| 502 | | Fh(tl, 1, "\n#define VGC_backend_%s %d\n", vgcname, tl->ndirector); |
| 503 | | |
| 504 | | fs = vcc_FldSpec(tl, |
| 505 | | "!host", |
| 506 | | "?port", |
| 507 | | "?host_header", |
| 508 | | "?connect_timeout", |
| 509 | | "?first_byte_timeout", |
| 510 | | "?between_bytes_timeout", |
| 511 | | "?probe", |
| 512 | | "?max_connections", |
| 513 | | "?saintmode_threshold", |
| 514 | | NULL); |
| 515 | | t_first = tl->t; |
| 516 | | |
| 517 | | ExpectErr(tl, '{'); |
| 518 | | vcc_NextToken(tl); |
| 519 | | |
| 520 | | vsb = vsb_newauto(); |
| 521 | | AN(vsb); |
| 522 | | tl->fb = vsb; |
| 523 | | |
| 524 | | Fb(tl, 0, "\nstatic const struct vrt_backend vgc_dir_priv_%s = {\n", |
| 525 | | vgcname); |
| 526 | | |
| 527 | | Fb(tl, 0, "\t.vcl_name = \"%.*s", PF(tl->t_dir)); |
| 528 | | if (serial >= 0) |
| 529 | | Fb(tl, 0, "[%d]", serial); |
| 530 | | Fb(tl, 0, "\",\n"); |
| 531 | | |
| 532 | | /* Check for old syntax */ |
| 533 | | if (tl->t->tok == ID && vcc_IdIs(tl->t, "set")) { |
| 534 | | vsb_printf(tl->sb, |
| 535 | | "NB: Backend Syntax has changed:\n" |
| 536 | | "Remove \"set\" and \"backend\" in front" |
| 537 | | " of backend fields.\n" ); |
| 538 | | vcc_ErrToken(tl, tl->t); |
| 539 | | vsb_printf(tl->sb, " at "); |
| 540 | | vcc_ErrWhere(tl, tl->t); |
| 541 | | return; |
| 542 | | } |
| 543 | | |
| 544 | | while (tl->t->tok != '}') { |
| 545 | | |
| 546 | | vcc_IsField(tl, &t_field, fs); |
| 547 | | ERRCHK(tl); |
| 548 | | if (vcc_IdIs(t_field, "host")) { |
| 549 | | ExpectErr(tl, CSTR); |
| 550 | | assert(tl->t->dec != NULL); |
| 551 | | t_host = tl->t; |
| 552 | | vcc_NextToken(tl); |
| 553 | | ExpectErr(tl, ';'); |
| 554 | | vcc_NextToken(tl); |
| 555 | | } else if (vcc_IdIs(t_field, "port")) { |
| 556 | | ExpectErr(tl, CSTR); |
| 557 | | assert(tl->t->dec != NULL); |
| 558 | | t_port = tl->t; |
| 559 | | vcc_NextToken(tl); |
| 560 | | ExpectErr(tl, ';'); |
| 561 | | vcc_NextToken(tl); |
| 562 | | } else if (vcc_IdIs(t_field, "host_header")) { |
| 563 | | ExpectErr(tl, CSTR); |
| 564 | | assert(tl->t->dec != NULL); |
| 565 | | t_hosthdr = tl->t; |
| 566 | | vcc_NextToken(tl); |
| 567 | | ExpectErr(tl, ';'); |
| 568 | | vcc_NextToken(tl); |
| 569 | | } else if (vcc_IdIs(t_field, "connect_timeout")) { |
| 570 | | Fb(tl, 0, "\t.connect_timeout = "); |
| 571 | | vcc_TimeVal(tl); |
| 572 | | ERRCHK(tl); |
| 573 | | Fb(tl, 0, ",\n"); |
| 574 | | ExpectErr(tl, ';'); |
| 575 | | vcc_NextToken(tl); |
| 576 | | } else if (vcc_IdIs(t_field, "first_byte_timeout")) { |
| 577 | | Fb(tl, 0, "\t.first_byte_timeout = "); |
| 578 | | vcc_TimeVal(tl); |
| 579 | | ERRCHK(tl); |
| 580 | | Fb(tl, 0, ",\n"); |
| 581 | | ExpectErr(tl, ';'); |
| 582 | | vcc_NextToken(tl); |
| 583 | | } else if (vcc_IdIs(t_field, "between_bytes_timeout")) { |
| 584 | | Fb(tl, 0, "\t.between_bytes_timeout = "); |
| 585 | | vcc_TimeVal(tl); |
| 586 | | ERRCHK(tl); |
| 587 | | Fb(tl, 0, ",\n"); |
| 588 | | ExpectErr(tl, ';'); |
| 589 | | vcc_NextToken(tl); |
| 590 | | } else if (vcc_IdIs(t_field, "max_connections")) { |
| 591 | | u = vcc_UintVal(tl); |
| 592 | | vcc_NextToken(tl); |
| 593 | | ERRCHK(tl); |
| 594 | | ExpectErr(tl, ';'); |
| 595 | | vcc_NextToken(tl); |
| 596 | | Fb(tl, 0, "\t.max_connections = %u,\n", u); |
| 597 | | } else if (vcc_IdIs(t_field, "saintmode_threshold")) { |
| 598 | | u = vcc_UintVal(tl); |
| 599 | | /* UINT_MAX == magic number to mark as unset, so |
| 600 | | * not allowed here. |
| 601 | | */ |
| 602 | | if (u == UINT_MAX) { |
| 603 | | vsb_printf(tl->sb, |
| 604 | | "Value outside allowed range: "); |
| 605 | | vcc_ErrToken(tl, tl->t); |
| 606 | | vsb_printf(tl->sb, " at\n"); |
| 607 | | vcc_ErrWhere(tl, tl->t); |
| 608 | | } |
| 609 | | vcc_NextToken(tl); |
| 610 | | ERRCHK(tl); |
| 611 | | saint = u; |
| 612 | | ExpectErr(tl, ';'); |
| 613 | | vcc_NextToken(tl); |
| 614 | | } else if (vcc_IdIs(t_field, "probe")) { |
| 615 | | vcc_ParseProbe(tl); |
| 616 | | ERRCHK(tl); |
| 617 | | } else { |
| 618 | | ErrInternal(tl); |
| 619 | | return; |
| 620 | | } |
| 621 | | |
| 622 | | } |
| 623 | | |
| 624 | | vcc_FieldsOk(tl, fs); |
| 625 | | ERRCHK(tl); |
| 626 | | |
| 627 | | /* Check that the hostname makes sense */ |
| 628 | | assert(t_host != NULL); |
| 629 | | ep = CheckHostPort(t_host->dec, "80"); |
| 630 | | if (ep != NULL) { |
| 631 | | vsb_printf(tl->sb, "Backend host '%.*s': %s\n", PF(t_host), ep); |
| 632 | | vcc_ErrWhere(tl, t_host); |
| 633 | | return; |
| 634 | | } |
| 635 | | |
| 636 | | /* Check that the portname makes sense */ |
| 637 | | if (t_port != NULL) { |
| 638 | | ep = CheckHostPort("127.0.0.1", t_port->dec); |
| 639 | | if (ep != NULL) { |
| 640 | | vsb_printf(tl->sb, |
| 641 | | "Backend port '%.*s': %s\n", PF(t_port), ep); |
| 642 | | vcc_ErrWhere(tl, t_port); |
| 643 | | return; |
| 644 | | } |
| 645 | | Emit_Sockaddr(tl, t_host, t_port->dec); |
| 646 | | } else { |
| 647 | | Emit_Sockaddr(tl, t_host, "80"); |
| 648 | | } |
| 649 | | ERRCHK(tl); |
| 650 | | |
| 651 | | ExpectErr(tl, '}'); |
| 652 | | |
| 653 | | /* We have parsed it all, emit the ident string */ |
| 654 | | vcc_EmitBeIdent(tl, tl->fb, serial, t_first, tl->t); |
| 655 | | |
| 656 | | /* Emit the hosthdr field, fall back to .host if not specified */ |
| 657 | | Fb(tl, 0, "\t.hosthdr = "); |
| 658 | | if (t_hosthdr != NULL) |
| 659 | | EncToken(tl->fb, t_hosthdr); |
| 660 | | else |
| 661 | | EncToken(tl->fb, t_host); |
| 662 | | Fb(tl, 0, ",\n"); |
| 663 | | |
| 664 | | Fb(tl, 0, "\t.saintmode_threshold = %d,\n",saint); |
| 665 | | |
| 666 | | /* Close the struct */ |
| 667 | | Fb(tl, 0, "};\n"); |
| 668 | | |
| 669 | | vcc_NextToken(tl); |
| 670 | | |
| 671 | | tl->fb = NULL; |
| 672 | | vsb_finish(vsb); |
| 673 | | AZ(vsb_overflowed(vsb)); |
| 674 | | Fh(tl, 0, "%s", vsb_data(vsb)); |
| 675 | | vsb_delete(vsb); |
| 676 | | |
| 677 | | Fi(tl, 0, "\tVRT_init_dir(cli, VCL_conf.director, \"simple\",\n" |
| 678 | | "\t VGC_backend_%s, &vgc_dir_priv_%s);\n", vgcname, vgcname); |
| 679 | | Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(%s));\n", vgcname); |
| 680 | | tl->ndirector++; |
| 681 | | } |
| 682 | | |
| 683 | | /*-------------------------------------------------------------------- |
| 684 | | * Parse and emit a backend host specification. |
| 685 | | * |
| 686 | | * The syntax is the following: |
| 687 | | * |
| 688 | | * backend_spec: |
| 689 | | * name_of_backend # by reference |
| 690 | | * '{' be_elements '}' # by specification |
| 691 | | * |
| 692 | | * The struct vrt_backend is emitted to Fh(). |
| 693 | | */ |
| 694 | | |
| 695 | | void |
| 696 | | vcc_ParseBackendHost(struct tokenlist *tl, int serial, char **nm) |
| 697 | | { |
| 698 | | struct host *h; |
| 699 | | struct token *t; |
| 700 | | char vgcname[BUFSIZ]; |
| 701 | | |
| 702 | | AN(nm); |
| 703 | | *nm = NULL; |
| 704 | | if (tl->t->tok == ID) { |
| 705 | | VTAILQ_FOREACH(h, &tl->hosts, list) { |
| 706 | | if (vcc_Teq(h->name, tl->t)) |
| 707 | | break; |
| 708 | | } |
| 709 | | if (h == NULL) { |
| 710 | | vsb_printf(tl->sb, "Reference to unknown backend "); |
| 711 | | vcc_ErrToken(tl, tl->t); |
| 712 | | vsb_printf(tl->sb, " at\n"); |
| 713 | | vcc_ErrWhere(tl, tl->t); |
| 714 | | return; |
| 715 | | } |
| 716 | | vcc_AddRef(tl, h->name, R_BACKEND); |
| 717 | | vcc_NextToken(tl); |
| 718 | | ExpectErr(tl, ';'); |
| 719 | | vcc_NextToken(tl); |
| 720 | | *nm = h->vgcname; |
| 721 | | } else if (tl->t->tok == '{') { |
| 722 | | t = tl->t; |
| 723 | | |
| 724 | | sprintf(vgcname, "%.*s_%d", PF(tl->t_dir), serial); |
| 725 | | |
| 726 | | Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(_%.*s));\n", |
| 727 | | PF(tl->t_dir)); |
| 728 | | vcc_ParseHostDef(tl, serial, vgcname); |
| 729 | | if (tl->err) { |
| 730 | | vsb_printf(tl->sb, |
| 731 | | "\nIn backend host specification starting at:\n"); |
| 732 | | vcc_ErrWhere(tl, t); |
| 733 | | } |
| 734 | | *nm = strdup(vgcname); /* XXX */ |
| 735 | | |
| 736 | | return; |
| 737 | | } else { |
| 738 | | vsb_printf(tl->sb, |
| 739 | | "Expected a backend host specification here, " |
| 740 | | "either by name or by {...}\n"); |
| 741 | | vcc_ErrToken(tl, tl->t); |
| 742 | | vsb_printf(tl->sb, " at\n"); |
| 743 | | vcc_ErrWhere(tl, tl->t); |
| 744 | | return; |
| 745 | | } |
| 746 | | } |
| 747 | | |
| 748 | | /*-------------------------------------------------------------------- |
| 749 | | * Parse a plain backend aka a simple director |
| 750 | | */ |
| 751 | | |
| 752 | | static void |
| 753 | | vcc_ParseSimpleDirector(struct tokenlist *tl) |
| 754 | | { |
| 755 | | struct host *h; |
| 756 | | char vgcname[BUFSIZ]; |
| 757 | | |
| 758 | | h = TlAlloc(tl, sizeof *h); |
| 759 | | h->name = tl->t_dir; |
| 760 | | vcc_AddDef(tl, tl->t_dir, R_BACKEND); |
| 761 | | sprintf(vgcname, "_%.*s", PF(h->name)); |
| 762 | | h->vgcname = TlAlloc(tl, strlen(vgcname) + 1); |
| 763 | | strcpy(h->vgcname, vgcname); |
| 764 | | |
| 765 | | vcc_ParseHostDef(tl, -1, vgcname); |
| 766 | | ERRCHK(tl); |
| 767 | | |
| 768 | | VTAILQ_INSERT_TAIL(&tl->hosts, h, list); |
| 769 | | } |
| 770 | | |
| 771 | | /*-------------------------------------------------------------------- |
| 772 | | * Parse directors and backends |
| 773 | | */ |
| 774 | | |
| 775 | | static const struct dirlist { |
| 776 | | const char *name; |
| 777 | | parsedirector_f *func; |
| 778 | | } dirlist[] = { |
| 779 | | { "hash", vcc_ParseRandomDirector }, |
| 780 | | { "random", vcc_ParseRandomDirector }, |
| 781 | | { "client", vcc_ParseRandomDirector }, |
| 782 | | { "round-robin", vcc_ParseRoundRobinDirector }, |
| 783 | | { NULL, NULL } |
| 784 | | }; |
| 785 | | |
| 786 | | void |
| 787 | | vcc_ParseDirector(struct tokenlist *tl) |
| 788 | | { |
| 789 | | struct token *t_first; |
| 790 | | struct dirlist const *dl; |
| 791 | | |
| 792 | | t_first = tl->t; |
| 793 | | vcc_NextToken(tl); /* ID: director | backend */ |
| 794 | | |
| 795 | | vcc_ExpectCid(tl); /* ID: name */ |
| 796 | | ERRCHK(tl); |
| 797 | | tl->t_dir = tl->t; |
| 798 | | vcc_NextToken(tl); |
| 799 | | |
| 800 | | |
| 801 | | if (vcc_IdIs(t_first, "backend")) { |
| 802 | | tl->t_policy = t_first; |
| 803 | | vcc_ParseSimpleDirector(tl); |
| 804 | | } else { |
| 805 | | Fh(tl, 1, "\n#define VGC_backend__%.*s %d\n", |
| 806 | | PF(tl->t_dir), tl->ndirector); |
| 807 | | vcc_AddDef(tl, tl->t_dir, R_BACKEND); |
| 808 | | tl->ndirector++; |
| 809 | | ExpectErr(tl, ID); /* ID: policy */ |
| 810 | | tl->t_policy = tl->t; |
| 811 | | vcc_NextToken(tl); |
| 812 | | |
| 813 | | for (dl = dirlist; dl->name != NULL; dl++) |
| 814 | | if (vcc_IdIs(tl->t_policy, dl->name)) |
| 815 | | break; |
| 816 | | if (dl->name == NULL) { |
| 817 | | vsb_printf(tl->sb, "Unknown director policy: "); |
| 818 | | vcc_ErrToken(tl, tl->t_policy); |
| 819 | | vsb_printf(tl->sb, " at\n"); |
| 820 | | vcc_ErrWhere(tl, tl->t_policy); |
| 821 | | return; |
| 822 | | } |
| 823 | | ExpectErr(tl, '{'); |
| 824 | | vcc_NextToken(tl); |
| 825 | | dl->func(tl); |
| 826 | | if (!tl->err) { |
| 827 | | ExpectErr(tl, '}'); |
| 828 | | vcc_NextToken(tl); |
| 829 | | } |
| 830 | | Fi(tl, 0, |
| 831 | | "\tVRT_init_dir(cli, VCL_conf.director, \"%.*s\",\n", |
| 832 | | PF(tl->t_policy)); |
| 833 | | Fi(tl, 0, "\t VGC_backend__%.*s, &vgc_dir_priv_%.*s);\n", |
| 834 | | PF(tl->t_dir), PF(tl->t_dir)); |
| 835 | | |
| 836 | | |
| 837 | | } |
| 838 | | if (tl->err) { |
| 839 | | vsb_printf(tl->sb, |
| 840 | | "\nIn %.*s specification starting at:\n", PF(t_first)); |
| 841 | | vcc_ErrWhere(tl, t_first); |
| 842 | | return; |
| 843 | | } |
| 844 | | tl->t_policy = NULL; |
| 845 | | tl->t_dir = NULL; |
| 846 | | } |