Path: blob/main/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c
107110 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 the hrDiskStorageTable31*/3233#include <sys/types.h>34#include <sys/param.h>35#include <sys/ata.h>36#include <sys/disk.h>37#include <sys/linker.h>38#include <sys/mdioctl.h>39#include <sys/module.h>40#include <sys/sysctl.h>4142#include <assert.h>43#include <ctype.h>44#include <err.h>45#include <errno.h>46#include <paths.h>47#include <stdlib.h>48#include <string.h>49#include <syslog.h>50#include <unistd.h>5152#include "hostres_snmp.h"53#include "hostres_oid.h"54#include "hostres_tree.h"5556enum hrDiskStrorageAccess {57DS_READ_WRITE = 1,58DS_READ_ONLY = 259};6061enum hrDiskStrorageMedia {62DSM_OTHER = 1,63DSM_UNKNOWN = 2,64DSM_HARDDISK = 3,65DSM_FLOPPYDISK = 4,66DSM_OPTICALDISKROM= 5,67DSM_OPTICALDISKWORM= 6,68DSM_OPTICALDISKRW= 7,69DSM_RAMDISK = 870};7172/*73* This structure is used to hold a SNMP table entry for HOST-RESOURCES-MIB's74* hrDiskStorageTable. Note that index is external being allocated and75* maintained by the hrDeviceTable code.76*77* NOTE: according to MIB removable means removable media, not the78* device itself (like a USB card reader)79*/80struct disk_entry {81int32_t index;82int32_t access; /* enum hrDiskStrorageAccess */83int32_t media; /* enum hrDiskStrorageMedia*/84int32_t removable; /* enum snmpTCTruthValue*/85int32_t capacity;86TAILQ_ENTRY(disk_entry) link;87/*88* next items are not from the SNMP mib table, only to be used89* internally90*/91#define HR_DISKSTORAGE_FOUND 0x00192#define HR_DISKSTORAGE_ATA 0x002 /* belongs to the ATA subsystem */93#define HR_DISKSTORAGE_MD 0x004 /* it is a MD (memory disk) */94uint32_t flags;95uint64_t r_tick;96u_char dev_name[32]; /* device name, i.e. "ad4" or "acd0" */97};98TAILQ_HEAD(disk_tbl, disk_entry);99100/* the head of the list with hrDiskStorageTable's entries */101static struct disk_tbl disk_tbl =102TAILQ_HEAD_INITIALIZER(disk_tbl);103104/* last tick when hrFSTable was updated */105static uint64_t disk_storage_tick;106107/* minimum number of ticks between refreshs */108uint32_t disk_storage_tbl_refresh = HR_DISK_TBL_REFRESH * 100;109110/* fd for "/dev/mdctl"*/111static int md_fd = -1;112113/* buffer for sysctl("kern.disks") */114static char *disk_list;115static size_t disk_list_len;116117/* some constants */118static const struct asn_oid OIDX_hrDeviceDiskStorage_c =119OIDX_hrDeviceDiskStorage;120121/**122* Load the MD driver if it isn't loaded already.123*/124static void125mdmaybeload(void)126{127char name1[64], name2[64];128129snprintf(name1, sizeof(name1), "g_%s", MD_NAME);130snprintf(name2, sizeof(name2), "geom_%s", MD_NAME);131if (modfind(name1) == -1) {132/* Not present in kernel, try loading it. */133if (kldload(name2) == -1 || modfind(name1) == -1) {134if (errno != EEXIST) {135errx(EXIT_FAILURE,136"%s module not available!", name2);137}138}139}140}141142/**143* Create a new entry into the DiskStorageTable.144*/145static struct disk_entry *146disk_entry_create(const struct device_entry *devEntry)147{148struct disk_entry *entry;149150assert(devEntry != NULL);151if (devEntry == NULL)152return NULL;153154if ((entry = malloc(sizeof(*entry))) == NULL) {155syslog(LOG_WARNING, "hrDiskStorageTable: %s: %m", __func__);156return (NULL);157}158159memset(entry, 0, sizeof(*entry));160entry->index = devEntry->index;161INSERT_OBJECT_INT(entry, &disk_tbl);162163return (entry);164}165166/**167* Delete a disk table entry.168*/169static void170disk_entry_delete(struct disk_entry *entry)171{172struct device_entry *devEntry;173174assert(entry != NULL);175TAILQ_REMOVE(&disk_tbl, entry, link);176177devEntry = device_find_by_index(entry->index);178179free(entry);180181/*182* Also delete the respective device entry -183* this is needed for disk devices that are not184* detected by libdevinfo185*/186if (devEntry != NULL &&187(devEntry->flags & HR_DEVICE_IMMUTABLE) == HR_DEVICE_IMMUTABLE)188device_entry_delete(devEntry);189}190191/**192* Find a disk storage entry given its index.193*/194static struct disk_entry *195disk_find_by_index(int32_t idx)196{197struct disk_entry *entry;198199TAILQ_FOREACH(entry, &disk_tbl, link)200if (entry->index == idx)201return (entry);202203return (NULL);204}205206/**207* Get the disk parameters208*/209static void210disk_query_disk(struct disk_entry *entry)211{212char dev_path[128];213int fd;214off_t mediasize;215216if (entry == NULL || entry->dev_name[0] == '\0')217return;218219snprintf(dev_path, sizeof(dev_path),220"%s%s", _PATH_DEV, entry->dev_name);221entry->capacity = 0;222223HRDBG("OPENING device %s", dev_path);224if ((fd = open(dev_path, O_RDONLY|O_NONBLOCK)) == -1) {225HRDBG("OPEN device %s failed: %s", dev_path, strerror(errno));226return;227}228229if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) {230HRDBG("DIOCGMEDIASIZE for device %s failed: %s",231dev_path, strerror(errno));232(void)close(fd);233return;234}235236mediasize = mediasize / 1024;237entry->capacity = (mediasize > INT_MAX ? INT_MAX : mediasize);238partition_tbl_handle_disk(entry->index, entry->dev_name);239240(void)close(fd);241}242243/**244* Find all ATA disks in the device table.245*/246static void247disk_OS_get_ATA_disks(void)248{249struct device_map_entry *map;250struct device_entry *entry;251struct disk_entry *disk_entry;252const struct disk_entry *found;253254/* Things we know are ata disks */255static const struct disk_entry lookup[] = {256{257.dev_name = "ad",258.media = DSM_HARDDISK,259.removable = SNMP_FALSE260},261{262.dev_name = "ar",263.media = DSM_OTHER,264.removable = SNMP_FALSE265},266{267.dev_name = "acd",268.media = DSM_OPTICALDISKROM,269.removable = SNMP_TRUE270},271{272.dev_name = "afd",273.media = DSM_FLOPPYDISK,274.removable = SNMP_TRUE275},276{277.dev_name = "ast",278.media = DSM_OTHER,279.removable = SNMP_TRUE280},281282{ .media = DSM_UNKNOWN }283};284285/* Walk over the device table looking for ata disks */286STAILQ_FOREACH(map, &device_map, link) {287/* Skip deleted entries. */288if (map->entry_p == NULL)289continue;290for (found = lookup; found->media != DSM_UNKNOWN; found++) {291if (strncmp(map->name_key, found->dev_name,292strlen(found->dev_name)) != 0)293continue;294295/*296* Avoid false disk devices. For example adw(4) and297* adv(4) - they are not disks!298*/299if (strlen(map->name_key) > strlen(found->dev_name) &&300!isdigit(map->name_key[strlen(found->dev_name)]))301continue;302303/* First get the entry from the hrDeviceTbl */304entry = map->entry_p;305entry->type = &OIDX_hrDeviceDiskStorage_c;306307/* Then check hrDiskStorage table for this device */308disk_entry = disk_find_by_index(entry->index);309if (disk_entry == NULL) {310disk_entry = disk_entry_create(entry);311if (disk_entry == NULL)312continue;313314disk_entry->access = DS_READ_WRITE;315strlcpy(disk_entry->dev_name, entry->name,316sizeof(disk_entry->dev_name));317318disk_entry->media = found->media;319disk_entry->removable = found->removable;320}321322disk_entry->flags |= HR_DISKSTORAGE_FOUND;323disk_entry->flags |= HR_DISKSTORAGE_ATA;324325disk_query_disk(disk_entry);326disk_entry->r_tick = this_tick;327}328}329}330331/**332* Find MD disks in the device table.333*/334static void335disk_OS_get_MD_disks(void)336{337struct device_map_entry *map;338struct device_entry *entry;339struct disk_entry *disk_entry;340struct md_ioctl mdio;341int unit;342343if (md_fd <= 0)344return;345346/* Look for md devices */347STAILQ_FOREACH(map, &device_map, link) {348/* Skip deleted entries. */349if (map->entry_p == NULL)350continue;351if (sscanf(map->name_key, "md%d", &unit) != 1)352continue;353354/* First get the entry from the hrDeviceTbl */355entry = device_find_by_index(map->hrIndex);356entry->type = &OIDX_hrDeviceDiskStorage_c;357358/* Then check hrDiskStorage table for this device */359disk_entry = disk_find_by_index(entry->index);360if (disk_entry == NULL) {361disk_entry = disk_entry_create(entry);362if (disk_entry == NULL)363continue;364365memset(&mdio, 0, sizeof(mdio));366mdio.md_version = MDIOVERSION;367mdio.md_unit = unit;368369if (ioctl(md_fd, MDIOCQUERY, &mdio) < 0) {370syslog(LOG_ERR,371"hrDiskStorageTable: Couldnt ioctl");372continue;373}374375if ((mdio.md_options & MD_READONLY) == MD_READONLY)376disk_entry->access = DS_READ_ONLY;377else378disk_entry->access = DS_READ_WRITE;379380strlcpy(disk_entry->dev_name, entry->name,381sizeof(disk_entry->dev_name));382383disk_entry->media = DSM_RAMDISK;384disk_entry->removable = SNMP_FALSE;385}386387disk_entry->flags |= HR_DISKSTORAGE_FOUND;388disk_entry->flags |= HR_DISKSTORAGE_MD;389disk_entry->r_tick = this_tick;390}391}392393/**394* Find rest of disks395*/396static void397disk_OS_get_disks(void)398{399size_t disk_cnt = 0;400struct device_entry *entry;401struct disk_entry *disk_entry;402403size_t need = 0;404405if (sysctlbyname("kern.disks", NULL, &need, NULL, 0) == -1) {406syslog(LOG_ERR, "%s: sysctl_1 kern.disks failed: %m", __func__);407return;408}409410if (need == 0)411return;412413if (disk_list_len != need + 1 || disk_list == NULL) {414disk_list_len = need + 1;415disk_list = reallocf(disk_list, disk_list_len);416}417418if (disk_list == NULL) {419syslog(LOG_ERR, "%s: reallocf failed", __func__);420disk_list_len = 0;421return;422}423424memset(disk_list, 0, disk_list_len);425426if (sysctlbyname("kern.disks", disk_list, &need, NULL, 0) == -1 ||427disk_list[0] == 0) {428syslog(LOG_ERR, "%s: sysctl_2 kern.disks failed: %m", __func__);429return;430}431432for (disk_cnt = 0; disk_cnt < need; disk_cnt++) {433char *disk = NULL;434char disk_device[128] = "";435436disk = strsep(&disk_list, " ");437if (disk == NULL)438break;439440snprintf(disk_device, sizeof(disk_device),441"%s%s", _PATH_DEV, disk);442443/* First check if the disk is in the hrDeviceTable. */444if ((entry = device_find_by_name(disk)) == NULL) {445/*446* not found there - insert it as immutable447*/448syslog(LOG_WARNING, "%s: adding device '%s' to "449"device list", __func__, disk);450451if ((entry = device_entry_create(disk, "", "")) == NULL)452continue;453454entry->flags |= HR_DEVICE_IMMUTABLE;455}456457entry->type = &OIDX_hrDeviceDiskStorage_c;458459/* Then check hrDiskStorage table for this device */460disk_entry = disk_find_by_index(entry->index);461if (disk_entry == NULL) {462disk_entry = disk_entry_create(entry);463if (disk_entry == NULL)464continue;465}466467disk_entry->flags |= HR_DISKSTORAGE_FOUND;468469if ((disk_entry->flags & HR_DISKSTORAGE_ATA) ||470(disk_entry->flags & HR_DISKSTORAGE_MD)) {471/*472* ATA/MD detection is running before this one,473* so don't waste the time here474*/475continue;476}477478disk_entry->access = DS_READ_WRITE;479disk_entry->media = DSM_UNKNOWN;480disk_entry->removable = SNMP_FALSE;481482if (strncmp(disk_entry->dev_name, "da", 2) == 0 ||483strncmp(disk_entry->dev_name, "ada", 3) == 0) {484disk_entry->media = DSM_HARDDISK;485disk_entry->removable = SNMP_FALSE;486} else if (strncmp(disk_entry->dev_name, "cd", 2) == 0) {487disk_entry->media = DSM_OPTICALDISKROM;488disk_entry->removable = SNMP_TRUE;489} else {490disk_entry->media = DSM_UNKNOWN;491disk_entry->removable = SNMP_FALSE;492}493494strlcpy((char *)disk_entry->dev_name, disk,495sizeof(disk_entry->dev_name));496497disk_query_disk(disk_entry);498disk_entry->r_tick = this_tick;499}500}501502/**503* Refresh routine for hrDiskStorageTable504* Usable for polling the system for any changes.505*/506void507refresh_disk_storage_tbl(int force)508{509struct disk_entry *entry, *entry_tmp;510511if (disk_storage_tick != 0 && !force &&512this_tick - disk_storage_tick < disk_storage_tbl_refresh) {513HRDBG("no refresh needed");514return;515}516517partition_tbl_pre_refresh();518519/* mark each entry as missing */520TAILQ_FOREACH(entry, &disk_tbl, link)521entry->flags &= ~HR_DISKSTORAGE_FOUND;522523disk_OS_get_ATA_disks(); /* this must be called first ! */524disk_OS_get_MD_disks();525disk_OS_get_disks();526527/*528* Purge items that disappeared529*/530TAILQ_FOREACH_SAFE(entry, &disk_tbl, link, entry_tmp)531if (!(entry->flags & HR_DISKSTORAGE_FOUND))532/* XXX remove IMMUTABLE entries that have disappeared */533disk_entry_delete(entry);534535disk_storage_tick = this_tick;536537partition_tbl_post_refresh();538539HRDBG("refresh DONE");540}541542/*543* Init the things for both of hrDiskStorageTable544*/545int546init_disk_storage_tbl(void)547{548char mddev[32] = "";549550/* Try to load md.ko if not loaded already */551mdmaybeload();552553md_fd = -1;554snprintf(mddev, sizeof(mddev) - 1, "%s%s", _PATH_DEV, MDCTL_NAME);555if ((md_fd = open(mddev, O_RDWR)) == -1) {556syslog(LOG_ERR, "open %s failed - will not include md(4) "557"info: %m", mddev);558}559560refresh_disk_storage_tbl(1);561562return (0);563}564565/*566* Finalization routine for hrDiskStorageTable567* It destroys the lists and frees any allocated heap memory568*/569void570fini_disk_storage_tbl(void)571{572struct disk_entry *n1;573574while ((n1 = TAILQ_FIRST(&disk_tbl)) != NULL) {575TAILQ_REMOVE(&disk_tbl, n1, link);576free(n1);577}578579free(disk_list);580581if (md_fd > 0) {582if (close(md_fd) == -1)583syslog(LOG_ERR,"close (/dev/mdctl) failed: %m");584md_fd = -1;585}586}587588/*589* This is the implementation for a generated (by our SNMP "compiler" tool)590* function prototype, see hostres_tree.h591* It handles the SNMP operations for hrDiskStorageTable592*/593int594op_hrDiskStorageTable(struct snmp_context *ctx __unused,595struct snmp_value *value, u_int sub, u_int iidx __unused,596enum snmp_op curr_op)597{598struct disk_entry *entry;599600refresh_disk_storage_tbl(0);601602switch (curr_op) {603604case SNMP_OP_GETNEXT:605if ((entry = NEXT_OBJECT_INT(&disk_tbl,606&value->var, sub)) == NULL)607return (SNMP_ERR_NOSUCHNAME);608value->var.len = sub + 1;609value->var.subs[sub] = entry->index;610goto get;611612case SNMP_OP_GET:613if ((entry = FIND_OBJECT_INT(&disk_tbl,614&value->var, sub)) == NULL)615return (SNMP_ERR_NOSUCHNAME);616goto get;617618case SNMP_OP_SET:619if ((entry = FIND_OBJECT_INT(&disk_tbl,620&value->var, sub)) == NULL)621return (SNMP_ERR_NO_CREATION);622return (SNMP_ERR_NOT_WRITEABLE);623624case SNMP_OP_ROLLBACK:625case SNMP_OP_COMMIT:626abort();627}628abort();629630get:631switch (value->var.subs[sub - 1]) {632633case LEAF_hrDiskStorageAccess:634value->v.integer = entry->access;635return (SNMP_ERR_NOERROR);636637case LEAF_hrDiskStorageMedia:638value->v.integer = entry->media;639return (SNMP_ERR_NOERROR);640641case LEAF_hrDiskStorageRemovable:642value->v.integer = entry->removable;643return (SNMP_ERR_NOERROR);644645case LEAF_hrDiskStorageCapacity:646value->v.integer = entry->capacity;647return (SNMP_ERR_NOERROR);648}649abort();650}651652653