Path: blob/main/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
108545 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: hrPartitionTable implementation for SNMPd.31*/3233#include <sys/types.h>34#include <sys/limits.h>3536#include <assert.h>37#include <err.h>38#include <inttypes.h>39#include <libgeom.h>40#include <paths.h>41#include <stdlib.h>42#include <string.h>43#include <syslog.h>44#include <sysexits.h>4546#include "hostres_snmp.h"47#include "hostres_oid.h"48#include "hostres_tree.h"4950#define HR_FREEBSD_PART_TYPE 1655152/* Maximum length for label and id including \0 */53#define PART_STR_MLEN (128 + 1)5455/*56* One row in the hrPartitionTable57*/58struct partition_entry {59asn_subid_t index[2];60u_char *label; /* max allocated len will be PART_STR_MLEN */61u_char *id; /* max allocated len will be PART_STR_MLEN */62int32_t size;63int32_t fs_Index;64TAILQ_ENTRY(partition_entry) link;65#define HR_PARTITION_FOUND 0x00166uint32_t flags;67};68TAILQ_HEAD(partition_tbl, partition_entry);6970/*71* This table is used to get a consistent indexing. It saves the name -> index72* mapping while we rebuild the partition table.73*/74struct partition_map_entry {75int32_t index; /* partition_entry::index */76u_char *id; /* max allocated len will be PART_STR_MLEN */7778/*79* next may be NULL if the respective partition_entry80* is (temporally) gone.81*/82struct partition_entry *entry;83STAILQ_ENTRY(partition_map_entry) link;84};85STAILQ_HEAD(partition_map, partition_map_entry);8687/* Mapping table for consistent indexing */88static struct partition_map partition_map =89STAILQ_HEAD_INITIALIZER(partition_map);9091/* THE partition table. */92static struct partition_tbl partition_tbl =93TAILQ_HEAD_INITIALIZER(partition_tbl);9495/* next int available for indexing the hrPartitionTable */96static uint32_t next_partition_index = 1;9798/*99* Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK100* macro.101*/102static int103partition_entry_cmp(const struct partition_entry *a,104const struct partition_entry *b)105{106assert(a != NULL);107assert(b != NULL);108109if (a->index[0] < b->index[0])110return (-1);111112if (a->index[0] > b->index[0])113return (+1);114115if (a->index[1] < b->index[1])116return (-1);117118if (a->index[1] > b->index[1])119return (+1);120121return (0);122}123124/*125* Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC126* macros127*/128static int129partition_idx_cmp(const struct asn_oid *oid, u_int sub,130const struct partition_entry *entry)131{132u_int i;133134for (i = 0; i < 2 && i < oid->len - sub; i++) {135if (oid->subs[sub + i] < entry->index[i])136return (-1);137if (oid->subs[sub + i] > entry->index[i])138return (+1);139}140if (oid->len - sub < 2)141return (-1);142if (oid->len - sub > 2)143return (+1);144145return (0);146}147148/**149* Create a new partition table entry150*/151static struct partition_entry *152partition_entry_create(int32_t ds_index, const char *chunk_name)153{154struct partition_entry *entry;155struct partition_map_entry *map;156size_t id_len;157158/* sanity checks */159assert(chunk_name != NULL);160if (chunk_name == NULL || chunk_name[0] == '\0')161return (NULL);162163/* check whether we already have seen this partition */164STAILQ_FOREACH(map, &partition_map, link)165if (strcmp(map->id, chunk_name) == 0)166break;167168if (map == NULL) {169/* new object - get a new index and create a map */170171if (next_partition_index > INT_MAX) {172/* Unrecoverable error - die clean and quicly*/173syslog(LOG_ERR, "%s: hrPartitionTable index wrap",174__func__);175errx(EX_SOFTWARE, "hrPartitionTable index wrap");176}177178if ((map = malloc(sizeof(*map))) == NULL) {179syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__);180return (NULL);181}182183id_len = strlen(chunk_name) + 1;184if (id_len > PART_STR_MLEN)185id_len = PART_STR_MLEN;186187if ((map->id = malloc(id_len)) == NULL) {188free(map);189return (NULL);190}191192map->index = next_partition_index++;193194strlcpy(map->id, chunk_name, id_len);195196map->entry = NULL;197198STAILQ_INSERT_TAIL(&partition_map, map, link);199200HRDBG("%s added into hrPartitionMap at index=%d",201chunk_name, map->index);202203} else {204HRDBG("%s exists in hrPartitionMap index=%d",205chunk_name, map->index);206}207208if ((entry = malloc(sizeof(*entry))) == NULL) {209syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__);210return (NULL);211}212memset(entry, 0, sizeof(*entry));213214/* create the index */215entry->index[0] = ds_index;216entry->index[1] = map->index;217218map->entry = entry;219220if ((entry->id = strdup(map->id)) == NULL) {221free(entry);222return (NULL);223}224225/*226* reuse id_len from here till the end of this function227* for partition_entry::label228*/229id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1;230231if (id_len > PART_STR_MLEN)232id_len = PART_STR_MLEN;233234if ((entry->label = malloc(id_len )) == NULL) {235free(entry->id);236free(entry);237return (NULL);238}239240snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name);241242INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link,243partition_entry_cmp);244245return (entry);246}247248/**249* Delete a partition table entry but keep the map entry intact.250*/251static void252partition_entry_delete(struct partition_entry *entry)253{254struct partition_map_entry *map;255256assert(entry != NULL);257258TAILQ_REMOVE(&partition_tbl, entry, link);259STAILQ_FOREACH(map, &partition_map, link)260if (map->entry == entry) {261map->entry = NULL;262break;263}264free(entry->id);265free(entry->label);266free(entry);267}268269/**270* Find a partition table entry by name. If none is found, return NULL.271*/272static struct partition_entry *273partition_entry_find_by_name(const char *name)274{275struct partition_entry *entry = NULL;276277TAILQ_FOREACH(entry, &partition_tbl, link)278if (strcmp(entry->id, name) == 0)279return (entry);280281return (NULL);282}283284/**285* Find a partition table entry by label. If none is found, return NULL.286*/287static struct partition_entry *288partition_entry_find_by_label(const char *name)289{290struct partition_entry *entry = NULL;291292TAILQ_FOREACH(entry, &partition_tbl, link)293if (strcmp(entry->label, name) == 0)294return (entry);295296return (NULL);297}298299/**300* Process a chunk from libgeom(4). A chunk is either a slice or a partition.301* If necessary create a new partition table entry for it. In any case302* set the size field of the entry and set the FOUND flag.303*/304static void305handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size)306{307struct partition_entry *entry;308daddr_t k_size;309310assert(chunk_name != NULL);311assert(chunk_name[0] != '\0');312if (chunk_name == NULL || chunk_name[0] == '\0')313return;314315HRDBG("ANALYZE chunk %s", chunk_name);316317if ((entry = partition_entry_find_by_name(chunk_name)) == NULL)318if ((entry = partition_entry_create(ds_index,319chunk_name)) == NULL)320return;321322entry->flags |= HR_PARTITION_FOUND;323324/* actual size may overflow the SNMP type */325k_size = chunk_size / 1024;326entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size);327}328329/**330* Start refreshing the partition table. A call to this function will331* be followed by a call to handleDiskStorage() for every disk, followed332* by a single call to the post_refresh function.333*/334void335partition_tbl_pre_refresh(void)336{337struct partition_entry *entry;338339/* mark each entry as missing */340TAILQ_FOREACH(entry, &partition_tbl, link)341entry->flags &= ~HR_PARTITION_FOUND;342}343344/**345* Try to find a geom(4) class by its name. Returns a pointer to that346* class if found NULL otherways.347*/348static struct gclass *349find_class(struct gmesh *mesh, const char *name)350{351struct gclass *classp;352353LIST_FOREACH(classp, &mesh->lg_class, lg_class)354if (strcmp(classp->lg_name, name) == 0)355return (classp);356return (NULL);357}358359/**360* Process all MBR-type partitions from the given disk.361*/362static void363get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)364{365struct ggeom *gp;366struct gprovider *pp;367struct gconfig *conf;368long part_type;369370LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {371/* We are only interested in partitions from this disk */372if (strcmp(gp->lg_name, disk_dev_name) != 0)373continue;374375/*376* Find all the non-BSD providers (these are handled in get_bsd)377*/378LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {379LIST_FOREACH(conf, &pp->lg_config, lg_config) {380if (conf->lg_name == NULL ||381conf->lg_val == NULL ||382strcmp(conf->lg_name, "type") != 0)383continue;384385/*386* We are not interested in BSD partitions387* (ie ad0s1 is not interesting at this point).388* We'll take care of them in detail (slice389* by slice) in get_bsd.390*/391part_type = strtol(conf->lg_val, NULL, 10);392if (part_type == HR_FREEBSD_PART_TYPE)393break;394HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name);395HRDBG("Mediasize: %jd",396(intmax_t)pp->lg_mediasize / 1024);397HRDBG("Sectorsize: %u", pp->lg_sectorsize);398HRDBG("Mode: %s", pp->lg_mode);399HRDBG("CONFIG: %s: %s",400conf->lg_name, conf->lg_val);401402handle_chunk(ds_index, pp->lg_name,403pp->lg_mediasize);404}405}406}407}408409/**410* Process all BSD-type partitions from the given disk.411*/412static void413get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)414{415struct ggeom *gp;416struct gprovider *pp;417418LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {419/*420* We are only interested in those geoms starting with421* the disk_dev_name passed as parameter to this function.422*/423if (strncmp(gp->lg_name, disk_dev_name,424strlen(disk_dev_name)) != 0)425continue;426427LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {428if (pp->lg_name == NULL)429continue;430handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize);431}432}433}434435/**436* Called from the DiskStorage table for every row. Open the GEOM(4) framework437* and process all the partitions in it.438* ds_index is the index into the DiskStorage table.439* This is done in two steps: for non BSD partitions the geom class "MBR" is440* used, for our BSD slices the "BSD" geom class.441*/442void443partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name)444{445struct gmesh mesh; /* GEOM userland tree */446struct gclass *classp;447int error;448449assert(disk_dev_name != NULL);450assert(ds_index > 0);451452HRDBG("===> getting partitions for %s <===", disk_dev_name);453454/* try to construct the GEOM tree */455if ((error = geom_gettree(&mesh)) != 0) {456syslog(LOG_WARNING, "cannot get GEOM tree: %m");457return;458}459460/*461* First try the GEOM "MBR" class.462* This is needed for non-BSD slices (aka partitions)463* on PC architectures.464*/465if ((classp = find_class(&mesh, "MBR")) != NULL) {466get_mbr(classp, ds_index, disk_dev_name);467} else {468HRDBG("cannot find \"MBR\" geom class");469}470471/*472* Get the "BSD" GEOM class.473* Here we'll find all the info needed about the BSD slices.474*/475if ((classp = find_class(&mesh, "BSD")) != NULL) {476get_bsd_sun(classp, ds_index, disk_dev_name);477} else {478/* no problem on sparc64 */479HRDBG("cannot find \"BSD\" geom class");480}481482/*483* Get the "SUN" GEOM class.484* Here we'll find all the info needed about the SUN slices.485*/486if ((classp = find_class(&mesh, "SUN")) != NULL) {487get_bsd_sun(classp, ds_index, disk_dev_name);488} else {489/* no problem on i386 */490HRDBG("cannot find \"SUN\" geom class");491}492493geom_deletetree(&mesh);494}495496/**497* Finish refreshing the table.498*/499void500partition_tbl_post_refresh(void)501{502struct partition_entry *e, *etmp;503504/*505* Purge items that disappeared506*/507TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp)508if (!(e->flags & HR_PARTITION_FOUND))509partition_entry_delete(e);510}511512/*513* Finalization routine for hrPartitionTable514* It destroys the lists and frees any allocated heap memory515*/516void517fini_partition_tbl(void)518{519struct partition_map_entry *m;520521while ((m = STAILQ_FIRST(&partition_map)) != NULL) {522STAILQ_REMOVE_HEAD(&partition_map, link);523if(m->entry != NULL) {524TAILQ_REMOVE(&partition_tbl, m->entry, link);525free(m->entry->id);526free(m->entry->label);527free(m->entry);528}529free(m->id);530free(m);531}532assert(TAILQ_EMPTY(&partition_tbl));533}534535/**536* Called from the file system code to insert the file system table index537* into the partition table entry. Note, that an partition table entry exists538* only for local file systems.539*/540void541handle_partition_fs_index(const char *name, int32_t fs_idx)542{543struct partition_entry *entry;544545if ((entry = partition_entry_find_by_label(name)) == NULL) {546HRDBG("%s IS MISSING from hrPartitionTable", name);547return;548}549HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx);550entry->fs_Index = fs_idx;551}552553/*554* This is the implementation for a generated (by our SNMP tool)555* function prototype, see hostres_tree.h556* It handles the SNMP operations for hrPartitionTable557*/558int559op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value,560u_int sub, u_int iidx __unused, enum snmp_op op)561{562struct partition_entry *entry;563564/*565* Refresh the disk storage table (which refreshes the partition566* table) if necessary.567*/568refresh_disk_storage_tbl(0);569570switch (op) {571572case SNMP_OP_GETNEXT:573if ((entry = NEXT_OBJECT_FUNC(&partition_tbl,574&value->var, sub, partition_idx_cmp)) == NULL)575return (SNMP_ERR_NOSUCHNAME);576577value->var.len = sub + 2;578value->var.subs[sub] = entry->index[0];579value->var.subs[sub + 1] = entry->index[1];580581goto get;582583case SNMP_OP_GET:584if ((entry = FIND_OBJECT_FUNC(&partition_tbl,585&value->var, sub, partition_idx_cmp)) == NULL)586return (SNMP_ERR_NOSUCHNAME);587goto get;588589case SNMP_OP_SET:590if ((entry = FIND_OBJECT_FUNC(&partition_tbl,591&value->var, sub, partition_idx_cmp)) == NULL)592return (SNMP_ERR_NOT_WRITEABLE);593return (SNMP_ERR_NO_CREATION);594595case SNMP_OP_ROLLBACK:596case SNMP_OP_COMMIT:597abort();598}599abort();600601get:602switch (value->var.subs[sub - 1]) {603604case LEAF_hrPartitionIndex:605value->v.integer = entry->index[1];606return (SNMP_ERR_NOERROR);607608case LEAF_hrPartitionLabel:609return (string_get(value, entry->label, -1));610611case LEAF_hrPartitionID:612return(string_get(value, entry->id, -1));613614case LEAF_hrPartitionSize:615value->v.integer = entry->size;616return (SNMP_ERR_NOERROR);617618case LEAF_hrPartitionFSIndex:619value->v.integer = entry->fs_Index;620return (SNMP_ERR_NOERROR);621}622abort();623}624625626