# This file is part of varnish-mib -*- c -*-
# 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.
#
# 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 .
# This macro inserts a comment instructing Emacs and vi that
# this file is read-only. It must be called at the end of the file.
@define ROCOM@
/* Local variables:
buffer-read-only: t
End:
vi: set ro:
*/
@enddefine@
@startperl@
use VarnishMib::MIBTable;
$vars{'varnish_translate_table'} = {
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 => '',
set_action => 'varnish_ban' ],
clientRequestsReceived => [ DICT => 'MAIN.client_req' ],
clientRequests400 => [ DICT => 'MAIN.client_req_400' ],
clientRequests411 => [ NULL ],
clientRequests413 => [ NULL ],
clientRequests417 => [ DICT => 'MAIN.client_req_417' ],
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 ],
backendConnRetry => [ DICT => 'MAIN.backend_retry' ],
backendRequests => [ DICT => 'MAIN.backend_req' ],
totalSessions => [ DICT => 'MAIN.s_sess' ],
totalRequests => [ DICT => 'MAIN.client_req' ],
totalPipe => [ DICT => 'MAIN.s_pipe' ],
totalPass => [ DICT => 'MAIN.s_pass' ],
totalFetch => [ DICT => 'MAIN.s_fetch' ],
totalRequestHeaderBytes => [ DICT => 'MAIN.s_req_hdrbytes' ],
totalRequestBodyBytes => [ DICT => 'MAIN.s_req_bodybytes' ],
totalResponseHeaderBytes => [ DICT => 'MAIN.s_resp_hdrbytes' ],
totalResponseBodyBytes => [ DICT => 'MAIN.s_resp_bodybytes' ],
totalPipeHeaderBytes => [ DICT => 'MAIN.s_pipe_hdrbytes' ],
totalPipeIn => [ DICT => 'MAIN.s_pipe_in' ],
totalPipeOut => [ DICT => 'MAIN.s_pipe_out' ],
sessAccepted => [ DICT => 'MAIN.sess_conn'],
sessQueued => [ DICT => 'MAIN.sess_queued'],
sessDropped => [ DICT => 'MAIN.sess_dropped'],
sessClosed => [ DICT => 'MAIN.sess_closed'],
sessPipeline => [ DICT => 'MAIN.s_pipe'],
sessReadAhead => [ DICT => 'MAIN.sess_readahead'],
sessHerd => [ DICT => 'MAIN.sess_herd'],
sessDrop => [ DICT => 'MAIN.sess_drop'],
sessFail => [ DICT => 'MAIN.sess_fail'],
sessPipeOverflow => [ DICT => 'MAIN.sc_pipe_overflow'],
threadsPools => [ DICT => 'MAIN.pools'],
threadsTotal => [ DICT => 'MAIN.threads'],
threadsLimitHits => [ DICT => 'MAIN.threads_limited'],
threadsCreated => [ DICT => 'MAIN.threads_created'],
threadsDestroyed => [ DICT => 'MAIN.threads_destroyed'],
threadsFailed => [ DICT => 'MAIN.threads_failed'],
threadsQueueLength => [ DICT => 'MAIN.thread_queue_len'],
bansTotal => [ DICT => 'MAIN.bans' ],
bansCompleted => [ DICT => 'MAIN.bans_completed' ],
bansObj => [ DICT => 'MAIN.bans_obj' ],
bansReq => [ DICT => 'MAIN.bans_req' ],
bansAdded => [ DICT => 'MAIN.bans_added' ],
bansDeleted => [ DICT => 'MAIN.bans_deleted' ],
bansTested => [ DICT => 'MAIN.bans_tested' ],
bansObjectsKilled => [ DICT => 'MAIN.bans_obj_killed' ],
bansLurkerTested => [ DICT => 'MAIN.bans_lurker_tested' ],
bansTestTested => [ DICT => 'MAIN.bans_tests_tested' ],
bansLurkerTestTested => [ DICT => 'MAIN.bans_lurker_tests_tested' ],
bansLurkerObjKilled => [ DICT => 'MAIN.bans_lurker_obj_killed' ],
bansDups => [ DICT => 'MAIN.bans_dups' ],
bansLurkerContention => [ DICT => 'MAIN.bans_lurker_contention' ],
bansPersistedBytes => [ DICT => 'MAIN.bans_persisted_bytes' ],
bansPersistedFragmentation => [ DICT => 'MAIN.bans_persisted_fragmentation' ],
objectsCount => [ DICT => 'MAIN.n_object' ],
objectsVampire => [ DICT => 'MAIN.n_vampireobject' ],
objectsCore => [ DICT => 'MAIN.n_objectcore' ],
objectsHead => [ DICT => 'MAIN.n_objecthead' ],
objectsExpired => [ DICT => 'MAIN.n_expired' ],
objectsLRUNuked => [ DICT => 'MAIN.n_lru_nuked' ],
objectsLRUMoved => [ DICT => 'MAIN.n_lru_moved' ],
objectsPurges => [ DICT => 'MAIN.n_purges' ],
objectsObjPurged => [ DICT => 'MAIN.n_obj_purged' ],
objectsGzip => [ DICT => 'MAIN.n_gzip' ],
objectsGunzip => [ DICT => 'MAIN.n_gunzip' ],
vclTotal => [ DICT => 'MAIN.n_vcl' ],
vclAvail => [ DICT => 'MAIN.n_vcl_avail' ],
vclDiscard => [ DICT => 'MAIN.n_vcl_discard' ],
vclFail => [ DICT => 'MAIN.vcl_fail' ],
};
$vars{'varnish_hash_table'} = new VarnishMib::MIBTable(
'struct VSC_point *',
$vars{'varnish_translate_table'},
prefix => 'vmib_', max_collisions => 0
);
my @reset_names;
$vars{'varnish_translate'} = sub {
my $name = shift;
unless (exists($vars{'varnish_translate_table'}->{$name})) {
print STDERR "no translation for $name!\n";
exit(1);
}
my @r = @{$vars{'varnish_translate_table'}->{$name}};
delete @vars{@reset_names} if @reset_names;
if (($vars{varnish_type} = shift @r) eq 'NULL') {
@reset_names = ();
} else {
$vars{varnish_member} = shift @r;
}
my %a = @r;
@reset_names = map { 'varnish_' . $_ } keys %a;
@vars{@reset_names} = values %a;
return 0;
};
%field_dim_tab = (
vbeIPv4 => 4,
vbeIPv6 => 16
);
$vars{'set_field_dim'} = sub {
my $v = shift;
$vars{field_dim} = $field_dim_tab{$v};
return 0;
};
$vars{'modulename'} = $vars{'name'};
$vars{'modulename'} =~ s#.*/##;
$vars{'modulename'} =~ s/\.c$//;
0;
@endperl@
@open ${modulename}.h@
/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "vclient.h"
#include "modconf.h"
struct vsm *varnish_get_vsm_data(void);
int varnish_auth_response(const char *file, const char *challenge,
char response[CLI_AUTH_RESPONSE_LEN + 1]);
int varnish_ban(netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests,
struct vsm *vsm);
@open ${name}@
/* THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. */
#include "varnish_mib.h"
#include "backend.h"
static struct vsm *vd;
static struct vsc *vsc;
static void dict_load(struct vsc *vsc);
int
post_config(int majorID, int minorID, void *serverarg, void *clientarg)
{
vd = VSM_New();
if (!vd) {
snmp_log(LOG_ERR, "VSM_New: %s\n", strerror(errno));
return 1;
}
vsc = VSC_New(vd);
if (!vsc) {
snmp_log(LOG_ERR, "VSC_New: %s\n", strerror(errno));
return 1;
}
if (VSM_Attach(vd, 2))
snmp_log(LOG_ERR, "%s\n", VSM_Error(vd));
dict_load(vsc);
backend_collect_addr(vd);
return 0;
}
void
varnish_snmp_init(void)
{
snmp_register_callback(SNMP_CALLBACK_LIBRARY,
SNMP_CALLBACK_POST_READ_CONFIG,
post_config,
NULL);
}
void
varnish_snmp_deinit(void)
{
/* FIXME? */
}
struct vsm *
varnish_get_vsm_data(void)
{
if (vd) {
if (VSM_Status(vd) & (VSM_MGT_CHANGED|VSM_WRK_CHANGED)) {
DEBUGMSGTL(("$modulename", "reopening vd\n"));
dict_load(vsc);
backend_collect_addr(vd);
}
}
return vd;
}
${varnish_hash_table}
static struct VSC_point **
varnish_var_lookup(char const *str)
{
if (str) {
int i, h;
h = string_hash(str, vmib_hash_table_size);
i = h;
while (vmib_hash_table[i] != -1) {
if (strcmp(vmib_name_table[vmib_hash_table[i]], str) == 0)
return &vmib_data_table[vmib_hash_table[i]];
i = (i + 1) % vmib_hash_table_size;
if (i == h)
break;
}
}
return NULL;
}
static inline struct VSC_point *
varnish_get_oid(int i)
{
return vmib_data_table[i];
}
static void
dict_clear(void)
{
int i;
for (i = 0; i < MAX_VMIB_OID; i++)
if (vmib_data_table[i])
VSC_Destroy_Point(&vmib_data_table[i]);
}
static struct VSC_point const *
dict_install(struct VSC_point const *pt)
{
struct VSC_point **ent;
ent = varnish_var_lookup(pt->name);
if (!ent)
return NULL;
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"));
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);
}
}
vpt = dict_install(vpt);
return 0;
}
static void
dict_load(struct vsc *vsc)
{
struct vsm_fantom fantom = VSM_FANTOM_NULL;
dict_clear();
backend_clear();
VSC_Iter(vsc, &fantom, load_cb, NULL);
}
/* Variable handlers.
An instance handler only hands us one request at a time, unwrapping
any eventual GETNEXT requests.
*/
@foreach $i scalar@
@startperl@
&{$vars{'varnish_translate'}}($vars{'i'});
@endperl@
@if $varnish_type ne 'NULL'@
$varnish_if
static int
handle_$i(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
@if $i.settable@
int ret;
@end@
if (!varnish_get_vsm_data())
return SNMP_ERR_NOSUCHNAME;
switch (reqinfo->mode) {
case MODE_GET:
@if $varnish_type eq 'DICT'@
{
struct VSC_point *ent = varnish_get_oid($i);
uint64_t val;
if (!ent)
return SNMP_ERR_NOSUCHNAME;
val = *(uint64_t*)ent->ptr;
@if $i.type eq 'ASN_COUNTER64'@
{
struct counter64 ctr;
ctr.high = val >> 32;
ctr.low = val & 0xffffffff;
snmp_set_var_typed_value(requests->requestvb, $i.type,
&ctr,
sizeof(ctr));
}
@else@
@if $i.type eq 'ASN_TIMETICKS'@
val *= 100;
@end@
snmp_set_var_typed_value(requests->requestvb, $i.type,
&val,
sizeof(val));
@end@
}
@elsif $varnish_type eq 'STRING'@
{
const char *s = "$varnish_member";
snmp_set_var_typed_value(requests->requestvb, $i.type,
s, strlen(s));
}
@elsif $varnish_type eq 'PROG'@
{
void *valptr;
size_t valsize;
if ($varnish_member(&valptr, &valsize))
return SNMP_ERR_NOSUCHNAME;
snmp_set_var_typed_value(requests->requestvb, $i.type,
valptr, valsize);
@if $varnish_get_free@
free(valptr);
@end@
@elsif $varnish_type eq 'VAR'@
@if $i.type eq 'ASN_OCTET_STR'@
snmp_set_var_typed_value(requests->requestvb, $i.type,
$varnish_member, strlen($varnish_member));
@else@
snmp_set_var_typed_value(requests->requestvb, $i.type,
&$varnish_member, sizeof($varnish_member));
@end@
@elsif $varnish_type eq 'NUMBER'@
{
@if $i.type eq 'ASN_COUNTER64'@
struct counter64 ctr = { ($varnish_member>>32)&0xffffffff, ($varnish_member)&0xffffffff };
snmp_set_var_typed_value(requests->requestvb, $i.type,
&ctr,
sizeof(ctr));
@else@
uint64_t val = $varnish_member;
snmp_set_var_typed_value(requests->requestvb, $i.type,
&val,
sizeof(val));
@end@
}
@else@
@printf "unrecognized type %s for %s" $varnish_type $i@
@end@
break;
@if $i.settable@
/*
* SET REQUEST
*
* multiple states in the transaction. See:
* http://www.net-snmp.org/tutorial-5/toolkit/mib_module/set-actions.jpg
*/
case MODE_SET_RESERVE1:
/* or you could use netsnmp_check_vb_type_and_size instead */
ret = netsnmp_check_vb_type(requests->requestvb, $i.type);
if (ret != SNMP_ERR_NOERROR)
netsnmp_set_request_error(reqinfo, requests, ret);
break;
case MODE_SET_RESERVE2:
@if $varnish_set_reserve2 ne ''@
if ($varnish_set_reserve2 (reqinfo, requests, vd)) {
netsnmp_set_request_error(reqinfo, requests,
SNMP_ERR_RESOURCEUNAVAILABLE);
}
@end@
break;
case MODE_SET_FREE:
@if $varnish_set_free ne ''@
# Free resources allocated in RESERVE1 and/or
# RESERVE2. Something failed somewhere, and the states
# below won't be called.
$varnish_set_free(reqinfo, requests, vd);
@end@
break;
case MODE_SET_ACTION:
@if $varnish_set_action ne ''@
ret = $varnish_set_action(reqinfo, requests, vd);
if (ret != SNMP_ERR_NOERROR)
netsnmp_set_request_error(reqinfo, requests, ret);
@end@
break;
case MODE_SET_COMMIT:
@if $varnish_set_commit ne ''@
# delete temporary storage
if ($varnish_set_commit(reqinfo, requests, vd))
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_COMMITFAILED);
@end@
break;
case MODE_SET_UNDO:
@if $varnish_set_undo ne ''@
# UNDO and return to previous value for the object
if ($varnish_set_undo(reqinfo, requests, vd))
netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_UNDOFAILED);
@end@
break;
@end@
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_${i}\n", reqinfo->mode );
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
$varnish_endif
@end@
@end@
@foreach $i table@
## Determine the first/last column names
@eval $first_column = "-"@
@eval $last_column = "-"@
@foreach $c column@
@if $c.readable@
@if "$first_column" eq "-"@
@eval $first_column = $c@
@end@
@eval $last_column = $c@
@end@
@end@
@push@
@append ${modulename}.h@
/* column number definitions for table $i */
@foreach $c column@
#define COLUMN_$c.uc $c.subid
@end@
struct ${i}_entry {
/* Index values */
@foreach $idx index@
@if $idx.needlength@
@startperl@
&{$vars{'set_field_dim'}}($vars{'idx'});
@endperl@
@if $field_dim > 0@
$idx.decl $idx[$field_dim];
@else@
$idx.decl *$idx;
@end@
size_t ${idx}_len;
@else@
$idx.decl $idx;
@end@
@end@
/* Column values */
@foreach $c nonindex@
@if $c.readable@
@if $c.needlength@
@startperl@
&{$vars{'set_field_dim'}}($vars{'c'});
@endperl@
@if $field_dim > 0@
$c.decl ${c}[$field_dim];
@else@
$c.decl *$c;
@end@
size_t ${c}_len;
@else@
$c.decl $c;
@end@
@end@
@end@
};
extern unsigned ${i}_timeout;
int ${i}_load(netsnmp_cache *cache, void *vmagic);
void ${i}_free(netsnmp_cache *cache, void *vmagic);
@pop@
/** handles requests for the $i table */
static int
handle_table_${i}(
netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
netsnmp_request_info *request;
netsnmp_table_request_info *table_info;
struct ${i}_entry *table_entry;
switch (reqinfo->mode) {
case MODE_GET:
for (request = requests; request; request = request->next) {
table_entry = (struct ${i}_entry *)
netsnmp_tdata_extract_entry(request);
table_info = netsnmp_extract_table_info(request);
switch (table_info->colnum) {
@foreach $c column@
@if $c.readable@
case COLUMN_$c.uc:
if (!table_entry) {
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHINSTANCE);
continue;
}
@if $c.needlength@
snmp_set_var_typed_value(request->requestvb, $c.type,
table_entry->$c,
table_entry->${c}_len);
@elsif $c.type eq 'ASN_COUNTER64'@@
snmp_set_var_typed_value(requests->requestvb, $c.type,
&table_entry->$c,
sizeof(table_entry->$c));
@else@
snmp_set_var_typed_integer(request->requestvb, $c.type,
table_entry->$c);
@end@
break;
@end@
@end@
default:
netsnmp_set_request_error(reqinfo, request,
SNMP_NOSUCHOBJECT);
break;
}
}
break;
default:
/* we should never get here, so this is a really bad error */
snmp_log(LOG_ERR, "unknown mode (%d) in handle_table_${i}\n",
reqinfo->mode);
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
/** Initialize the $i table by defining its contents and how it's structured */
static void
initialize_table_$i(void)
{
const oid ${i}_oid[] = {$i.commaoid};
const size_t ${i}_oid_len = OID_LENGTH(${i}_oid);
netsnmp_handler_registration *reg;
netsnmp_tdata *table_data;
netsnmp_table_registration_info *table_info;
netsnmp_cache *cache;
DEBUGMSGTL(("${modulename}", "initializing table $i\n"));
reg = netsnmp_create_handler_registration("$i", handle_table_${i},
${i}_oid, ${i}_oid_len,
HANDLER_CAN_RONLY);
table_data = netsnmp_tdata_create_table("$i", 0);
if (!table_data) {
snmp_log(LOG_ERR,"error creating tdata table for $i\n");
return;
}
cache = netsnmp_cache_create(${i}_timeout,
${i}_load, ${i}_free,
${i}_oid, ${i}_oid_len);
if (!cache) {
snmp_log(LOG_ERR,"error creating cache for $i\n");
} else
cache->magic = (void *)table_data;
table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
if (!table_info) {
snmp_log(LOG_ERR,"error creating table info for $i\n");
return;
}
netsnmp_table_helper_add_indexes(table_info,
@foreach $idx index@
$idx.type, /* index: $idx */
@end@
0);
table_info->min_column = COLUMN_$first_column.uc;
table_info->max_column = COLUMN_$last_column.uc;
netsnmp_tdata_register(reg, table_data, table_info);
if (cache)
netsnmp_inject_handler(reg, netsnmp_cache_handler_get(cache));
/*
netsnmp_inject_handler_before(reg, netsnmp_cache_handler_get(cache),
TABLE_TDATA_NAME);
*/
}
@end@
/** Initializes the $name module */
void
init_$modulename(void)
{
if (vd) {
snmp_log(LOG_ERR, "%s: can't be loaded twice\n", "$modulename");
abort();
}
@foreach $i scalar@
@startperl@
&{$vars{'varnish_translate'}}($vars{'i'});
@endperl@
@if $varnish_type ne 'NULL'@
$varnish_if
const oid ${i}_oid[] = { $i.commaoid };
$varnish_endif
@end@
@end@
DEBUGMSGTL(("$modulename", "Initializing\n"));
varnish_mib_config_init();
@foreach $i scalar@
@startperl@
&{$vars{'varnish_translate'}}($vars{'i'});
@endperl@
@if $varnish_type ne 'NULL'@
$varnish_if
netsnmp_register_scalar(
netsnmp_create_handler_registration("$i", handle_$i,
${i}_oid, OID_LENGTH(${i}_oid),
@if !$i.settable@
HANDLER_CAN_RONLY
@end@
@if $i.settable@
HANDLER_CAN_RWRITE
@end@
));
$varnish_endif
@end@
@end@
@foreach $i table@
initialize_table_$i();
@end@
varnish_snmp_init();
}
void
deinit_$modulename(void)
{
varnish_snmp_deinit();
}
@calldefine ROCOM@