Path: blob/main/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c
108351 views
/*-1* Copyright (c) 2005-2006 The FreeBSD Project2* All rights reserved.3*4* Author: Victor Cruceru <[email protected]>5*6* Redistribution of this software and documentation and use in source and7* binary forms, with or without modification, are permitted provided that8* the following conditions are met:9*10* 1. Redistributions of source code or documentation must retain the above11* copyright notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* Host Resources MIB for SNMPd. Implementation for hrStorageTable31*/3233#include <sys/types.h>34#include <sys/param.h>35#include <sys/sysctl.h>36#include <sys/vmmeter.h>37#include <sys/mount.h>3839#include <vm/vm_param.h>4041#include <assert.h>42#include <err.h>43#include <limits.h>44#include <memstat.h>45#include <paths.h>46#include <stdlib.h>47#include <string.h>48#include <syslog.h>49#include <unistd.h> /* for getpagesize() */50#include <sysexits.h>5152#include "hostres_snmp.h"53#include "hostres_oid.h"54#include "hostres_tree.h"5556/* maximum length for description string according to MIB */57#define SE_DESC_MLEN (255 + 1)5859/*60* This structure is used to hold a SNMP table entry61* for HOST-RESOURCES-MIB's hrStorageTable62*/63struct storage_entry {64int32_t index;65const struct asn_oid *type;66u_char *descr;67int32_t allocationUnits;68int32_t size;69int32_t used;70uint32_t allocationFailures;71#define HR_STORAGE_FOUND 0x00172uint32_t flags; /* to be used internally*/73TAILQ_ENTRY(storage_entry) link;74};75TAILQ_HEAD(storage_tbl, storage_entry);7677/*78* Next structure is used to keep o list of mappings from a specific name79* (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the80* same index for a specific name at least for the duration of one SNMP agent81* run.82*/83struct storage_map_entry {84int32_t hrIndex; /* used for storage_entry::index */8586/* map key, also used for storage_entry::descr */87u_char *a_name;8889/*90* next may be NULL if the respective storage_entry91* is (temporally) gone92*/93struct storage_entry *entry;94STAILQ_ENTRY(storage_map_entry) link;95};96STAILQ_HEAD(storage_map, storage_map_entry);9798/* the head of the list with table's entries */99static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl);100101/*for consistent table indexing*/102static struct storage_map storage_map =103STAILQ_HEAD_INITIALIZER(storage_map);104105/* last (agent) tick when hrStorageTable was updated */106static uint64_t storage_tick;107108/* maximum number of ticks between two refreshs */109uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100;110111/* for kvm_getswapinfo, malloc'd */112static struct kvm_swap *swap_devs;113static size_t swap_devs_len; /* item count for swap_devs */114115/* for getfsstat, malloc'd */116static struct statfs *fs_buf;117static size_t fs_buf_count; /* item count for fs_buf */118119static struct vmtotal mem_stats;120121/* next int available for indexing the hrStorageTable */122static uint32_t next_storage_index = 1;123124/* start of list for memory detailed stats */125static struct memory_type_list *mt_list;126127/* Constants */128static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam;129static const struct asn_oid OIDX_hrStorageVirtualMemory_c =130OIDX_hrStorageVirtualMemory;131132/**133* Create a new entry into the storage table and, if necessary, an134* entry into the storage map.135*/136static struct storage_entry *137storage_entry_create(const char *name)138{139struct storage_entry *entry;140struct storage_map_entry *map;141size_t name_len;142143assert(name != NULL);144assert(strlen(name) > 0);145146STAILQ_FOREACH(map, &storage_map, link)147if (strcmp(map->a_name, name) == 0)148break;149150if (map == NULL) {151/* new object - get a new index */152if (next_storage_index > INT_MAX) {153syslog(LOG_ERR,154"%s: hrStorageTable index wrap", __func__);155errx(EX_SOFTWARE, "hrStorageTable index wrap");156}157158if ((map = malloc(sizeof(*map))) == NULL) {159syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ );160return (NULL);161}162163name_len = strlen(name) + 1;164if (name_len > SE_DESC_MLEN)165name_len = SE_DESC_MLEN;166167if ((map->a_name = malloc(name_len)) == NULL) {168free(map);169return (NULL);170}171172strlcpy(map->a_name, name, name_len);173map->hrIndex = next_storage_index++;174175STAILQ_INSERT_TAIL(&storage_map, map, link);176177HRDBG("%s added into hrStorageMap at index=%d",178name, map->hrIndex);179} else {180HRDBG("%s exists in hrStorageMap index=%d\n",181name, map->hrIndex);182}183184if ((entry = malloc(sizeof(*entry))) == NULL) {185syslog(LOG_WARNING, "%s: %m", __func__);186return (NULL);187}188memset(entry, 0, sizeof(*entry));189190entry->index = map->hrIndex;191192if ((entry->descr = strdup(map->a_name)) == NULL) {193free(entry);194return (NULL);195}196197map->entry = entry;198199INSERT_OBJECT_INT(entry, &storage_tbl);200201return (entry);202}203204/**205* Delete an entry from the storage table.206*/207static void208storage_entry_delete(struct storage_entry *entry)209{210struct storage_map_entry *map;211212assert(entry != NULL);213214TAILQ_REMOVE(&storage_tbl, entry, link);215STAILQ_FOREACH(map, &storage_map, link)216if (map->entry == entry) {217map->entry = NULL;218break;219}220free(entry->descr);221free(entry);222}223224/**225* Find a table entry by its name.226*/227static struct storage_entry *228storage_find_by_name(const char *name)229{230struct storage_entry *entry;231232TAILQ_FOREACH(entry, &storage_tbl, link)233if (strcmp(entry->descr, name) == 0)234return (entry);235236return (NULL);237}238239/*240* VM info.241*/242static void243storage_OS_get_vm(void)244{245int mib[2] = { CTL_VM, VM_TOTAL };246size_t len = sizeof(mem_stats);247int page_size_bytes;248struct storage_entry *entry;249250if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) {251syslog(LOG_ERR,252"hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) "253"failed: %m", __func__);254assert(0);255return;256}257258page_size_bytes = getpagesize();259260/* Real Memory Metrics */261if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL &&262(entry = storage_entry_create("Real Memory Metrics")) == NULL)263return; /* I'm out of luck now, maybe next time */264265entry->flags |= HR_STORAGE_FOUND;266entry->type = &OIDX_hrStorageRam_c;267entry->allocationUnits = page_size_bytes;268entry->size = mem_stats.t_rm;269entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */270entry->allocationFailures = 0;271272/* Shared Real Memory Metrics */273if ((entry = storage_find_by_name("Shared Real Memory Metrics")) ==274NULL &&275(entry = storage_entry_create("Shared Real Memory Metrics")) ==276NULL)277return;278279entry->flags |= HR_STORAGE_FOUND;280entry->type = &OIDX_hrStorageRam_c;281entry->allocationUnits = page_size_bytes;282entry->size = mem_stats.t_rmshr;283/* ACTIVE is not USED - FIXME */284entry->used = mem_stats.t_armshr;285entry->allocationFailures = 0;286}287288static void289storage_OS_get_memstat(void)290{291struct memory_type *mt_item;292struct storage_entry *entry;293294if (mt_list == NULL) {295if ((mt_list = memstat_mtl_alloc()) == NULL)296/* again? we have a serious problem */297return;298}299300if (memstat_sysctl_all(mt_list, 0) < 0) {301syslog(LOG_ERR, "memstat_sysctl_all failed: %s",302memstat_strerror(memstat_mtl_geterror(mt_list)) );303return;304}305306if ((mt_item = memstat_mtl_first(mt_list)) == NULL) {307/* usually this is not an error, no errno for this failure*/308HRDBG("memstat_mtl_first failed");309return;310}311312do {313const char *memstat_name;314uint64_t tmp_size;315int allocator;316char alloc_descr[SE_DESC_MLEN];317318memstat_name = memstat_get_name(mt_item);319320if (memstat_name == NULL || strlen(memstat_name) == 0)321continue;322323switch (allocator = memstat_get_allocator(mt_item)) {324325case ALLOCATOR_MALLOC:326snprintf(alloc_descr, sizeof(alloc_descr),327"MALLOC: %s", memstat_name);328break;329330case ALLOCATOR_UMA:331snprintf(alloc_descr, sizeof(alloc_descr),332"UMA: %s", memstat_name);333break;334335default:336snprintf(alloc_descr, sizeof(alloc_descr),337"UNKNOWN%d: %s", allocator, memstat_name);338break;339}340341if ((entry = storage_find_by_name(alloc_descr)) == NULL &&342(entry = storage_entry_create(alloc_descr)) == NULL)343return;344345entry->flags |= HR_STORAGE_FOUND;346entry->type = &OIDX_hrStorageRam_c;347348if ((tmp_size = memstat_get_size(mt_item)) == 0)349tmp_size = memstat_get_sizemask(mt_item);350entry->allocationUnits =351(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);352353tmp_size = memstat_get_countlimit(mt_item);354entry->size =355(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);356357tmp_size = memstat_get_count(mt_item);358entry->used =359(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);360361tmp_size = memstat_get_failures(mt_item);362entry->allocationFailures =363(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);364365} while((mt_item = memstat_mtl_next(mt_item)) != NULL);366}367368/**369* Get swap info370*/371static void372storage_OS_get_swap(void)373{374struct storage_entry *entry;375char swap_w_prefix[SE_DESC_MLEN];376size_t len;377int nswapdev;378379len = sizeof(nswapdev);380nswapdev = 0;381382if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) {383syslog(LOG_ERR,384"hrStorageTable: sysctlbyname(\"vm.nswapdev\") "385"failed. %m");386assert(0);387return;388}389390if (nswapdev <= 0) {391HRDBG("vm.nswapdev is %d", nswapdev);392return;393}394395if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) {396swap_devs_len = nswapdev + 1;397swap_devs = reallocf(swap_devs,398swap_devs_len * sizeof(struct kvm_swap));399400assert(swap_devs != NULL);401if (swap_devs == NULL) {402swap_devs_len = 0;403return;404}405}406407nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0);408if (nswapdev < 0) {409syslog(LOG_ERR,410"hrStorageTable: kvm_getswapinfo failed. %m\n");411assert(0);412return;413}414415for (len = 0; len < (size_t)nswapdev; len++) {416memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix));417snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1,418"Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname);419420entry = storage_find_by_name(swap_w_prefix);421if (entry == NULL)422entry = storage_entry_create(swap_w_prefix);423424assert (entry != NULL);425if (entry == NULL)426return; /* Out of luck */427428entry->flags |= HR_STORAGE_FOUND;429entry->type = &OIDX_hrStorageVirtualMemory_c;430entry->allocationUnits = getpagesize();431entry->size = swap_devs[len].ksw_total;432entry->used = swap_devs[len].ksw_used;433entry->allocationFailures = 0;434}435}436437/**438* Query the underlaying OS for the mounted file systems439* anf fill in the respective lists (for hrStorageTable and for hrFSTable)440*/441static void442storage_OS_get_fs(void)443{444struct storage_entry *entry;445uint64_t size, used;446int i, mounted_fs_count, units;447char fs_string[SE_DESC_MLEN];448449if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) {450syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");451return; /* out of luck this time */452}453454if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) {455fs_buf_count = mounted_fs_count;456fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs));457if (fs_buf == NULL) {458fs_buf_count = 0;459assert(0);460return;461}462}463464if ((mounted_fs_count = getfsstat(fs_buf,465fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) {466syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");467return; /* out of luck this time */468}469470HRDBG("got %d mounted FS", mounted_fs_count);471472fs_tbl_pre_refresh();473474for (i = 0; i < mounted_fs_count; i++) {475snprintf(fs_string, sizeof(fs_string),476"%s, type: %s, dev: %s", fs_buf[i].f_mntonname,477fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname);478479entry = storage_find_by_name(fs_string);480if (entry == NULL)481entry = storage_entry_create(fs_string);482483assert (entry != NULL);484if (entry == NULL)485return; /* Out of luck */486487entry->flags |= HR_STORAGE_FOUND;488entry->type = fs_get_type(&fs_buf[i]); /*XXX - This is wrong*/489490units = fs_buf[i].f_bsize;491size = fs_buf[i].f_blocks;492used = fs_buf[i].f_blocks - fs_buf[i].f_bfree;493while (size > INT_MAX) {494units <<= 1;495size >>= 1;496used >>= 1;497}498entry->allocationUnits = units;499entry->size = size;500entry->used = used;501502entry->allocationFailures = 0;503504/* take care of hrFSTable */505fs_tbl_process_statfs_entry(&fs_buf[i], entry->index);506}507508fs_tbl_post_refresh();509}510511/**512* Initialize storage table and populate it.513*/514void515init_storage_tbl(void)516{517if ((mt_list = memstat_mtl_alloc()) == NULL)518syslog(LOG_ERR,519"hrStorageTable: memstat_mtl_alloc() failed: %m");520521refresh_storage_tbl(1);522}523524void525fini_storage_tbl(void)526{527struct storage_map_entry *n1;528529if (swap_devs != NULL) {530free(swap_devs);531swap_devs = NULL;532}533swap_devs_len = 0;534535if (fs_buf != NULL) {536free(fs_buf);537fs_buf = NULL;538}539fs_buf_count = 0;540541while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) {542STAILQ_REMOVE_HEAD(&storage_map, link);543if (n1->entry != NULL) {544TAILQ_REMOVE(&storage_tbl, n1->entry, link);545free(n1->entry->descr);546free(n1->entry);547}548free(n1->a_name);549free(n1);550}551assert(TAILQ_EMPTY(&storage_tbl));552}553554void555refresh_storage_tbl(int force)556{557struct storage_entry *entry, *entry_tmp;558559if (!force && storage_tick != 0 &&560this_tick - storage_tick < storage_tbl_refresh) {561HRDBG("no refresh needed");562return;563}564565/* mark each entry as missing */566TAILQ_FOREACH(entry, &storage_tbl, link)567entry->flags &= ~HR_STORAGE_FOUND;568569storage_OS_get_vm();570storage_OS_get_swap();571storage_OS_get_fs();572storage_OS_get_memstat();573574/*575* Purge items that disappeared576*/577TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp)578if (!(entry->flags & HR_STORAGE_FOUND))579storage_entry_delete(entry);580581storage_tick = this_tick;582583HRDBG("refresh DONE");584}585586/*587* This is the implementation for a generated (by our SNMP tool)588* function prototype, see hostres_tree.h589* It handles the SNMP operations for hrStorageTable590*/591int592op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value,593u_int sub, u_int iidx __unused, enum snmp_op curr_op)594{595struct storage_entry *entry;596597refresh_storage_tbl(0);598599switch (curr_op) {600601case SNMP_OP_GETNEXT:602if ((entry = NEXT_OBJECT_INT(&storage_tbl,603&value->var, sub)) == NULL)604return (SNMP_ERR_NOSUCHNAME);605606value->var.len = sub + 1;607value->var.subs[sub] = entry->index;608goto get;609610case SNMP_OP_GET:611if ((entry = FIND_OBJECT_INT(&storage_tbl,612&value->var, sub)) == NULL)613return (SNMP_ERR_NOSUCHNAME);614goto get;615616case SNMP_OP_SET:617if ((entry = FIND_OBJECT_INT(&storage_tbl,618&value->var, sub)) == NULL)619return (SNMP_ERR_NO_CREATION);620return (SNMP_ERR_NOT_WRITEABLE);621622case SNMP_OP_ROLLBACK:623case SNMP_OP_COMMIT:624abort();625}626abort();627628get:629switch (value->var.subs[sub - 1]) {630631case LEAF_hrStorageIndex:632value->v.integer = entry->index;633return (SNMP_ERR_NOERROR);634635case LEAF_hrStorageType:636assert(entry->type != NULL);637value->v.oid = *entry->type;638return (SNMP_ERR_NOERROR);639640case LEAF_hrStorageDescr:641assert(entry->descr != NULL);642return (string_get(value, entry->descr, -1));643break;644645case LEAF_hrStorageAllocationUnits:646value->v.integer = entry->allocationUnits;647return (SNMP_ERR_NOERROR);648649case LEAF_hrStorageSize:650value->v.integer = entry->size;651return (SNMP_ERR_NOERROR);652653case LEAF_hrStorageUsed:654value->v.integer = entry->used;655return (SNMP_ERR_NOERROR);656657case LEAF_hrStorageAllocationFailures:658value->v.uint32 = entry->allocationFailures;659return (SNMP_ERR_NOERROR);660}661abort();662}663664665