# 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( 'const volatile uint64_t *', $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 *vsm; static struct vsc *vsc; static void dict_load(struct vsc *vsc); int post_config(int majorID, int minorID, void *serverarg, void *clientarg) { vsm = VSM_New(); if (!vsm) { snmp_log(LOG_ERR, "VSM_New: %s\n", strerror(errno)); return 1; } vsc = VSC_New(); if (!vsc) { snmp_log(LOG_ERR, "VSC_New: %s\n", strerror(errno)); return 1; } if (VSM_Attach(vsm, 2)) snmp_log(LOG_ERR, "%s\n", VSM_Error(vsm)); dict_load(vsc); backend_collect_addr(vsm); 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 (vsm) { if (VSM_Status(vsm) & (VSM_MGT_CHANGED|VSM_WRK_CHANGED)) { DEBUGMSGTL(("$modulename", "reopening vsm\n")); dict_load(vsc); backend_collect_addr(vsm); } } return vsm; } ${varnish_hash_table} static const volatile uint64_t ** 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 const volatile uint64_t * 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++) vmib_data_table[i] = NULL; } static void dict_install(struct VSC_point const *pt) { const volatile uint64_t **ent; ent = varnish_var_lookup(pt->name); if (ent) *ent = pt->ptr; } 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); } } dict_install(vpt); return 0; } static void dict_load(struct vsc *vsc) { dict_clear(); backend_clear(); VSC_Iter(vsc, vsm, 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'@ { const volatile uint64_t *ent = varnish_get_oid($i); uint64_t val; if (!ent) return SNMP_ERR_NOSUCHNAME; val = *ent; @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, vsm)) { 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, vsm); @end@ break; case MODE_SET_ACTION: @if $varnish_set_action ne ''@ ret = $varnish_set_action(reqinfo, requests, vsm); 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, vsm)) 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, vsm)) 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 (vsm) { 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@