diff options
author | Sergey Poznyakoff <gray@nxc.no> | 2018-02-01 15:16:07 +0100 |
---|---|---|
committer | Sergey Poznyakoff <gray@nxc.no> | 2018-02-01 17:10:10 +0100 |
commit | 85f908426e444a596b979a9569bf83fadfa5ace1 (patch) | |
tree | 7513334ef43be06b45f39f13ef9b6a3bf37931d8 /src | |
parent | 94973146c58df26b20e17e2e5b1274216ca88969 (diff) | |
download | varnish-mib-85f908426e444a596b979a9569bf83fadfa5ace1.tar.gz varnish-mib-85f908426e444a596b979a9569bf83fadfa5ace1.tar.bz2 |
Rewrite for Varnish 5.x
* configure.ac (AC_CHECK_VSC_C_MAIN_MEMBERS): Remove.
* src/.gitignore: Update.
* src/Makefile.am (varnish_mib_la_SOURCES): Add new sources.
* src/backend.h: New file.
* src/belex.l: New file.
* src/statdict.c: New file.
* src/ban.c: Rewrite.
* src/betab.c: Rewrite.
* src/varnish_mib.mib2c: Rewrite.
* src/vcli.c: Rewrite.
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/backend.h | 16 | ||||
-rw-r--r-- | src/ban.c | 15 | ||||
-rw-r--r-- | src/belex.l | 178 | ||||
-rw-r--r-- | src/betab.c | 482 | ||||
-rw-r--r-- | src/statdict.c | 172 | ||||
-rw-r--r-- | src/varnish_mib.mib2c | 227 | ||||
-rw-r--r-- | src/vcli.c | 69 |
9 files changed, 828 insertions, 335 deletions
diff --git a/src/.gitignore b/src/.gitignore index 61958b7..f40282a 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,2 +1,3 @@ varnish_mib.c varnish_mib.h +belex.c diff --git a/src/Makefile.am b/src/Makefile.am index 48c786b..3a7f12a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,15 +17,18 @@ dlmoddir=@DLMODDIR@ dlmod_LTLIBRARIES = varnish-mib.la varnish_mib_la_SOURCES = \ auth.c\ ban.c\ + backend.h\ + belex.l\ betab.c\ sha256.c\ sha256.h\ + statdict.c\ varnish_mib.c\ varnish_mib.h\ vcli.c BUILT_SOURCES = \ varnish_mib.c\ diff --git a/src/backend.h b/src/backend.h new file mode 100644 index 0000000..0c15e60 --- /dev/null +++ b/src/backend.h @@ -0,0 +1,16 @@ +typedef struct be_string { + char const *start; + size_t len; +} be_string_t; + +typedef void (*regfun_t)(be_string_t *, be_string_t *, be_string_t *, void *); + +void read_defs(const char *str, size_t len, regfun_t regfun, void *d); +void varnish_backend_table_timeout_parser(const char *token, char *line); +struct VSC_point; +void backend_register(char const *name, size_t len, char const *param, + const struct VSC_point *vpt); +void backend_clear(void); +int backend_collect_addr(void); +void backend_parser(const char *str, size_t len, regfun_t regfun, void *d); + @@ -35,13 +35,13 @@ send_ban_cmd(vcli_conn_t *conn, const char *expr) return 0; } int varnish_ban(netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests, - struct VSM_data *vd) + struct vsm *vsm) { int rc; struct vcli_conn conn; size_t len = requests->requestvb->val_len; char *expr = malloc(len + 1); @@ -49,13 +49,13 @@ varnish_ban(netsnmp_agent_request_info *reqinfo, snmp_log(LOG_ERR, "out of memory\n"); return SNMP_ERR_GENERR; } memcpy(expr, requests->requestvb->val.string, len); expr[len] = 0; DEBUGMSGTL(("varnish_ban", "setting ban %s\n", expr)); - rc = vcli_connect(vd, &conn); + rc = vcli_connect(vsm, &conn); if (rc == SNMP_ERR_NOERROR) { rc = send_ban_cmd(&conn, expr); vcli_disconnect(&conn); } free(expr); return rc ? SNMP_ERR_GENERR : SNMP_ERR_NOERROR; @@ -148,19 +148,20 @@ banTable_load(netsnmp_cache *cache, void *vmagic) { netsnmp_tdata *table = (netsnmp_tdata *) vmagic; long idx = 0; int rc; struct vcli_conn conn; char *p; - struct VSM_data *vd; + + struct vsm *vsm = varnish_get_vsm_data(); + if (!vsm) + return SNMP_ERR_GENERR; DEBUGMSGTL(("varnish_ban", "reloading ban table\n")); - vd = varnish_get_vsm_data(); - if (!vd) - return SNMP_ERR_NOSUCHNAME; - rc = vcli_connect(vd, &conn); + rc = vcli_connect(vsm, &conn); + if (rc != SNMP_ERR_NOERROR) return rc; if (vcli_asprintf(&conn, "ban.list\n") || vcli_write(&conn)) return SNMP_ERR_GENERR; diff --git a/src/belex.l b/src/belex.l new file mode 100644 index 0000000..f35f114 --- /dev/null +++ b/src/belex.l @@ -0,0 +1,178 @@ +%option nounput +%option noinput + +%{ +#include "backend.h" + +enum { + T_BOGUS = 256, + T_IDENT, + T_NUMBER, + T_STRING, + T_BACKEND, + T_HOST, + T_PORT +}; + +static char const *input_string; +static size_t input_len; +static size_t input_pos; +static char const *string_start; +static size_t current_pos; + +#define YY_INPUT(buf,result,max_size) \ + do { \ + size_t n = input_len - input_pos; \ + if (n > max_size) \ + n = max_size; \ + memcpy(buf, input_string, n); \ + input_pos += n; \ + result = n; \ + } while (0) + +#define YY_USER_ACTION \ + current_pos += yyleng; + +#define YY_DECL static int yylex(void) +static int yywrap(void); + +static char const * +input_point(void) +{ + return input_string + current_pos; +} + +#define YYSTYPE be_string_t +#define YYSTYPE_INITIALIZER { NULL, 0 } + +static YYSTYPE yylval; +%} + +%x COMMENT STR + +%% + /* C-style comments */ +"/*" BEGIN(COMMENT); +<COMMENT>[^*]* /* eat anything that's not a '*' */ +<COMMENT>"*"+[^*/]* /* eat up '*'s not followed by '/'s */ +<COMMENT>"*"+"/" BEGIN(INITIAL); + + /* Single-line comments */ +"//".*\n ; +#.*\n ; + + /* Multi-line strings */ +"{\"" { BEGIN(STR); string_start = input_point(); } +<STR>[^\"]* /* eat anything that's not a '"' */ +<STR>"\""+[^"}]* /* eat up '"'s not followed by '}'s */ +<STR>"\""+"}" { BEGIN(INITIAL); + yylval.start = string_start; + yylval.len = input_point() - yylval.start - 2; + return T_STRING; + } + /* Single-line strings */ +\".*\" { + yylval.start = input_point() - yyleng + 1; + yylval.len = yyleng - 2; + return T_STRING; + } +backend return T_BACKEND; +".host" return T_HOST; +".port" return T_PORT; +"{"|"}"|"="|";" return yytext[0]; + +[a-zA-Z_][a-zA-Z0-9_.]+ { + yylval.start = input_point() - yyleng; + yylval.len = yyleng; + return T_IDENT; + } +[0-9]+ return T_NUMBER; + +[ \t\n]+ ; + +. return T_BOGUS; + +%% +int +yywrap(void) +{ + return 1; +} + +static void +set_input(char const *str, size_t len) +{ + input_string = str; + input_len = len; + input_pos = 0; + current_pos = 0; +} + +static int brace_nesting; + +static void +read_backend(regfun_t regfun, void *d) +{ + int c; + int in_statement; + YYSTYPE label, host = YYSTYPE_INITIALIZER, port = YYSTYPE_INITIALIZER; + + if ((c = yylex()) != T_IDENT && c != T_STRING) + return; + label = yylval; + if ((c = yylex()) != '{') + return; + brace_nesting++; + in_statement = 0; + while (brace_nesting == 1) { + c = yylex(); + if (c == 0) + break; + else if (c == '{') + brace_nesting++; + else if (c == '}') + brace_nesting++; + else if (in_statement) { + if (c == ';') + in_statement = 0; + } else { + in_statement = 1; + if (c == T_HOST || c == T_PORT) { + YYSTYPE *stk = c == T_HOST ? &host : &port; + in_statement = 1; + if ((c = yylex()) == '=') { + c = yylex(); + if (c == T_STRING || c == T_IDENT) { + *stk = yylval; + } + } + } + } + } + regfun(&label, &host, &port, d); +} + +void +backend_parser(const char *str, size_t len, regfun_t regfun, void *d) +{ + int c; + + set_input(str, len); + + brace_nesting = 0; + while ((c = yylex())) { + if (brace_nesting == 0) { + if (c == T_BACKEND) { + read_backend(regfun, d); + } + } + switch (c) { + case '{': + brace_nesting++; + break; + case '}': + brace_nesting--; + break; + } + } +} diff --git a/src/betab.c b/src/betab.c index 49e02f7..388083b 100644 --- a/src/betab.c +++ b/src/betab.c @@ -1,8 +1,8 @@ /* This file is part of varnish-mib - Copyright (C) 2014 Sergey Poznyakoff + Copyright (C) 2014, 2018 Sergey Poznyakoff Varnish-mib is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. @@ -12,79 +12,45 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with varnish-mib. If not, see <http://www.gnu.org/licenses/>. */ #include "varnish_mib.h" +#include <varnish/vas.h> +#include <varnish/vqueue.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> #include <arpa/inet.h> +#include <netinet/in.h> -unsigned backendTable_timeout = 5; - -void -varnish_backend_table_timeout_parser(const char *token, char *line) -{ - varnish_mib_timeout_parser(token, line, &backendTable_timeout); -} - -/* - * create a new row in the table - */ -static struct backendTable_entry * -create_entry(netsnmp_tdata *table_data, long idx, - struct backendTable_entry *ent) -{ - struct backendTable_entry *entry; - netsnmp_tdata_row *row; - - entry = SNMP_MALLOC_TYPEDEF(struct backendTable_entry); - if (!entry) - return NULL; - - row = netsnmp_tdata_create_row(); - if (!row) { - SNMP_FREE(entry); - return NULL; - } - row->data = entry; - *entry = *ent; - - entry->vbeIndex = idx; - netsnmp_tdata_row_add_index(row, ASN_INTEGER, - &entry->vbeIndex, - sizeof(entry->vbeIndex)); - if (table_data) - netsnmp_tdata_add_row(table_data, row); - return entry; -} +#include "backend.h" -#define VSC_POINT_TYPE(p) ((p)->section->fantom->type) -#define VSC_POINT_IDENT(p) ((p)->section->fantom->ident) -#define VSC_POINT_NAME(p) ((p)->desc->name) -#if VARNISHAPI_MINOR == 0 -# define VSC_POINT_FMT(p) ((p)->desc->fmt) -#elif VARNISHAPI_MINOR == 1 -# define VSC_POINT_FMT(p) ((p)->desc->ctype) -#else -# error "unsupported Varnish API minor number" -#endif - -struct betab_priv { - int err; - long idx; - struct backendTable_entry ent; - netsnmp_tdata *table; +enum { + vbeHappyProbes, + vbeRequestHeaderBytes, + vbeRequestBodyBytes, + vbeResponseHeaderBytes, + vbeResponseBodyBytes, + vbePipeHeaderBytes, + vbePipeIn, + vbePipeOut, + VBE_MAX }; struct betab_trans { - const char *name; + char const *param; size_t off; }; -static struct betab_trans betab_trans[] = { +/* { "vcls", offsetof(struct backendTable_entry, vbeVcls) }, +*/ + +static struct betab_trans betab_trans[] = { { "happy", offsetof(struct backendTable_entry, vbeHappyProbes) }, { "bereq_hdrbytes", offsetof(struct backendTable_entry, vbeRequestHeaderBytes) }, { "bereq_bodybytes", offsetof(struct backendTable_entry, vbeRequestBodyBytes) }, @@ -98,171 +64,311 @@ static struct betab_trans betab_trans[] = { offsetof(struct backendTable_entry, vbePipeIn) }, { "pipe_out", offsetof(struct backendTable_entry, vbePipeOut) }, { NULL } }; -static int -identcmp(struct betab_priv *bp, const char *ident) -{ - size_t len; - size_t i; +struct backend_dfn { + long vbeIndex; + + char *vbeIdent; + size_t vbeIdent_len; + + char vbeIPv4[4]; + size_t vbeIPv4_len; + char vbeIPv6[16]; + size_t vbeIPv6_len; + u_long vbePort; - if (bp->idx == -1) - return 1; - for (i = 0; i < bp->ent.vbeIdent_len; i++, ident++) - if (bp->ent.vbeIdent[i] != *ident) - return 1; - if (*ident == '(' || *ident == 0) - return 0; - return 1; -} + const struct VSC_point *vpt[VBE_MAX]; + + VTAILQ_ENTRY(backend_dfn) list; +}; -static void -uint32_to_bytes (unsigned char *bytes, uint32_t u) -{ - int i; +static VTAILQ_HEAD(, backend_dfn) backends + = VTAILQ_HEAD_INITIALIZER(backends); - for (i = 0; i < 4; i++) - { - bytes[i] = u & 0xff; - u >>= 8; - } +void +backend_clear(void) +{ + while (!VTAILQ_EMPTY(&backends)) { + struct backend_dfn *dfn = VTAILQ_FIRST(&backends); + VTAILQ_REMOVE(&backends, dfn, list); + free(dfn->vbeIdent); + SNMP_FREE(dfn); + } } -static void -scanbuf(const char *s, struct backendTable_entry *ent) +void +backend_register(char const *name, size_t len, char const *param, + const struct VSC_point *vpt) { - char ipv4buf[16]; - char ipv6buf[81]; - unsigned long port; - char *p; - union { - struct in_addr in; - struct in6_addr in6; - } v; + int i = 0; + struct backend_dfn *dfn; - if (*s != '(') - return; - ++s; - p = ipv4buf; - while (p < ipv4buf + sizeof(ipv4buf) && *s && *s != ',') - *p++ = *s++; - if (*s != ',') - return; - *p = 0; + VTAILQ_FOREACH(dfn, &backends, list) { + if (dfn->vbeIdent_len == len + && memcmp(dfn->vbeIdent, name, len) == 0) + break; + i++; + } + if (!dfn) { + dfn = SNMP_MALLOC_TYPEDEF(struct backend_dfn); + AN(dfn); + dfn->vbeIndex = i; + dfn->vbeIdent = malloc(len + 1); + AN(dfn->vbeIdent); + memcpy(dfn->vbeIdent, name, len); + dfn->vbeIdent[len] = 0; + dfn->vbeIdent_len = len; + VTAILQ_INSERT_TAIL(&backends, dfn, list); + } + for (i = 0; betab_trans[i].param; i++) { + if (strcmp(betab_trans[i].param, param) == 0) { + dfn->vpt[i] = vpt; + break; + } + } +} - ++s; - p = ipv6buf; - while (p < ipv6buf + sizeof(ipv6buf) && *s && *s != ',') - *p++ = *s++; - *p = 0; +int +backendTable_load(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table_data = (netsnmp_tdata *) vmagic; + struct backend_dfn *dfn; - ++s; - port = strtoul(s, &p, 10); - if (*p != ')' || port > USHRT_MAX) - return; + DEBUGMSGTL(("varnish_backend", "loading backend table\n")); + + VTAILQ_FOREACH(dfn, &backends, list) { + int i; + + netsnmp_tdata_row *row; + struct backendTable_entry *ent; + + ent = SNMP_MALLOC_TYPEDEF(struct backendTable_entry); + ent->vbeIndex = dfn->vbeIndex; + ent->vbeIdent = malloc(dfn->vbeIdent_len); + memcpy(ent->vbeIdent, dfn->vbeIdent, dfn->vbeIdent_len); + ent->vbeIdent_len = dfn->vbeIdent_len; + memset(ent->vbeIPv4, 0, sizeof ent->vbeIPv4); + + memcpy(ent->vbeIPv4, dfn->vbeIPv4, dfn->vbeIPv4_len); + ent->vbeIPv4_len = dfn->vbeIPv4_len; + memcpy(ent->vbeIPv6, dfn->vbeIPv6, ent->vbeIPv6_len); + ent->vbeIPv6_len = dfn->vbeIPv6_len; + ent->vbePort = dfn->vbePort; + + for (i = 0; i < VBE_MAX; i++) { + U64 *u = (U64*)((char*)ent + betab_trans[i].off); + uint64_t n = *(const volatile uint64_t*)dfn->vpt[i]->ptr; + u->high = n >> 32; + u->low = n & 0xffffffff; + } + + row = netsnmp_tdata_create_row(); + if (!row) + break; + row->data = ent; + netsnmp_tdata_row_add_index(row, ASN_INTEGER, + &ent->vbeIndex, + sizeof(ent->vbeIndex)); + netsnmp_tdata_add_row(table_data, row); + } + return 0; +} + +void +backendTable_free(netsnmp_cache *cache, void *vmagic) +{ + netsnmp_tdata *table = (netsnmp_tdata *) vmagic; + netsnmp_tdata_row *row; - if (ipv4buf[0] && inet_pton(AF_INET, ipv4buf, &v)) { - ent->vbeIPv4_len = 4; - uint32_to_bytes(ent->vbeIPv4, v.in.s_addr); + DEBUGMSGTL(("varnish_backend", "freeing backend table\n")); + while ((row = netsnmp_tdata_row_first(table))) { + struct backendTable_entry *entry = row->data; + free(entry->vbeIdent); + SNMP_FREE(entry); + netsnmp_tdata_remove_and_delete_row(table, row); } +} + +unsigned backendTable_timeout = 5; + +void +varnish_backend_table_timeout_parser(const char *token, char *line) +{ + varnish_mib_timeout_parser(token, line, &backendTable_timeout); +} + +static struct backend_dfn * +dfn_lookup(be_string_t *vclname, be_string_t *label) +{ + size_t namelen; + struct backend_dfn *dfn; - if (ipv6buf[0] && inet_pton(AF_INET6, ipv6buf, &v)) { - ent->vbeIPv6_len = 16; - memcpy(ent->vbeIPv6, &v, ent->vbeIPv6_len); + namelen = vclname->len + label->len + 1; + VTAILQ_FOREACH(dfn, &backends, list) { + if (dfn->vbeIdent_len == namelen + && memcmp(dfn->vbeIdent, vclname->start, vclname->len) == 0 + && dfn->vbeIdent[vclname->len] == '.' + && memcmp(dfn->vbeIdent + vclname->len + 1, + label->start, label->len) == 0) + return dfn; } - ent->vbePort = port; -} - -/* Process a single statistics point. See comment below. */ -static int -create_entry_cb(void *priv, const struct VSC_point *const pt) + return NULL; +} + +static u_long +to_port(be_string_t *str) { - struct betab_priv *bp = priv; - struct betab_trans *tp; - - if (bp->err || !pt || strcmp(VSC_POINT_TYPE(pt), "VBE") || - strcmp(VSC_POINT_FMT(pt), "uint64_t")) - return 0; - if (identcmp(bp, VSC_POINT_IDENT(pt))) { - const char *full_id; - - if (bp->idx != -1 - && !create_entry(bp->table, bp->idx, &bp->ent)) { - snmp_log(LOG_ERR, "out of memory\n"); - bp->err = SNMP_ERR_GENERR; + int i; + u_long v = 0; + for (i = 0; i < str->len; i++) { + static char dig[] = "0123456789"; + char *p = strchr(dig, str->start[i]); + if (!p) return 0; - } + v = v * 10 + p - dig; + } + return v; +} - memset(&bp->ent, 0, sizeof(bp->ent)); - bp->ent.vbeIndex = ++bp->idx; +static void +uint32_to_bytes (unsigned char *bytes, uint32_t u) +{ + int i; - full_id = VSC_POINT_IDENT(pt); - bp->ent.vbeIdent_len = strcspn(full_id, "("); - bp->ent.vbeIdent = malloc(bp->ent.vbeIdent_len); - if (!bp->ent.vbeIdent) { - snmp_log(LOG_ERR, "out of memory\n"); - bp->err = SNMP_ERR_GENERR; - return 0; - } - memcpy(bp->ent.vbeIdent, full_id, bp->ent.vbeIdent_len); - full_id += bp->ent.vbeIdent_len; - scanbuf(full_id, &bp->ent); + for (i = 0; i < 4; i++) { + bytes[i] = u & 0xff; + u >>= 8; } +} - for (tp = betab_trans; tp->name; tp++) { - if (strcmp(VSC_POINT_NAME(pt), tp->name) == 0) { - U64 *u = (U64*)((char*)&bp->ent + tp->off); - uint64_t n = *(const volatile uint64_t*)pt->ptr; - u->high = n >> 32; - u->low = n & 0xffffffff; - break; +static void +update_dfn(be_string_t *label, be_string_t *host, be_string_t *port, + void *data) +{ + struct backend_dfn *dfn; + + dfn = dfn_lookup(data, label); + if (!dfn) + return; + dfn->vbePort = to_port(port); + if (host->len) { + struct addrinfo *res, *ap; + int rc; + char *node; + + node = malloc(host->len+1); + AN(node); + memcpy(node, host->start, host->len); + node[host->len] = 0; + rc = getaddrinfo(node, NULL, NULL, &res); + free(node); + if (rc) + return; + for (ap = res; ap; ap = ap->ai_next) { + if (ap->ai_addr->sa_family == AF_INET) { + if (dfn->vbeIPv4_len == 0) { + struct sockaddr_in *s = + (struct sockaddr_in *)ap->ai_addr; + uint32_to_bytes((unsigned char*)dfn->vbeIPv4, + s->sin_addr.s_addr); + dfn->vbeIPv4_len = 4; + } + } else if (ap->ai_addr->sa_family == AF_INET6) { + if (dfn->vbeIPv4_len == 0) { + struct sockaddr_in6 *s = + (struct sockaddr_in6 *)ap->ai_addr; + dfn->vbeIPv6_len = 16; + memcpy(dfn->vbeIPv6, &s->sin6_addr, + dfn->vbeIPv6_len); + } + } } + freeaddrinfo(res); } - return 0; } -/* Varnish API does not provide access to struct VSC_C_vbe, so the only - way to backend statistics is to iterate over all statistics data, selecting - the entries marked as VBE. That's what this function does. - */ -int -backendTable_load(netsnmp_cache *cache, void *vmagic) +static int +backend_parse(struct vcli_conn *conn, be_string_t *name) { - struct betab_priv bp; - struct VSM_data *vd = varnish_get_vsm_data(); + if (vcli_asprintf(conn, "vcl.show %*.*s\n", + (int)name->len, (int)name->len, + name->start) + || vcli_write(conn)) + return SNMP_ERR_GENERR; - if (!vd) - return SNMP_ERR_NOSUCHNAME; + if (vcli_read_response(conn)) + return SNMP_ERR_GENERR; - bp.idx = -1; - bp.err = 0; - bp.table = (netsnmp_tdata *) vmagic; - memset(&bp.ent, 0, sizeof(bp.ent)); - - DEBUGMSGTL(("varnish_ban", "loading backend table\n")); - VSC_Iter(vd, NULL, create_entry_cb, &bp); - /* FIXME: perhaps handle bp.err separately */ - if (bp.idx != -1) { - DEBUGMSGTL(("varnish_ban", "loaded %lu backend entries\n", - bp.idx + 1)); - if (!create_entry(bp.table, bp.idx, &bp.ent)) - snmp_log(LOG_ERR, "out of memory\n"); + if (conn->resp != CLIS_OK) { + snmp_log(LOG_ERR, "vcl.show command rejected: %u %s\n", + conn->resp, conn->base); + return SNMP_ERR_GENERR; } + backend_parser(conn->base, conn->bufsize, update_dfn, name); return 0; } + +struct backend_name { + be_string_t str; + VTAILQ_ENTRY(backend_name) list; +}; +VTAILQ_HEAD(backend_name_list, backend_name); -void -backendTable_free(netsnmp_cache *cache, void *vmagic) +static void +backend_name_add(struct backend_name_list *namelist, + char const *str, size_t len) { - netsnmp_tdata *table = (netsnmp_tdata *) vmagic; - netsnmp_tdata_row *row; + struct backend_name *name; + VTAILQ_FOREACH(name, namelist, list) { + if (name->str.len == len + && memcmp(name->str.start, str, len) == 0) + return; + } + name = SNMP_MALLOC_TYPEDEF(struct backend_name); + AN(name); + name->str.start = str; + name->str.len = len; + VTAILQ_INSERT_TAIL(namelist, name, list); +} - DEBUGMSGTL(("varnish_ban", "freeing backend table\n")); - while ((row = netsnmp_tdata_row_first(table))) { - struct backendTable_entry *entry = row->data; - free(entry->vbeIdent); - SNMP_FREE(entry); - netsnmp_tdata_remove_and_delete_row(table, row); +int +backend_collect_addr(void) +{ + struct backend_dfn *dfn; + struct vcli_conn conn; + struct vsm *vsm; + int rc; + struct backend_name_list namelist + = VTAILQ_HEAD_INITIALIZER(namelist); + + vsm = varnish_get_vsm_data(); + if (!vsm) + return SNMP_ERR_GENERR; + DEBUGMSGTL(("varnish_backend", "getting backend info\n")); + + rc = vcli_connect(vsm, &conn); + if (rc != SNMP_ERR_NOERROR) + return rc; + + /* Select backend names */ + VTAILQ_FOREACH(dfn, &backends, list) { + int i; + for (i = 0; i < dfn->vbeIdent_len; i++) + if (dfn->vbeIdent[i] == '.') + break; + if (i == dfn->vbeIdent_len) + continue; + backend_name_add(&namelist, dfn->vbeIdent, i); } + + while (!VTAILQ_EMPTY(&namelist)) { + struct backend_name *name = VTAILQ_FIRST(&namelist); + VTAILQ_REMOVE(&namelist, name, list); + backend_parse(&conn, &name->str); + } + + vcli_disconnect(&conn); + return 0; } diff --git a/src/statdict.c b/src/statdict.c new file mode 100644 index 0000000..e87123b --- /dev/null +++ b/src/statdict.c @@ -0,0 +1,172 @@ +/* This file is part of varnish-mib + Copyright (C) 2018 Sergey Poznyakoff + + Varnish-mib is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Varnish-mib is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with varnish-mib. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <inttypes.h> +#include <string.h> +#include <vapi/vsc.h> +#include <vapi/vsm.h> +#include <vas.h> +#include <vcli.h> +#include "backend.h" + +static size_t hash_size[] = { + 7, 17, 37, 101, 229, 487, 1009, 2039, 4091, 8191, 16411 +}; +static size_t hash_num = 0; +static struct VSC_point **dict; + +#define SIZE_T_MAX ((size_t)-1) + +static inline size_t +rotl(size_t x, int n) +{ + return ((x << n) | (x >> ((CHAR_BIT * sizeof x) - n))) & SIZE_T_MAX; +} + +static size_t +hash(const char *str) +{ + size_t value = 0; + unsigned char ch; + + while ((ch = *str++)) + value = ch + rotl(value, 7); + return value % hash_size[hash_num]; +} + +struct VSC_point **lookup(const char *name, int install); + +static void +rehash(void) +{ + struct VSC_point **old_dict = dict; + size_t old_size = hash_size[hash_num]; + size_t i; + + ++hash_num; + AN(hash_num < sizeof(hash_size) / sizeof(hash_size[0])); + dict = calloc(hash_size[hash_num], sizeof(dict[0])); + AN(dict); + + for (i = 0; i < old_size; i++) { + if (old_dict[i]) + *lookup(old_dict[i]->name, 1) = old_dict[i]; + } + free(old_dict); +} + +struct VSC_point ** +lookup(const char *name, int install) +{ + size_t i, pos; + + if (!dict) { + if (install) { + dict = calloc(hash_size[hash_num], sizeof(dict[0])); + AN(dict); + } else + return NULL; + } + + pos = hash(name); + + for (i = pos; dict[i];) { + if (strcmp(dict[i]->name, name) == 0) { + return &dict[i]; + } + + if (++i >= hash_size[hash_num]) + i = 0; + if (i == pos) + break; + } + + if (!install) + return NULL; + + if (!dict[i]) + return &dict[i]; + + rehash(); + return lookup(name, install); +} + +int +dict_lookup(char const *key, uint64_t *val) +{ + struct VSC_point **ent; + + ent = lookup(key, 0); + if (!ent) { + fprintf(stderr, "%s NOT FOUND\n", key); + } + if (!ent) + return -1; + *val = *(uint64_t*)(*ent)->ptr; + return 0; +} + +void +dict_clear(void) +{ + if (dict) { + size_t i; + for (i = 0; i < hash_size[hash_num]; i++) + if (dict[i]) + VSC_Destroy_Point(&dict[i]); + } +} + +struct VSC_point const * +dict_install(struct VSC_point const *pt) +{ + struct VSC_point **ent; + + ent = lookup(pt->name, 1); + AN(ent); + if (*ent) + VSC_Destroy_Point(ent); + *ent = VSC_Clone_Point(pt); + return *ent; +} + +static int +load_cb(void *priv, const struct VSC_point *vpt) +{ + AZ(strcmp(vpt->ctype, "uint64_t")); + vpt = dict_install(vpt); + if (strncmp(vpt->name, "VBE.", 4) == 0) { + char const *name = vpt->name + 4; + char *p = strrchr(name, '.'); + if (p) { + backend_register(name, p - name, p + 1, vpt); + } + } + return 0; +} + +void +dict_load(struct vsc *vsc) +{ + struct vsm_fantom fantom = VSM_FANTOM_NULL; + dict_clear(); + backend_clear(); + VSC_Iter(vsc, &fantom, load_cb, NULL); + backend_collect_addr(); +} diff --git a/src/varnish_mib.mib2c b/src/varnish_mib.mib2c index 9d49ce9..a458723 100644 --- a/src/varnish_mib.mib2c +++ b/src/varnish_mib.mib2c @@ -1,8 +1,8 @@ # This file is part of varnish-mib -*- c -*- -# Copyright (C) 2014-2016 Sergey Poznyakoff +# Copyright (C) 2014-2018 Sergey Poznyakoff # # Varnish-mib is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # @@ -22,86 +22,85 @@ End: vi: set ro: */ @enddefine@ @startperl@ $vars{'varnish_translate_table'} = { - uptime => [ 'MAIN', 'uptime' ], - clientAcceptedConnections => [ 'MAIN', 'sess_conn' ], - clientRequestsReceived => [ 'MAIN', 'client_req' ], - clientCacheHits => [ 'MAIN', 'cache_hit' ], - clientCacheHitsPass => [ 'MAIN', 'cache_hitpass' ], - clientCacheMisses => [ 'MAIN', 'cache_miss' ], + uptime => [ 'DICT', 'MAIN.uptime' ], + clientAcceptedConnections => [ 'DICT', 'MAIN.sess_conn' ], + clientCacheHits => [ 'DICT', 'MAIN.cache_hit' ], + clientCacheHitsPass => [ 'DICT', 'MAIN.cache_hitpass' ], + clientCacheMisses => [ 'DICT', 'MAIN.cache_miss' ], clientBan => [ 'STRING', '', { varnish_set_action => 'varnish_ban' } ], - clientRequestsReceived => [ 'MAIN', 'client_req' ], - clientRequests400 => [ 'MAIN', 'client_req_400' ], - clientRequests411 => [ 'MAIN', 'client_req_411' ], - clientRequests413 => [ 'MAIN', 'client_req_413' ], - clientRequests417 => [ 'MAIN', 'client_req_417' ], + clientRequestsReceived => [ 'DICT', 'MAIN.client_req' ], + clientRequests400 => [ 'DICT', 'MAIN.client_req_400' ], + clientRequests411 => [ 'NULL', 'MAIN.client_req_411' ], + clientRequests413 => [ 'NULL', 'MAIN.client_req_413' ], + clientRequests417 => [ 'DICT', 'MAIN.client_req_417' ], - backendConnSuccess => [ 'MAIN', 'backend_conn' ], - backendConnNotAttempted => [ 'MAIN', 'backend_unhealthy' ], - backendConnToMany => [ 'MAIN', 'backend_busy' ], - backendConnFailures => [ 'MAIN', 'backend_fail' ], - backendConnReuses => [ 'MAIN', 'backend_reuse' ], - backendConnRecycled => [ 'MAIN', 'backend_recycle' ], - backendConnUnused => [ 'MAIN', 'backend_toolate' ], - backendConnRetry => [ 'MAIN', 'backend_retry' ], - backendRequests => [ 'MAIN', 'backend_req' ], + backendConnSuccess => [ 'DICT', 'MAIN.backend_conn' ], + backendConnNotAttempted => [ 'DICT', 'MAIN.backend_unhealthy' ], + backendConnToMany => [ 'DICT', 'MAIN.backend_busy' ], + backendConnFailures => [ 'DICT', 'MAIN.backend_fail' ], + backendConnReuses => [ 'DICT', 'MAIN.backend_reuse' ], + backendConnRecycled => [ 'DICT', 'MAIN.backend_recycle' ], + backendConnUnused => [ 'NULL', 'MAIN.backend_toolate' ], + backendConnRetry => [ 'DICT', 'MAIN.backend_retry' ], + backendRequests => [ 'DICT', 'MAIN.backend_req' ], - totalSessions => [ 'MAIN', 's_sess' ], - totalRequests => [ 'MAIN', 's_req' ], - totalPipe => [ 'MAIN', 's_pipe' ], - totalPass => [ 'MAIN', 's_pass' ], - totalFetch => [ 'MAIN', 's_fetch' ], - totalRequestHeaderBytes => [ |