summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c')
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c630
1 files changed, 630 insertions, 0 deletions
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
new file mode 100644
index 0000000..65c0012
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
@@ -0,0 +1,630 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Host Resources MIB: hrPartitionTable implementation for SNMPd.
+ */
+
+#include <sys/types.h>
+#include <sys/limits.h>
+
+#include <assert.h>
+#include <err.h>
+#include <inttypes.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#ifdef PC98
+#define HR_FREEBSD_PART_TYPE 0xc494
+#else
+#define HR_FREEBSD_PART_TYPE 165
+#endif
+
+/* Maximum length for label and id including \0 */
+#define PART_STR_MLEN (128 + 1)
+
+/*
+ * One row in the hrPartitionTable
+ */
+struct partition_entry {
+ asn_subid_t index[2];
+ u_char *label; /* max allocated len will be PART_STR_MLEN */
+ u_char *id; /* max allocated len will be PART_STR_MLEN */
+ int32_t size;
+ int32_t fs_Index;
+ TAILQ_ENTRY(partition_entry) link;
+#define HR_PARTITION_FOUND 0x001
+ uint32_t flags;
+};
+TAILQ_HEAD(partition_tbl, partition_entry);
+
+/*
+ * This table is used to get a consistent indexing. It saves the name -> index
+ * mapping while we rebuild the partition table.
+ */
+struct partition_map_entry {
+ int32_t index; /* partition_entry::index */
+ u_char *id; /* max allocated len will be PART_STR_MLEN */
+
+ /*
+ * next may be NULL if the respective partition_entry
+ * is (temporally) gone.
+ */
+ struct partition_entry *entry;
+ STAILQ_ENTRY(partition_map_entry) link;
+};
+STAILQ_HEAD(partition_map, partition_map_entry);
+
+/* Mapping table for consistent indexing */
+static struct partition_map partition_map =
+ STAILQ_HEAD_INITIALIZER(partition_map);
+
+/* THE partition table. */
+static struct partition_tbl partition_tbl =
+ TAILQ_HEAD_INITIALIZER(partition_tbl);
+
+/* next int available for indexing the hrPartitionTable */
+static uint32_t next_partition_index = 1;
+
+/*
+ * Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK
+ * macro.
+ */
+static int
+partition_entry_cmp(const struct partition_entry *a,
+ const struct partition_entry *b)
+{
+ assert(a != NULL);
+ assert(b != NULL);
+
+ if (a->index[0] < b->index[0])
+ return (-1);
+
+ if (a->index[0] > b->index[0])
+ return (+1);
+
+ if (a->index[1] < b->index[1])
+ return (-1);
+
+ if (a->index[1] > b->index[1])
+ return (+1);
+
+ return (0);
+}
+
+/*
+ * Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC
+ * macros
+ */
+static int
+partition_idx_cmp(const struct asn_oid *oid, u_int sub,
+ const struct partition_entry *entry)
+{
+ u_int i;
+
+ for (i = 0; i < 2 && i < oid->len - sub; i++) {
+ if (oid->subs[sub + i] < entry->index[i])
+ return (-1);
+ if (oid->subs[sub + i] > entry->index[i])
+ return (+1);
+ }
+ if (oid->len - sub < 2)
+ return (-1);
+ if (oid->len - sub > 2)
+ return (+1);
+
+ return (0);
+}
+
+/**
+ * Create a new partition table entry
+ */
+static struct partition_entry *
+partition_entry_create(int32_t ds_index, const char *chunk_name)
+{
+ struct partition_entry *entry;
+ struct partition_map_entry *map;
+ size_t id_len;
+
+ /* sanity checks */
+ assert(chunk_name != NULL);
+ if (chunk_name == NULL || chunk_name[0] == '\0')
+ return (NULL);
+
+ /* check whether we already have seen this partition */
+ STAILQ_FOREACH(map, &partition_map, link)
+ if (strcmp(map->id, chunk_name) == 0)
+ break;
+
+ if (map == NULL) {
+ /* new object - get a new index and create a map */
+
+ if (next_partition_index > INT_MAX) {
+ /* Unrecoverable error - die clean and quicly*/
+ syslog(LOG_ERR, "%s: hrPartitionTable index wrap",
+ __func__);
+ errx(EX_SOFTWARE, "hrPartitionTable index wrap");
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__);
+ return (NULL);
+ }
+
+ id_len = strlen(chunk_name) + 1;
+ if (id_len > PART_STR_MLEN)
+ id_len = PART_STR_MLEN;
+
+ if ((map->id = malloc(id_len)) == NULL) {
+ free(map);
+ return (NULL);
+ }
+
+ map->index = next_partition_index++;
+
+ strlcpy(map->id, chunk_name, id_len);
+
+ map->entry = NULL;
+
+ STAILQ_INSERT_TAIL(&partition_map, map, link);
+
+ HRDBG("%s added into hrPartitionMap at index=%d",
+ chunk_name, map->index);
+
+ } else {
+ HRDBG("%s exists in hrPartitionMap index=%d",
+ chunk_name, map->index);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ /* create the index */
+ entry->index[0] = ds_index;
+ entry->index[1] = map->index;
+
+ map->entry = entry;
+
+ if ((entry->id = strdup(map->id)) == NULL) {
+ free(entry);
+ return (NULL);
+ }
+
+ /*
+ * reuse id_len from here till the end of this function
+ * for partition_entry::label
+ */
+ id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1;
+
+ if (id_len > PART_STR_MLEN)
+ id_len = PART_STR_MLEN;
+
+ if ((entry->label = malloc(id_len )) == NULL) {
+ free(entry->id);
+ free(entry);
+ return (NULL);
+ }
+
+ snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name);
+
+ INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link,
+ partition_entry_cmp);
+
+ return (entry);
+}
+
+/**
+ * Delete a partition table entry but keep the map entry intact.
+ */
+static void
+partition_entry_delete(struct partition_entry *entry)
+{
+ struct partition_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&partition_tbl, entry, link);
+ STAILQ_FOREACH(map, &partition_map, link)
+ if (map->entry == entry) {
+ map->entry = NULL;
+ break;
+ }
+ free(entry->id);
+ free(entry->label);
+ free(entry);
+}
+
+/**
+ * Find a partition table entry by name. If none is found, return NULL.
+ */
+static struct partition_entry *
+partition_entry_find_by_name(const char *name)
+{
+ struct partition_entry *entry = NULL;
+
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ if (strcmp(entry->id, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Find a partition table entry by label. If none is found, return NULL.
+ */
+static struct partition_entry *
+partition_entry_find_by_label(const char *name)
+{
+ struct partition_entry *entry = NULL;
+
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ if (strcmp(entry->label, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Process a chunk from libgeom(4). A chunk is either a slice or a partition.
+ * If necessary create a new partition table entry for it. In any case
+ * set the size field of the entry and set the FOUND flag.
+ */
+static void
+handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size)
+{
+ struct partition_entry *entry;
+ daddr_t k_size;
+
+ assert(chunk_name != NULL);
+ assert(chunk_name[0] != '\0');
+ if (chunk_name == NULL || chunk_name == '\0')
+ return;
+
+ HRDBG("ANALYZE chunk %s", chunk_name);
+
+ if ((entry = partition_entry_find_by_name(chunk_name)) == NULL)
+ if ((entry = partition_entry_create(ds_index,
+ chunk_name)) == NULL)
+ return;
+
+ entry->flags |= HR_PARTITION_FOUND;
+
+ /* actual size may overflow the SNMP type */
+ k_size = chunk_size / 1024;
+ entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size);
+}
+
+/**
+ * Start refreshing the partition table. A call to this function will
+ * be followed by a call to handleDiskStorage() for every disk, followed
+ * by a single call to the post_refresh function.
+ */
+void
+partition_tbl_pre_refresh(void)
+{
+ struct partition_entry *entry;
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ entry->flags &= ~HR_PARTITION_FOUND;
+}
+
+/**
+ * Try to find a geom(4) class by its name. Returns a pointer to that
+ * class if found NULL otherways.
+ */
+static struct gclass *
+find_class(struct gmesh *mesh, const char *name)
+{
+ struct gclass *classp;
+
+ LIST_FOREACH(classp, &mesh->lg_class, lg_class)
+ if (strcmp(classp->lg_name, name) == 0)
+ return (classp);
+ return (NULL);
+}
+
+/**
+ * Process all MBR-type partitions from the given disk.
+ */
+static void
+get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
+{
+ struct ggeom *gp;
+ struct gprovider *pp;
+ struct gconfig *conf;
+ long part_type;
+
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ /* We are only interested in partitions from this disk */
+ if (strcmp(gp->lg_name, disk_dev_name) != 0)
+ continue;
+
+ /*
+ * Find all the non-BSD providers (these are handled in get_bsd)
+ */
+ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ LIST_FOREACH(conf, &pp->lg_config, lg_config) {
+ if (conf->lg_name == NULL ||
+ conf->lg_val == NULL ||
+ strcmp(conf->lg_name, "type") != 0)
+ continue;
+
+ /*
+ * We are not interested in BSD partitions
+ * (ie ad0s1 is not interesting at this point).
+ * We'll take care of them in detail (slice
+ * by slice) in get_bsd.
+ */
+ part_type = strtol(conf->lg_val, NULL, 10);
+ if (part_type == HR_FREEBSD_PART_TYPE)
+ break;
+ HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name);
+ HRDBG("Mediasize: %jd",
+ (intmax_t)pp->lg_mediasize / 1024);
+ HRDBG("Sectorsize: %u", pp->lg_sectorsize);
+ HRDBG("Mode: %s", pp->lg_mode);
+ HRDBG("CONFIG: %s: %s",
+ conf->lg_name, conf->lg_val);
+
+ handle_chunk(ds_index, pp->lg_name,
+ pp->lg_mediasize);
+ }
+ }
+ }
+}
+
+/**
+ * Process all BSD-type partitions from the given disk.
+ */
+static void
+get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
+{
+ struct ggeom *gp;
+ struct gprovider *pp;
+
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ /*
+ * We are only interested in those geoms starting with
+ * the disk_dev_name passed as parameter to this function.
+ */
+ if (strncmp(gp->lg_name, disk_dev_name,
+ strlen(disk_dev_name)) != 0)
+ continue;
+
+ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ if (pp->lg_name == NULL)
+ continue;
+ handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize);
+ }
+ }
+}
+
+/**
+ * Called from the DiskStorage table for every row. Open the GEOM(4) framework
+ * and process all the partitions in it.
+ * ds_index is the index into the DiskStorage table.
+ * This is done in two steps: for non BSD partitions the geom class "MBR" is
+ * used, for our BSD slices the "BSD" geom class.
+ */
+void
+partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name)
+{
+ struct gmesh mesh; /* GEOM userland tree */
+ struct gclass *classp;
+ int error;
+
+ assert(disk_dev_name != NULL);
+ assert(ds_index > 0);
+
+ HRDBG("===> getting partitions for %s <===", disk_dev_name);
+
+ /* try to construct the GEOM tree */
+ if ((error = geom_gettree(&mesh)) != 0) {
+ syslog(LOG_WARNING, "cannot get GEOM tree: %m");
+ return;
+ }
+
+ /*
+ * First try the GEOM "MBR" class.
+ * This is needed for non-BSD slices (aka partitions)
+ * on PC architectures.
+ */
+ if ((classp = find_class(&mesh, "MBR")) != NULL) {
+ get_mbr(classp, ds_index, disk_dev_name);
+ } else {
+ HRDBG("cannot find \"MBR\" geom class");
+ }
+
+ /*
+ * Get the "BSD" GEOM class.
+ * Here we'll find all the info needed about the BSD slices.
+ */
+ if ((classp = find_class(&mesh, "BSD")) != NULL) {
+ get_bsd_sun(classp, ds_index, disk_dev_name);
+ } else {
+ /* no problem on sparc64 */
+ HRDBG("cannot find \"BSD\" geom class");
+ }
+
+ /*
+ * Get the "SUN" GEOM class.
+ * Here we'll find all the info needed about the BSD slices.
+ */
+ if ((classp = find_class(&mesh, "SUN")) != NULL) {
+ get_bsd_sun(classp, ds_index, disk_dev_name);
+ } else {
+ /* no problem on i386 */
+ HRDBG("cannot find \"SUN\" geom class");
+ }
+
+ geom_deletetree(&mesh);
+}
+
+/**
+ * Finish refreshing the table.
+ */
+void
+partition_tbl_post_refresh(void)
+{
+ struct partition_entry *e, *etmp;
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp)
+ if (!(e->flags & HR_PARTITION_FOUND))
+ partition_entry_delete(e);
+}
+
+/*
+ * Finalization routine for hrPartitionTable
+ * It destroys the lists and frees any allocated heap memory
+ */
+void
+fini_partition_tbl(void)
+{
+ struct partition_map_entry *m;
+
+ while ((m = STAILQ_FIRST(&partition_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&partition_map, link);
+ if(m->entry != NULL) {
+ TAILQ_REMOVE(&partition_tbl, m->entry, link);
+ free(m->entry->id);
+ free(m->entry->label);
+ free(m->entry);
+ }
+ free(m->id);
+ free(m);
+ }
+ assert(TAILQ_EMPTY(&partition_tbl));
+}
+
+/**
+ * Called from the file system code to insert the file system table index
+ * into the partition table entry. Note, that an partition table entry exists
+ * only for local file systems.
+ */
+void
+handle_partition_fs_index(const char *name, int32_t fs_idx)
+{
+ struct partition_entry *entry;
+
+ if ((entry = partition_entry_find_by_label(name)) == NULL) {
+ HRDBG("%s IS MISSING from hrPartitionTable", name);
+ return;
+ }
+ HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx);
+ entry->fs_Index = fs_idx;
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrPartitionTable
+ */
+int
+op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct partition_entry *entry;
+
+ /*
+ * Refresh the disk storage table (which refreshes the partition
+ * table) if necessary.
+ */
+ refresh_disk_storage_tbl(0);
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ value->var.len = sub + 2;
+ value->var.subs[sub] = entry->index[0];
+ value->var.subs[sub + 1] = entry->index[1];
+
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (SNMP_ERR_NO_CREATION);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrPartitionIndex:
+ value->v.integer = entry->index[1];
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrPartitionLabel:
+ return (string_get(value, entry->label, -1));
+
+ case LEAF_hrPartitionID:
+ return(string_get(value, entry->id, -1));
+
+ case LEAF_hrPartitionSize:
+ value->v.integer = entry->size;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrPartitionFSIndex:
+ value->v.integer = entry->fs_Index;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
OpenPOWER on IntegriCloud