Commit 4c2ae4ea authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Swing the infamous "phk viking axe" and deorbit directors from

the core code.

In the future directors belong in VMODs, which takes the need to
hack VCC code out of the learning-curve.

Certain directors have already been moved to VMODs, others will
need a bit of brain-surgery before they go there.

This commit is to get them out of the way for V4
parent 12e571b3
......@@ -19,8 +19,6 @@ varnishd_SOURCES = \
cache/cache_busyobj.c \
cache/cache_cli.c \
cache/cache_dir.c \
cache/cache_dir_dns.c \
cache/cache_dir_random.c \
cache/cache_esi_deliver.c \
cache/cache_esi_fetch.c \
cache/cache_esi_parse.c \
......
......@@ -388,26 +388,6 @@ vbe_GetVbe(struct req *req, struct vdi_simple *vs)
return (vc);
}
/*--------------------------------------------------------------------
* Returns the backend if and only if the this is a simple director.
* XXX: Needs a better name and possibly needs a better general approach.
* XXX: This is mainly used by the DNS director to fetch the actual backend
* XXX: so it can compare DNS lookups with the actual IP.
*/
struct backend *
vdi_get_backend_if_simple(const struct director *d)
{
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
struct vdi_simple *vs, *vs2;
vs2 = d->priv;
if (vs2->magic != VDI_SIMPLE_MAGIC)
return (NULL);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_SIMPLE_MAGIC);
return (vs->backend);
}
/*--------------------------------------------------------------------
*
*/
......
......@@ -167,7 +167,6 @@ struct vbc {
/* cache_backend.c */
void VBE_ReleaseConn(struct vbc *vc);
struct backend *vdi_get_backend_if_simple(const struct director *d);
/* cache_backend_cfg.c */
void VBE_DropRefConn(struct backend *);
......@@ -184,7 +183,3 @@ void VBP_Summary(struct cli *cli, const struct vbp_target *vt);
/* Init functions for directors */
typedef void dir_init_f(struct cli *, struct director **, int , const void*);
dir_init_f VRT_init_dir_simple;
dir_init_f VRT_init_dir_dns;
dir_init_f VRT_init_dir_hash;
dir_init_f VRT_init_dir_random;
dir_init_f VRT_init_dir_client;
......@@ -251,14 +251,6 @@ VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
ASSERT_CLI();
if (!strcmp(name, "simple"))
VRT_init_dir_simple(cli, dir, idx, priv);
else if (!strcmp(name, "hash"))
VRT_init_dir_hash(cli, dir, idx, priv);
else if (!strcmp(name, "random"))
VRT_init_dir_random(cli, dir, idx, priv);
else if (!strcmp(name, "dns"))
VRT_init_dir_dns(cli, dir, idx, priv);
else if (!strcmp(name, "client"))
VRT_init_dir_client(cli, dir, idx, priv);
else
INCOMPL();
}
......
This diff is collapsed.
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2011 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This code is shared between the random, client and hash directors, because
* they share the same properties and most of the same selection logic.
*
* The random director picks a backend on random.
*
* The hash director picks based on the hash from vcl_hash{}
*
* The client director picks based on client identity or IP-address
*
* In all cases, the choice is by weight of the healthy subset of
* configured backends.
*
* Failures to get a connection are retried, here all three policies
* fall back to a deterministically random choice, by weight in the
* healthy subset.
*
*/
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include "cache.h"
#include "cache_backend.h"
#include "vend.h"
#include "vrt.h"
#include "vsha256.h"
/*--------------------------------------------------------------------*/
struct vdi_random_host {
struct director *backend;
double weight;
};
enum crit_e {c_random, c_hash, c_client};
struct vdi_random {
unsigned magic;
#define VDI_RANDOM_MAGIC 0x3771ae23
struct director dir;
enum crit_e criteria;
unsigned retries;
double tot_weight;
struct vdi_random_host *hosts;
unsigned nhosts;
};
/*
* Applies sha256 using the given context and input/length, and returns
* a double in the range [0...1[ based on the hash.
*/
static double
vdi_random_sha(const char *input, ssize_t len)
{
struct SHA256Context ctx;
uint8_t sign[SHA256_LEN];
AN(input);
SHA256_Init(&ctx);
SHA256_Update(&ctx, input, len);
SHA256_Final(sign, &ctx);
return (scalbn(vle32dec(sign), -32));
}
/*
* Sets up the initial seed for picking a backend according to policy.
*/
static double
vdi_random_init_seed(const struct vdi_random *vs, const struct req *req)
{
const char *p;
double retval;
switch (vs->criteria) {
case c_client:
if (req->client_identity != NULL)
p = req->client_identity;
else
p = req->sp->addr;
retval = vdi_random_sha(p, strlen(p));
break;
case c_hash:
AN(req->digest);
retval = scalbn(vle32dec(req->digest), -32);
break;
case c_random:
default:
retval = scalbn(random(), -31);
break;
}
return (retval);
}
/*
* Find the healthy backend corresponding to the weight r [0...1[
*/
static struct vbc *
vdi_random_pick_one(struct req *req, const struct vdi_random *vs, double r,
int retries)
{
double w[vs->nhosts];
int i;
double s1, s2;
struct vbc *vbc;
/* Sum up the weights of all backends */
memset(w, 0, sizeof w);
s1 = 0.0;
for (i = 0; i < vs->nhosts; i++) {
w[i] = vs->hosts[i].weight;
s1 += w[i];
}
if (s1 == 0.0)
return (NULL);
while (retries-- > 0) {
assert(r >= 0.0 && r < 1.0);
r *= s1;
s2 = 0.0;
for (i = 0; i < vs->nhosts; i++) {
s2 += w[i];
if (r >= s2)
continue;
if (!VDI_Healthy(vs->hosts[i].backend, req))
break;
vbc = VDI_GetFd(vs->hosts[i].backend, req);
if (vbc == NULL)
break;
return (vbc);
}
/*
* Rescale and rotate r's relative position this backends
* window onto the remaining backends and try again.
*/
r -= (s2 - w[i]); // r in [0...w[i][
r /= w[i]; // r in [0...1[
r *= (s1 - w[i])/s1; // r in [0...1-W[i]]
r += s2 / s1; // rotate
if (r >= 1.0)
r -= 1.0;
}
return (NULL);
}
/*
* Try the specified number of times to get a backend.
* First one according to policy, after that, deterministically
* random by rehashing the key.
*/
static struct vbc *
vdi_random_getfd(const struct director *d, struct req *req)
{
struct vdi_random *vs;
double r;
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
r = vdi_random_init_seed(vs, req);
return (vdi_random_pick_one(req, vs, r, vs->retries));
}
/*
* Healthy if just a single backend is...
* XXX: we should really have a weight param/criteria here
*/
static unsigned
vdi_random_healthy(const struct director *d, const struct req *req)
{
struct vdi_random *vs;
int i;
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
for (i = 0; i < vs->nhosts; i++) {
if (VDI_Healthy(vs->hosts[i].backend, req))
return (1);
}
return (0);
}
static void
vdi_random_fini(const struct director *d)
{
struct vdi_random *vs;
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_RANDOM_MAGIC);
free(vs->hosts);
free(vs->dir.vcl_name);
vs->dir.magic = 0;
FREE_OBJ(vs);
}
static void
vrt_init(struct cli *cli, struct director **bp, int idx,
const void *priv, enum crit_e criteria)
{
const struct vrt_dir_random *t;
struct vdi_random *vs;
const struct vrt_dir_random_entry *te;
struct vdi_random_host *vh;
int i;
ASSERT_CLI();
(void)cli;
t = priv;
ALLOC_OBJ(vs, VDI_RANDOM_MAGIC);
XXXAN(vs);
vs->hosts = calloc(sizeof *vh, t->nmember);
XXXAN(vs->hosts);
vs->dir.magic = DIRECTOR_MAGIC;
vs->dir.priv = vs;
vs->dir.name = "random";
REPLACE(vs->dir.vcl_name, t->name);
vs->dir.getfd = vdi_random_getfd;
vs->dir.fini = vdi_random_fini;
vs->dir.healthy = vdi_random_healthy;
vs->criteria = criteria;
vs->retries = t->retries;
if (vs->retries == 0)
vs->retries = t->nmember;
vh = vs->hosts;
te = t->members;
vs->tot_weight = 0.;
for (i = 0; i < t->nmember; i++, vh++, te++) {
assert(te->weight > 0.0);
vh->weight = te->weight;
vs->tot_weight += vh->weight;
vh->backend = bp[te->host];
AN(vh->backend);
}
vs->nhosts = t->nmember;
bp[idx] = &vs->dir;
}
void
VRT_init_dir_random(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init(cli, bp, idx, priv, c_random);
}
void
VRT_init_dir_hash(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init(cli, bp, idx, priv, c_hash);
}
void
VRT_init_dir_client(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init(cli, bp, idx, priv, c_client);
}
varnishtest "Test DNS director order"
varnish v1 -vcl {
backend test {
.host = "192.168.0.1";
}
director foo dns {
{ .backend = { .host = "127.0.0.1";} }
.list = {
"192.168.0.0"/24;
}
}
sub vcl_recv {
set req.backend = foo;
if (req.http.x-aa) {
set req.backend = test;
}
}
} -start
varnishtest "Test proper fallbacks of client director"
server s1 {
rxreq
txresp -status 200
accept
rxreq
txresp -status 200
} -start
varnish v1 -vcl+backend {
director foo client{
.retries = 5;
{ .backend = { .host = "${bad_ip}"; .port = "9090"; } .weight = 1; }
{ .backend = s1; .weight = 1;}
}
director bar client{
.retries = 1;
{ .backend = { .host = "${bad_ip}"; .port = "9090"; } .weight = 1; }
{ .backend = s1; .weight = 1;}
}
sub vcl_recv {
if (req.url ~ "/one") {
set req.backend = foo;
} else {
set req.backend = bar;
}
# Carefully chosen seed that'll give us bad backend on
# first try and good on second.
set client.identity = "1.4";
return (pass);
}
} -start
client c1 {
txreq -url "/one"
rxresp
expect resp.status == 200
txreq -url "/two"
rxresp
expect resp.status == 503
} -run
varnish v1 -expect backend_fail == 2
varnishtest "DNS director tramples on other backends"
server s1 {
rxreq
txresp
} -start
server s2 {
rxreq
txresp
} -start
varnish v1 -vcl {
backend b1 {
.host = "${s1_addr}";
.port = "${s1_port}";
}
director d1 dns {
.list = {
.port = "80";
"${bad_ip}"/32;
}
.ttl = 1m;
}
backend b2 {
.host = "${s2_addr}";
.port = "${s2_port}";
}
sub vcl_recv {
set req.backend = d1;
if (req.url == "/1") {
set req.backend = b1;
} else {
set req.backend = b2;
}
}
} -start
client c1 {
txreq -url "/1"
rxresp
txreq -url "/2"
rxresp
} -run
varnishtest "VCL: test syntax/semantic checks on backend decls. (vcc_backend.c)"
# Missing backend
varnish v1 -errvcl {No backends or directors found in VCL program} {
}
# Reference to non-existent backend
varnish v1 -errvcl {Symbol not found: 'b2' (expected type BACKEND):} {
backend b1 {
.host = "127.0.0.1";
}
sub vcl_recv {
set req.backend = b2;
}
}
# Missing .host
varnish v1 -errvcl {Mandatory field 'host' missing.} {
backend b1 {
.port = "http";
}
}
# Too many .host
varnish v1 -errvcl {Field 'host' redefined at:} {
backend b1 {
.host = "foo";
.host = "bar";
}
}
# Too many .port
varnish v1 -errvcl {Field 'port' redefined at:} {
backend b1 {
.host = "foo";
.port = "http";
.port = "https";
}
}
# Too many .connect_timeout
varnish v1 -errvcl {Field 'connect_timeout' redefined at:} {
backend b1 {
.host = "foo";
.connect_timeout = 1m;
.connect_timeout = 1m;
}
}
# unknown field
varnish v1 -errvcl {Unknown field: 'foobar' at} {
backend b1 {
.host = "127.0.0.1";
.foobar = 123;
}
}
# Check backend reference by name
varnish v1 -errvcl {Expected a backend host specification here, either by name or by {...}} {
backend b1 { .host = "${bad_ip}"; }
director r1 random {
{ .weight = 1; .backend = b1; }
{ .weight = 1; .backend = { .host = "127.0.0.3"; } }
{ .weight = 1; .backend = 3745; } // Brownie points for getting the joke
}
}
# Check backend reference by name
varnish v1 -errvcl "Expected '{' got 'b1'" {
backend b1 { .host = "${bad_ip}"; }
backend b2 b1;
}
# Check backend reference by non-C-compat name
varnish v1 -errvcl {Identifier 'b-1' contains illegal characters, use [0-9a-zA-Z_] only.} {
backend b-1 { .host = "${bad_ip}"; }
}
# Check backend reference by non-C-compat name
varnish v1 -errvcl {Symbol not found: 'b-1' (expected type BACKEND):} {
backend b1 { .host = "${bad_ip}"; }
sub vcl_recv {
set req.backend = b-1;
}
}
# Old backend syntax
varnish v1 -errvcl {Remove "set" and "backend" in front of backend fields.} {
backend b1 {
set host = "127.0.0.1";
}
}
# Too many .connect_timeout
varnish v1 -errvcl {Unknown time unit 'q'. Legal are 's', 'm', 'h' and 'd'} {
backend b1 {
.host = "foo";
.connect_timeout = 1 q;
}
}
# Two clashing backends
varnish v1 -errvcl {Backend b1 redefined} {
backend b1 { .host = "127.0.0.1"; }
backend b1 { .host = "127.0.0.1"; }
}
# Unknown director policy
varnish v1 -errvcl {Unknown director policy: 'anarchy' at} {
director r1 anarchy { .host = "127.0.0.1"; }
}
varnish v1 -errvcl {Backend host "v00002.freebsd.dk": resolves to multiple ipv4 addresses.} {
/* too many IP numbers */
backend b1 { .host = "v00002.freebsd.dk"; }
}
varnish v1 -errvcl {Backend host '"////."' could not be resolved to an IP address:} {
backend b1 { .host = "////."; }
}
varnish v1 -errvcl {Backend host '"127.0.0.1"' could not be resolved to an IP address:} {
backend b1 { .host = "127.0.0.1"; .port = "////."; }
}
varnish v1 -errvcl {Unknown field: 'foobar' at} {
backend b1 {
.host = "127.0.0.1";
.foobar = "foo";
}
}
varnish v1 -errvcl {Unknown field: 'ice' at} {
backend b1 {
.host = "127.0.0.1";
.probe = {
.ice = "vanilla";
}
}
}
varnish v1 -errvcl {Must specify .threshold with .window} {
backend b1 {
.host = "127.0.0.1";
.probe = { .window = 8; }
}
}
varnish v1 -errvcl {Threshold must be 64 or less.} {
backend b1 {
.host = "127.0.0.1";
.probe = { .threshold = 65; }
}
}
varnish v1 -vcl {
backend b1 {
.host = "127.0.0.1";
.probe = { .threshold = 64; }
}
}
varnish v1 -errvcl {Window must be 64 or less.} {
backend b1 {
.host = "127.0.0.1";
.probe = { .threshold = 32; .window = 65; }
}
}
varnish v1 -vcl {
backend b1 {
.host = "127.0.0.1";
.probe = { .threshold = 32; .window = 64; }
}
}
varnish v1 -errvcl {Threshold can not be greater than window.} {
backend b1 {
.host = "127.0.0.1";
.probe = { .threshold = 32; .window = 31; }
}
}
varnish v1 -errvcl {Must specify .expected_response with exactly three digits (100 <= x <= 999)} {
backend b1 {
.host = "127.0.0.1";
.probe = { .expected_response = 13; }
}
}
varnishtest "VCL: test syntax/semantic checks on director decls."
# syntax in inline backend
varnish v1 -errvcl {Unknown field: 'foo' at} {
director r1 random {
{ .backend = { .foo = 2; }; .weight = 1;}
}
}
# reference to unknown backend host
varnish v1 -errvcl {Reference to unknown backend 'b2' at} {
director r1 random {
{ .backend = b2; .weight = 1; }
}
}
# missing backend
varnish v1 -errvcl {Mandatory field 'backend' missing.} {
director r1 random {
{ .weight = 1; }
}
}
# invalid weight
varnish v1 -errvcl {Expected CNUM got 'k'} {
director r1 random {
{ .backend = {.host = "127.0.0.1";} .weight = k; }
}
}
# invalid weight
varnish v1 -errvcl {The .weight must be higher than zero.} {
director r1 random {
{ .backend = {.host = "127.0.0.1";} .weight = 0; }
}
}
# retries spec
varnish v1 -vcl {
director r1 random {
.retries = 3;
{ .backend = {.host = "127.0.0.1";} .weight = 1; }
}
}
varnish v1 -errvcl {Unknown field: 'foobar' at} {
director r1 random {
.foobar = 3;
{ .backend = {.host = "127.0.0.1";} .weight = 1; }
}
}
varnish v1 -errvcl {Expected CNUM got 'x'} {
director r1 random {
.retries = x;
{ .backend = {.host = "127.0.0.1";} .weight = 1; }
}
}
varnishtest "VCL: test creation/destruction of backends"
varnish v1 -vcl {
backend b1 {
.host = "127.0.0.1";
}
}
varnish v1 -cli vcl.list
varnish v1 -start
varnish v1 -expect n_backend == 1
varnish v1 -vcl {
backend b1 {
.host = "127.0.0.1";
}
}
varnish v1 -expect n_backend == 1
varnish v1 -expect n_vcl == 2
varnish v1 -vcl {
backend b2 {
.host = "127.0.0.1";
}
}
varnish v1 -expect n_backend == 2
# give the expiry thread a chance to let go of its VCL
delay 2
varnish v1 -expect n_vcl == 3
varnish v1 -cli "vcl.discard vcl1"
varnish v1 -expect n_backend == 2
varnish v1 -expect n_vcl == 2
varnish v1 -cli "vcl.discard vcl2"
varnish v1 -expect n_backend == 1
varnish v1 -expect n_vcl == 1
varnish v1 -vcl {
backend b2 {
.host = "127.0.0.1";
}
director r1 random {
{ .backend = { .host = "127.0.0.1"; } .weight = 1; }
{ .backend = b2 ; .weight = 1; }
}
sub vcl_recv {
set req.backend = r1;
}
}
varnish v1 -expect n_backend == 2
varnish v1 -expect n_vcl == 2
varnish v1 -cli "vcl.use vcl3"
varnish v1 -expect n_backend == 2
varnish v1 -expect n_vcl == 2
varnish v1 -cli "vcl.discard vcl4"
varnish v1 -cli "vcl.list"
delay 1
varnish v1 -cli "vcl.list"
varnish v1 -expect n_backend == 1
varnish v1 -expect n_vcl == 1
varnishtest "Client director"
server s1 {
rxreq
txresp -hdr "be: s1" -bodylen 1
} -start
server s2 {
rxreq
txresp -hdr "be: s2" -bodylen 2
rxreq
txresp -hdr "be: s2" -bodylen 4
} -start
varnish v1 -vcl+backend {
director d1 client {
{ .backend = s1; .weight = 1; }
{ .backend = s2; .weight = 1; }
}
sub vcl_recv {
set req.backend = d1;
if (req.http.id) {
set client.identity = req.http.id;
}
return (pass);
}
sub vcl_fetch {
set beresp.http.be_ip = beresp.backend.ip;
set beresp.http.be_port = beresp.backend.port;
set beresp.http.be_name = beresp.backend.name;
}
sub vcl_deliver {
set resp.http.id = client.identity;
}
} -start
client c1 {
txreq
rxresp
expect resp.http.id == "127.0.0.1"
expect resp.http.be == s2
expect resp.bodylen == 2
txreq -hdr "id: foo"
rxresp
expect resp.http.id == "foo"
expect resp.http.be == s1
expect resp.bodylen == 1
txreq -hdr "id: baz"
rxresp
expect resp.http.id == "baz"
expect resp.http.be == s2
expect resp.bodylen == 4
} -run
varnishtest "DNS director"
server s1 {
rxreq
txresp
} -start
varnish v1 -vcl+backend {
director d1 dns {
{ .backend = s1; }
}
sub vcl_recv {
set req.backend = d1;
return (pass);
}
} -start
client c1 {
txreq -hdr "Host: localhost"
rxresp
expect resp.status == 200
txreq -hdr "Host: .......coco-x-zamzam-i-cant-bother-making-it-random"
rxresp
expect resp.status == 503
} -run
varnish v2 -vcl {
director directorname dns {
.list = {
.host_header = "www.example.com";
.port = "80";
.connect_timeout = 0.4s;
"192.168.15.0"/24;
"192.168.16.128"/25;
}
.ttl = 5m;
.suffix = "internal.example.net";
}
}
varnishtest "DNS director bad VCL tests"
varnish v1 -errvcl {Expected '.' got '192'} {
director directorname dns {
.list = {
192.168.15.0/24;
}
}
}
varnish v1 -errvcl "Expected '.' got '}'" {
director directorname dns {
.list = {
.host_header = "www.example.com";
.port = "80";
.connect_timeout = 0.4s;
}
}
}
varnish v9 -errvcl {Unknown field: 'host_hdr' at} {
director directorname dns {
.list = {
.host_hdr = "192.168.1.1";
"192.168.1.2";
}
}
}
varnish v1 -errvcl {Unknown field: 'host_hdr' at} {
director directorname dns {
.list = {
.host_hdr = "www.example.com";
"192.168.16.128"/25;
}
}
}
varnish v1 -errvcl {Expected CSTR got '80'} {
director directorname dns {
.list = {
.port = 80;
"192.168.15.0"/24;
}
}
}
# XXX: reported token should be improved
varnish v1 -errvcl {IP and network mask not compatible: ';' at} {
director directorname dns {
.list = {
"192.168.15.0"/33;
}
}
}
# XXX: reported token should be improved
varnish v1 -errvcl {IP and network mask not compatible: ';' at} {
director directorname dns {
.list = {
"192.168.16.255"/24;
}
}
}
......@@ -17,8 +17,6 @@ libvcl_la_SOURCES = \
vcc_backend.c \
vcc_backend_util.c \
vcc_compile.c \
vcc_dir_random.c \
vcc_dir_dns.c \
vcc_expr.c \
vcc_parse.c \
vcc_fixed_token.c \
......
......@@ -107,7 +107,7 @@ struct foo_proto {
socklen_t l;
};
void
static void
Emit_Sockaddr(struct vcc *tl, const struct token *t_host, const char *port)
{
struct foo_proto protos[3], *pp;
......@@ -585,68 +585,6 @@ vcc_ParseHostDef(struct vcc *tl, int serial, const char *vgcname)
tl->ndirector++;
}
/*--------------------------------------------------------------------
* Parse and emit a backend host specification.
*
* The syntax is the following:
*
* backend_spec:
* name_of_backend # by reference
* '{' be_elements '}' # by specification
*
* The struct vrt_backend is emitted to Fh().
*/
void
vcc_ParseBackendHost(struct vcc *tl, int serial, char **nm)
{
struct host *h;
struct token *t;
char vgcname[BUFSIZ];
AN(nm);
*nm = NULL;
if (tl->t->tok == ID) {
VTAILQ_FOREACH(h, &tl->hosts, list) {
if (vcc_Teq(h->name, tl->t))
break;
}
if (h == NULL) {
VSB_printf(tl->sb, "Reference to unknown backend ");
vcc_ErrToken(tl, tl->t);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
return;
}
vcc_AddRef(tl, h->name, SYM_BACKEND);
vcc_NextToken(tl);
SkipToken(tl, ';');
*nm = h->vgcname;
} else if (tl->t->tok == '{') {
t = tl->t;
sprintf(vgcname, "%.*s_%d", PF(tl->t_dir), serial);
vcc_ParseHostDef(tl, serial, vgcname);
if (tl->err) {
VSB_printf(tl->sb,
"\nIn backend host specification starting at:\n");
vcc_ErrWhere(tl, t);
}
*nm = strdup(vgcname); /* XXX */
return;
} else {
VSB_printf(tl->sb,
"Expected a backend host specification here, "
"either by name or by {...}\n");
vcc_ErrToken(tl, tl->t);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
return;
}
}
/*--------------------------------------------------------------------
* Tell rest of compiler about a backend
*/
......@@ -696,25 +634,10 @@ vcc_ParseSimpleDirector(struct vcc *tl)
* Parse directors and backends
*/
static const struct dirlist {
const char *name;
parsedirector_f *func;
} dirlist[] = {
{ "hash", vcc_ParseRandomDirector },
{ "random", vcc_ParseRandomDirector },
{ "client", vcc_ParseRandomDirector },
{ "round-robin", NULL },
{ "fallback", NULL },
{ "dns", vcc_ParseDnsDirector },
{ NULL, NULL }
};
void
vcc_ParseDirector(struct vcc *tl)
{
struct token *t_first;
struct dirlist const *dl;
int isfirst;
t_first = tl->t;
......@@ -738,45 +661,10 @@ vcc_ParseDirector(struct vcc *tl)
tl->t_policy = t_first;
vcc_ParseSimpleDirector(tl);
} else {
vcc_DefBackend(tl, tl->t_dir);
ERRCHK(tl);
ExpectErr(tl, ID); /* ID: policy */
tl->t_policy = tl->t;
vcc_NextToken(tl);
for (dl = dirlist; dl->name != NULL; dl++)
if (vcc_IdIs(tl->t_policy, dl->name))
break;
if (dl->name == NULL) {
VSB_printf(tl->sb, "Unknown director policy: ");
vcc_ErrToken(tl, tl->t_policy);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t_policy);
return;
}
if (dl->func == NULL) {
VSB_printf(tl->sb,
"\n%.*s director are now in VMOD.directors\n",
PF(tl->t_policy));
vcc_ErrWhere(tl, tl->t_policy);
return;
}
Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(_%.*s));\n",
PF(tl->t_dir));
SkipToken(tl, '{');
dl->func(tl);
if (!tl->err)
SkipToken(tl, '}');
Fh(tl, 1, "\n#define VGC_backend__%.*s %d\n",
PF(tl->t_dir), tl->ndirector);
tl->ndirector++;
Fi(tl, 0,
"\tVRT_init_dir(cli, VCL_conf.director, \"%.*s\",\n",
PF(tl->t_policy));
Fi(tl, 0, "\t VGC_backend__%.*s, &vgc_dir_priv_%.*s);\n",
PF(tl->t_dir), PF(tl->t_dir));
VSB_printf(tl->sb,
"\ndirectors are now in VMOD.directors\n");
vcc_ErrWhere(tl, tl->t_policy);
return;
}
if (tl->err) {
VSB_printf(tl->sb,
......
......@@ -51,7 +51,7 @@ struct fld_spec {
struct token *found;
};
void
static void
vcc_ResetFldSpec(struct fld_spec *f)
{
......
......@@ -227,19 +227,13 @@ int vcc_ParseAction(struct vcc *tl);
/* vcc_backend.c */
struct fld_spec;
typedef void parsedirector_f(struct vcc *tl);
void vcc_ParseProbe(struct vcc *tl);
void vcc_ParseDirector(struct vcc *tl);
void vcc_ParseBackendHost(struct vcc *tl, int serial, char **nm);
struct fld_spec * vcc_FldSpec(struct vcc *tl, const char *first, ...);
void vcc_ResetFldSpec(struct fld_spec *f);
void vcc_IsField(struct vcc *tl, struct token **t, struct fld_spec *fs);
void vcc_FieldsOk(struct vcc *tl, const struct fld_spec *fs);
void Emit_Sockaddr(struct vcc *tl, const struct token *t_host,
const char *port);
/* vcc_compile.c */
extern struct method method_tab[];
/*
......@@ -269,9 +263,6 @@ char *TlDupTok(struct vcc *tl, const struct token *tok);
void EncString(struct vsb *sb, const char *b, const char *e, int mode);
/* vcc_dir_random.c */
parsedirector_f vcc_ParseRandomDirector;
/* vcc_expr.c */
void vcc_Duration(struct vcc *tl, double *);
unsigned vcc_UintVal(struct vcc *tl);
......@@ -284,9 +275,6 @@ void vcc_Eval_Func(struct vcc *tl, const char *cfunc, const char *extra,
const char *name, const char *args);
sym_expr_t vcc_Eval_Backend;
/* vcc_dir_dns.c */
parsedirector_f vcc_ParseDnsDirector;
/* vcc_obj.c */
extern const struct var vcc_vars[];
......@@ -319,7 +307,6 @@ void vcc_ErrWhere(struct vcc *, const struct token *);
void vcc_ErrWhere2(struct vcc *, const struct token *, const struct token *);
void vcc__Expect(struct vcc *tl, unsigned tok, unsigned line);
int vcc_Teq(const struct token *t1, const struct token *t2);
int vcc_IdIs(const struct token *t, const char *p);
void vcc_ExpectCid(struct vcc *tl);
void vcc_Lexer(struct vcc *tl, struct source *sp);
......
/*-
* Copyright (c) 2009-2010 Varnish Software AS
* All rights reserved.
*
* Author: Kristian Lyngstol <kristian@bohemians.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "vcc_compile.h"
/*--------------------------------------------------------------------
* Parse directors
*/
static struct vcc_dir_backend_defaults {
char *port;
char *hostheader;
double connect_timeout;
double first_byte_timeout;
double between_bytes_timeout;
unsigned max_connections;
unsigned saint;
} b_defaults;
static void
vcc_dir_initialize_defaults(void)
{
b_defaults.port = NULL;
b_defaults.hostheader = NULL;
b_defaults.connect_timeout = -1.0;
b_defaults.first_byte_timeout = -1.0;
b_defaults.between_bytes_timeout = -1.0;
b_defaults.max_connections = UINT_MAX;
b_defaults.saint = UINT_MAX;
}
static const struct token *dns_first;
static void
print_backend(struct vcc *tl,
int serial,
const uint8_t *ip)
{
char vgcname[BUFSIZ];
char strip[16];
struct token tmptok;
struct vsb *vsb;
bprintf(strip, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]);
tmptok.dec = strip;
bprintf(vgcname, "%.*s_%d", PF(tl->t_dir), serial);
vsb = VSB_new_auto();
AN(vsb);
tl->fb = vsb;
Fc(tl, 0, "\t{ .host = VGC_backend_%s },\n",vgcname);
Fh(tl, 1, "\n#define VGC_backend_%s %d\n", vgcname, tl->ndirector);
Fb(tl, 0, "\nstatic const struct vrt_backend vgc_dir_priv_%s = {\n",
vgcname);
Fb(tl, 0, "\t.vcl_name = \"%.*s", PF(tl->t_dir));
if (serial >= 0)
Fb(tl, 0, "[%d]", serial);
Fb(tl, 0, "\",\n");
Emit_Sockaddr(tl, &tmptok, b_defaults.port ? b_defaults.port : "80");
Fb(tl, 0, "\t.hosthdr = \"");
if (b_defaults.hostheader != NULL)
Fb(tl, 0, "%s", b_defaults.hostheader);
else
Fb(tl, 0, "%s", strip);
Fb(tl, 0, "\",\n");
Fb(tl, 0, "\t.saintmode_threshold = %d,\n",b_defaults.saint);
#define FB_TIMEOUT(type) do { \
if (b_defaults.type != -1.0) \
Fb(tl, 0, "\t.%s = %g,\n",#type,b_defaults.type); \
} while (0)
FB_TIMEOUT(connect_timeout);
FB_TIMEOUT(first_byte_timeout);
FB_TIMEOUT(between_bytes_timeout);
Fb(tl, 0, "};\n");
tl->fb = NULL;
AZ(VSB_finish(vsb));
Fh(tl, 0, "%s", VSB_data(vsb));
VSB_delete(vsb);
Fi(tl, 0, "\tVRT_init_dir(cli, VCL_conf.director, \"simple\",\n"
"\t VGC_backend_%s, &vgc_dir_priv_%s);\n", vgcname, vgcname);
Ff(tl, 0, "\tVRT_fini_dir(cli, VGCDIR(%s));\n", vgcname);
tl->ndirector++;
}
/*
* Output backends for all IPs in the range supplied by
* "a[0].a[1].a[2].a[3]/inmask".
*
* XXX:
* This assumes that a uint32_t can be safely accessed as an array of 4
* uint8_ts.
*/
static void
vcc_dir_dns_makebackend(struct vcc *tl,
int *serial,
const unsigned char a[],
int inmask)
{
uint32_t ip4=0;
uint32_t ip4end;
uint32_t mask = UINT32_MAX << (32-inmask);
ip4 |= a[0] << 24;
ip4 |= a[1] << 16;
ip4 |= a[2] << 8;
ip4 |= a[3] ;
ip4end = ip4 | ~mask;
if (ip4 != (ip4 & mask)) {
VSB_printf(tl->sb, "IP and network mask not compatible: ");
vcc_ErrToken(tl, tl->t);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
ERRCHK(tl);
}
while (ip4 <= ip4end) {
uint8_t *b;
b=(uint8_t *)&ip4;
(*serial)++;
print_backend(tl, *serial, b);
ip4++;
}
}
static void
vcc_dir_dns_parse_backend_options(struct vcc *tl)
{
struct fld_spec *fs;
struct token *t_field;
double t;
unsigned u;
vcc_dir_initialize_defaults();
fs = vcc_FldSpec(tl,
"?port",
"?host_header",
"?connect_timeout",
"?first_byte_timeout",
"?between_bytes_timeout",
"?max_connections",
"?saintmode_threshold",
NULL);
while (tl->t->tok != CSTR) {
vcc_IsField(tl, &t_field, fs);
ERRCHK(tl);
if (vcc_IdIs(t_field, "port")) {
ExpectErr(tl, CSTR);
assert(tl->t->dec != NULL);
b_defaults.port = strdup(tl->t->dec);
assert(b_defaults.port);
vcc_NextToken(tl);
SkipToken(tl, ';');
} else if (vcc_IdIs(t_field, "host_header")) {
ExpectErr(tl, CSTR);
assert(tl->t->dec != NULL);
b_defaults.hostheader = strdup(tl->t->dec);
assert(b_defaults.hostheader);
vcc_NextToken(tl);
SkipToken(tl, ';');
} else if (vcc_IdIs(t_field, "connect_timeout")) {
vcc_Duration(tl, &t);
ERRCHK(tl);
b_defaults.connect_timeout = t;
SkipToken(tl, ';');
} else if (vcc_IdIs(t_field, "first_byte_timeout")) {
vcc_Duration(tl, &t);
ERRCHK(tl);
b_defaults.first_byte_timeout = t;
SkipToken(tl, ';');
} else if (vcc_IdIs(t_field, "between_bytes_timeout")) {
vcc_Duration(tl, &t);
ERRCHK(tl);
b_defaults.between_bytes_timeout = t;
SkipToken(tl, ';');
} else if (vcc_IdIs(t_field, "max_connections")) {
u = vcc_UintVal(tl);
ERRCHK(tl);
SkipToken(tl, ';');
b_defaults.max_connections = u;
} else if (vcc_IdIs(t_field, "saintmode_threshold")) {
u = vcc_UintVal(tl);
/* UINT_MAX == magic number to mark as unset, so
* not allowed here.
*/
if (u == UINT_MAX) {
VSB_printf(tl->sb,
"Value outside allowed range: ");
vcc_ErrToken(tl, tl->t);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
}
ERRCHK(tl);
b_defaults.saint = u;
SkipToken(tl, ';');
} else {
ErrInternal(tl);
return;
}
}
}
/* Parse a list of backends with optional /mask notation, then print out
* all relevant backends.
*/
static void
vcc_dir_dns_parse_list(struct vcc *tl, int *serial)
{
unsigned char a[4],mask;
int ret;
ERRCHK(tl);
SkipToken(tl, '{');
if (tl->t->tok != CSTR) {
vcc_dir_dns_parse_backend_options(tl);
ERRCHK(tl);
}
while (tl->t->tok == CSTR) {
mask = 32;
ret = sscanf(tl->t->dec, "%hhu.%hhu.%hhu.%hhu",
&a[0], &a[1], &a[2], &a[3]);
if (ret != 4) {
VSB_printf(tl->sb, "Incomplete IP supplied: ");
vcc_ErrToken(tl, tl->t);
VSB_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
ERRCHK(tl);
}
vcc_NextToken(tl);
if (tl->t->tok == '/') {
vcc_NextToken(tl);
mask = vcc_UintVal(tl);
ERRCHK(tl);
}
vcc_dir_dns_makebackend(tl,serial,a,mask);
SkipToken(tl,';');
}
ExpectErr(tl, '}');
}
void
vcc_ParseDnsDirector(struct vcc *tl)
{
struct token *t_field, *t_be, *t_suffix = NULL;
double ttl = 60.0;
int nelem = 0;
struct fld_spec *fs;
const char *first;
char *p;
dns_first = tl->t;
tl->fb = tl->fc;
fs = vcc_FldSpec(tl, "!backend", "?ttl", "?suffix","?list", NULL);
Fc(tl, 0, "\nstatic const struct vrt_dir_dns_entry "
"vddnse_%.*s[] = {\n", PF(tl->t_dir));
for (; tl->t->tok != '}'; ) { /* List of members */
if (tl->t->tok == '{') {
nelem++;
first = "";
t_be = tl->t;
vcc_ResetFldSpec(fs);
ExpectErr(tl, '{');
vcc_NextToken(tl);
Fc(tl, 0, "\t{");
while (tl->t->tok != '}') { /* Member fields */
vcc_IsField(tl, &t_field, fs);
ERRCHK(tl);
if (vcc_IdIs(t_field, "backend")) {
vcc_ParseBackendHost(tl, nelem, &p);
ERRCHK(tl);
AN(p);
Fc(tl, 0, "%s .host = VGC_backend_%s",
first, p);
} else {
ErrInternal(tl);
}
first = ", ";
}
vcc_FieldsOk(tl, fs);
if (tl->err) {
VSB_printf(tl->sb, "\nIn member host"
" specification starting at:\n");
vcc_ErrWhere(tl, t_be);
return;
}
Fc(tl, 0, " },\n");
} else {
vcc_IsField(tl, &t_field, fs);
ERRCHK(tl);
if (vcc_IdIs(t_field, "suffix")) {
ExpectErr(tl, CSTR);
t_suffix = tl->t;
vcc_NextToken(tl);
ExpectErr(tl, ';');
} else if (vcc_IdIs(t_field, "ttl")) {
vcc_Duration(tl, &ttl);
ExpectErr(tl, ';');
} else if (vcc_IdIs(t_field, "list")) {
vcc_dir_dns_parse_list(tl,&nelem);
}
}
vcc_NextToken(tl);
}
Fc(tl, 0, "};\n");
Fc(tl, 0, "\nstatic const struct vrt_dir_dns vgc_dir_priv_%.*s = {\n",
PF(tl->t_dir));
Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(tl->t_dir));
Fc(tl, 0, "\t.nmember = %d,\n", nelem);
Fc(tl, 0, "\t.members = vddnse_%.*s,\n", PF(tl->t_dir));
Fc(tl, 0, "\t.suffix = ");
if (t_suffix)
Fc(tl, 0, "%.*s", PF(t_suffix));
else
Fc(tl, 0, "\"\"");
Fc(tl, 0, ",\n");
Fc(tl, 0, "\t.ttl = %f", ttl);
Fc(tl, 0, ",\n");
Fc(tl, 0, "};\n");
}
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2010 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include "vcc_compile.h"
/*--------------------------------------------------------------------
* Parse directors
*/
void
vcc_ParseRandomDirector(struct vcc *tl)
{
struct token *t_field, *t_be, *t_x;
int nelem;
struct fld_spec *fs, *mfs;
unsigned u, retries;
const char *first;
char *p;
fs = vcc_FldSpec(tl, "?retries", NULL);
retries = 0;
while (tl->t->tok != '{') {
vcc_IsField(tl, &t_field, fs);
ERRCHK(tl);
if (vcc_IdIs(t_field, "retries")) {
ExpectErr(tl, CNUM);
retries = vcc_UintVal(tl);
ERRCHK(tl);
SkipToken(tl, ';');
} else {
ErrInternal(tl);
}
}
mfs = vcc_FldSpec(tl, "!backend", "!weight", NULL);
Fc(tl, 0,
"\nstatic const struct vrt_dir_random_entry vdre_%.*s[] = {\n",
PF(tl->t_dir));
for (nelem = 0; tl->t->tok != '}'; nelem++) { /* List of members */
first = "";
t_be = tl->t;
vcc_ResetFldSpec(mfs);
SkipToken(tl, '{');
Fc(tl, 0, "\t{");
while (tl->t->tok != '}') { /* Member fields */
vcc_IsField(tl, &t_field, mfs);
ERRCHK(tl);
if (vcc_IdIs(t_field, "backend")) {
vcc_ParseBackendHost(tl, nelem, &p);
ERRCHK(tl);
AN(p);
Fc(tl, 0, "%s .host = VGC_backend_%s",
first, p);
} else if (vcc_IdIs(t_field, "weight")) {
ExpectErr(tl, CNUM);
t_x = tl->t;
u = vcc_UintVal(tl);
ERRCHK(tl);
if (u == 0) {
VSB_printf(tl->sb,
"The .weight must be higher "
"than zero.\n");
vcc_ErrWhere(tl, t_x);
return;
}
Fc(tl, 0, "%s .weight = %u", first, u);
SkipToken(tl, ';');
} else {
ErrInternal(tl);
}
first = ", ";
}
vcc_FieldsOk(tl, mfs);
if (tl->err) {
VSB_printf(tl->sb,
"\nIn member host specification starting at:\n");
vcc_ErrWhere(tl, t_be);
return;
}
Fc(tl, 0, " },\n");
vcc_NextToken(tl);
}
Fc(tl, 0, "};\n");
Fc(tl, 0,
"\nstatic const struct vrt_dir_random vgc_dir_priv_%.*s = {\n",
PF(tl->t_dir));
Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(tl->t_dir));
Fc(tl, 0, "\t.retries = %u,\n", retries);
Fc(tl, 0, "\t.nmember = %d,\n", nelem);
Fc(tl, 0, "\t.members = vdre_%.*s,\n", PF(tl->t_dir));
Fc(tl, 0, "};\n");
}
......@@ -271,18 +271,6 @@ vcc__Expect(struct vcc *tl, unsigned tok, unsigned line)
vcc_ErrWhere(tl, tl->t);
}
/*--------------------------------------------------------------------
* Compare token to token
*/
int
vcc_Teq(const struct token *t1, const struct token *t2)
{
if (t1->e - t1->b != t2->e - t2->b)
return (0);
return (!memcmp(t1->b, t2->b, t1->e - t1->b));
}
/*--------------------------------------------------------------------
* Compare ID token to string, return true of match
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment