/*- * Copyright (c) 2005-2006 The FreeBSD Project * All rights reserved. * * Author: Victor Cruceru * * 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 for SNMPd. Implementation for the hrDiskStorageTable */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" enum hrDiskStrorageAccess { DS_READ_WRITE = 1, DS_READ_ONLY = 2 }; enum hrDiskStrorageMedia { DSM_OTHER = 1, DSM_UNKNOWN = 2, DSM_HARDDISK = 3, DSM_FLOPPYDISK = 4, DSM_OPTICALDISKROM= 5, DSM_OPTICALDISKWORM= 6, DSM_OPTICALDISKRW= 7, DSM_RAMDISK = 8 }; /* * This structure is used to hold a SNMP table entry for HOST-RESOURCES-MIB's * hrDiskStorageTable. Note that index is external being allocated and * maintained by the hrDeviceTable code. * * NOTE: according to MIB removable means removable media, not the * device itself (like a USB card reader) */ struct disk_entry { int32_t index; int32_t access; /* enum hrDiskStrorageAccess */ int32_t media; /* enum hrDiskStrorageMedia*/ int32_t removable; /* enum snmpTCTruthValue*/ int32_t capacity; TAILQ_ENTRY(disk_entry) link; /* * next items are not from the SNMP mib table, only to be used * internally */ #define HR_DISKSTORAGE_FOUND 0x001 #define HR_DISKSTORAGE_ATA 0x002 /* belongs to the ATA subsystem */ #define HR_DISKSTORAGE_MD 0x004 /* it is a MD (memory disk) */ uint32_t flags; uint64_t r_tick; u_char dev_name[32]; /* device name, i.e. "ad4" or "acd0" */ }; TAILQ_HEAD(disk_tbl, disk_entry); /* the head of the list with hrDiskStorageTable's entries */ static struct disk_tbl disk_tbl = TAILQ_HEAD_INITIALIZER(disk_tbl); /* last tick when hrFSTable was updated */ static uint64_t disk_storage_tick; /* minimum number of ticks between refreshs */ uint32_t disk_storage_tbl_refresh = HR_DISK_TBL_REFRESH * 100; /* fd for "/dev/mdctl"*/ static int md_fd = -1; /* buffer for sysctl("kern.disks") */ static char *disk_list; static size_t disk_list_len; /* some constants */ static const struct asn_oid OIDX_hrDeviceDiskStorage_c = OIDX_hrDeviceDiskStorage; /** * Load the MD driver if it isn't loaded already. */ static void mdmaybeload(void) { char name1[64], name2[64]; snprintf(name1, sizeof(name1), "g_%s", MD_NAME); snprintf(name2, sizeof(name2), "geom_%s", MD_NAME); if (modfind(name1) == -1) { /* Not present in kernel, try loading it. */ if (kldload(name2) == -1 || modfind(name1) == -1) { if (errno != EEXIST) { errx(EXIT_FAILURE, "%s module not available!", name2); } } } } /** * Create a new entry into the DiskStorageTable. */ static struct disk_entry * disk_entry_create(const struct device_entry *devEntry) { struct disk_entry *entry; assert(devEntry != NULL); if (devEntry == NULL) return NULL; if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "hrDiskStorageTable: %s: %m", __func__); return (NULL); } memset(entry, 0, sizeof(*entry)); entry->index = devEntry->index; INSERT_OBJECT_INT(entry, &disk_tbl); return (entry); } /** * Delete a disk table entry. */ static void disk_entry_delete(struct disk_entry *entry) { struct device_entry *devEntry; assert(entry != NULL); TAILQ_REMOVE(&disk_tbl, entry, link); devEntry = device_find_by_index(entry->index); free(entry); /* * Also delete the respective device entry - * this is needed for disk devices that are not * detected by libdevinfo */ if (devEntry != NULL && (devEntry->flags & HR_DEVICE_IMMUTABLE) == HR_DEVICE_IMMUTABLE) device_entry_delete(devEntry); } /** * Find a disk storage entry given its index. */ static struct disk_entry * disk_find_by_index(int32_t idx) { struct disk_entry *entry; TAILQ_FOREACH(entry, &disk_tbl, link) if (entry->index == idx) return (entry); return (NULL); } /** * Get the disk parameters */ static void disk_query_disk(struct disk_entry *entry) { char dev_path[128]; int fd; off_t mediasize; if (entry == NULL || entry->dev_name[0] == '\0') return; snprintf(dev_path, sizeof(dev_path), "%s%s", _PATH_DEV, entry->dev_name); entry->capacity = 0; HRDBG("OPENING device %s", dev_path); if ((fd = open(dev_path, O_RDONLY|O_NONBLOCK)) == -1) { HRDBG("OPEN device %s failed: %s", dev_path, strerror(errno)); return; } if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) { HRDBG("DIOCGMEDIASIZE for device %s failed: %s", dev_path, strerror(errno)); (void)close(fd); return; } mediasize = mediasize / 1024; entry->capacity = (mediasize > INT_MAX ? INT_MAX : mediasize); partition_tbl_handle_disk(entry->index, entry->dev_name); (void)close(fd); } /** * Find all ATA disks in the device table. */ static void disk_OS_get_ATA_disks(void) { struct device_map_entry *map; struct device_entry *entry; struct disk_entry *disk_entry; const struct disk_entry *found; /* Things we know are ata disks */ static const struct disk_entry lookup[] = { { .dev_name = "ad", .media = DSM_HARDDISK, .removable = SNMP_FALSE }, { .dev_name = "ar", .media = DSM_OTHER, .removable = SNMP_FALSE }, { .dev_name = "acd", .media = DSM_OPTICALDISKROM, .removable = SNMP_TRUE }, { .dev_name = "afd", .media = DSM_FLOPPYDISK, .removable = SNMP_TRUE }, { .dev_name = "ast", .media = DSM_OTHER, .removable = SNMP_TRUE }, { .media = DSM_UNKNOWN } }; /* Walk over the device table looking for ata disks */ STAILQ_FOREACH(map, &device_map, link) { for (found = lookup; found->media != DSM_UNKNOWN; found++) { if (strncmp(map->name_key, found->dev_name, strlen(found->dev_name)) != 0) continue; /* * Avoid false disk devices. For example adw(4) and * adv(4) - they are not disks! */ if (strlen(map->name_key) > strlen(found->dev_name) && !isdigit(map->name_key[strlen(found->dev_name)])) continue; /* First get the entry from the hrDeviceTbl */ entry = map->entry_p; entry->type = &OIDX_hrDeviceDiskStorage_c; /* Then check hrDiskStorage table for this device */ disk_entry = disk_find_by_index(entry->index); if (disk_entry == NULL) { disk_entry = disk_entry_create(entry); if (disk_entry == NULL) continue; disk_entry->access = DS_READ_WRITE; strlcpy(disk_entry->dev_name, entry->name, sizeof(disk_entry->dev_name)); disk_entry->media = found->media; disk_entry->removable = found->removable; } disk_entry->flags |= HR_DISKSTORAGE_FOUND; disk_entry->flags |= HR_DISKSTORAGE_ATA; disk_query_disk(disk_entry); disk_entry->r_tick = this_tick; } } } /** * Find MD disks in the device table. */ static void disk_OS_get_MD_disks(void) { struct device_map_entry *map; struct device_entry *entry; struct disk_entry *disk_entry; struct md_ioctl mdio; int unit; if (md_fd <= 0) return; /* Look for md devices */ STAILQ_FOREACH(map, &device_map, link) { if (sscanf(map->name_key, "md%d", &unit) != 1) continue; /* First get the entry from the hrDeviceTbl */ entry = device_find_by_index(map->hrIndex); entry->type = &OIDX_hrDeviceDiskStorage_c; /* Then check hrDiskStorage table for this device */ disk_entry = disk_find_by_index(entry->index); if (disk_entry == NULL) { disk_entry = disk_entry_create(entry); if (disk_entry == NULL) continue; memset(&mdio, 0, sizeof(mdio)); mdio.md_version = MDIOVERSION; mdio.md_unit = unit; if (ioctl(md_fd, MDIOCQUERY, &mdio) < 0) { syslog(LOG_ERR, "hrDiskStorageTable: Couldnt ioctl"); continue; } if ((mdio.md_options & MD_READONLY) == MD_READONLY) disk_entry->access = DS_READ_ONLY; else disk_entry->access = DS_READ_WRITE; strlcpy(disk_entry->dev_name, entry->name, sizeof(disk_entry->dev_name)); disk_entry->media = DSM_RAMDISK; disk_entry->removable = SNMP_FALSE; } disk_entry->flags |= HR_DISKSTORAGE_FOUND; disk_entry->flags |= HR_DISKSTORAGE_MD; disk_entry->r_tick = this_tick; } } /** * Find rest of disks */ static void disk_OS_get_disks(void) { size_t disk_cnt = 0; struct device_entry *entry; struct disk_entry *disk_entry; size_t need = 0; if (sysctlbyname("kern.disks", NULL, &need, NULL, 0) == -1) { syslog(LOG_ERR, "%s: sysctl_1 kern.disks failed: %m", __func__); return; } if (need == 0) return; if (disk_list_len != need + 1 || disk_list == NULL) { disk_list_len = need + 1; disk_list = reallocf(disk_list, disk_list_len); } if (disk_list == NULL) { syslog(LOG_ERR, "%s: reallocf failed", __func__); disk_list_len = 0; return; } memset(disk_list, 0, disk_list_len); if (sysctlbyname("kern.disks", disk_list, &need, NULL, 0) == -1 || disk_list[0] == 0) { syslog(LOG_ERR, "%s: sysctl_2 kern.disks failed: %m", __func__); return; } for (disk_cnt = 0; disk_cnt < need; disk_cnt++) { char *disk = NULL; char disk_device[128] = ""; disk = strsep(&disk_list, " "); if (disk == NULL) break; snprintf(disk_device, sizeof(disk_device), "%s%s", _PATH_DEV, disk); /* First check if the disk is in the hrDeviceTable. */ if ((entry = device_find_by_name(disk)) == NULL) { /* * not found there - insert it as immutable */ syslog(LOG_WARNING, "%s: device '%s' not in " "device list", __func__, disk); if ((entry = device_entry_create(disk, "", "")) == NULL) continue; entry->flags |= HR_DEVICE_IMMUTABLE; } entry->type = &OIDX_hrDeviceDiskStorage_c; /* Then check hrDiskStorage table for this device */ disk_entry = disk_find_by_index(entry->index); if (disk_entry == NULL) { disk_entry = disk_entry_create(entry); if (disk_entry == NULL) continue; } disk_entry->flags |= HR_DISKSTORAGE_FOUND; if ((disk_entry->flags & HR_DISKSTORAGE_ATA) || (disk_entry->flags & HR_DISKSTORAGE_MD)) { /* * ATA/MD detection is running before this one, * so don't waste the time here */ continue; } disk_entry->access = DS_READ_WRITE; disk_entry->media = DSM_UNKNOWN; disk_entry->removable = SNMP_FALSE; if (strncmp(disk_entry->dev_name, "da", 2) == 0) { disk_entry->media = DSM_HARDDISK; disk_entry->removable = SNMP_FALSE; } else if (strncmp(disk_entry->dev_name, "cd", 2) == 0) { disk_entry->media = DSM_OPTICALDISKROM; disk_entry->removable = SNMP_TRUE; } else { disk_entry->media = DSM_UNKNOWN; disk_entry->removable = SNMP_FALSE; } strlcpy((char *)disk_entry->dev_name, disk, sizeof(disk_entry->dev_name)); disk_query_disk(disk_entry); disk_entry->r_tick = this_tick; } } /** * Refresh routine for hrDiskStorageTable * Usable for polling the system for any changes. */ void refresh_disk_storage_tbl(int force) { struct disk_entry *entry, *entry_tmp; if (disk_storage_tick != 0 && !force && this_tick - disk_storage_tick < disk_storage_tbl_refresh) { HRDBG("no refresh needed"); return; } partition_tbl_pre_refresh(); /* mark each entry as missing */ TAILQ_FOREACH(entry, &disk_tbl, link) entry->flags &= ~HR_DISKSTORAGE_FOUND; disk_OS_get_ATA_disks(); /* this must be called first ! */ disk_OS_get_MD_disks(); disk_OS_get_disks(); /* * Purge items that disappeared */ TAILQ_FOREACH_SAFE(entry, &disk_tbl, link, entry_tmp) if (!(entry->flags & HR_DISKSTORAGE_FOUND)) /* XXX remove IMMUTABLE entries that have disappeared */ disk_entry_delete(entry); disk_storage_tick = this_tick; partition_tbl_post_refresh(); HRDBG("refresh DONE"); } /* * Init the things for both of hrDiskStorageTable */ int init_disk_storage_tbl(void) { char mddev[32] = ""; /* Try to load md.ko if not loaded already */ mdmaybeload(); md_fd = -1; snprintf(mddev, sizeof(mddev) - 1, "%s%s", _PATH_DEV, MDCTL_NAME); if ((md_fd = open(mddev, O_RDWR)) == -1) { syslog(LOG_ERR, "open %s failed - will not include md(4) " "info: %m", mddev); } refresh_disk_storage_tbl(1); return (0); } /* * Finalization routine for hrDiskStorageTable * It destroys the lists and frees any allocated heap memory */ void fini_disk_storage_tbl(void) { struct disk_entry *n1; while ((n1 = TAILQ_FIRST(&disk_tbl)) != NULL) { TAILQ_REMOVE(&disk_tbl, n1, link); free(n1); } free(disk_list); if (md_fd > 0) { if (close(md_fd) == -1) syslog(LOG_ERR,"close (/dev/mdctl) failed: %m"); md_fd = -1; } } /* * This is the implementation for a generated (by our SNMP "compiler" tool) * function prototype, see hostres_tree.h * It handles the SNMP operations for hrDiskStorageTable */ int op_hrDiskStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op curr_op) { struct disk_entry *entry; refresh_disk_storage_tbl(0); switch (curr_op) { case SNMP_OP_GETNEXT: if ((entry = NEXT_OBJECT_INT(&disk_tbl, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 1; value->var.subs[sub] = entry->index; goto get; case SNMP_OP_GET: if ((entry = FIND_OBJECT_INT(&disk_tbl, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((entry = FIND_OBJECT_INT(&disk_tbl, &value->var, sub)) == NULL) return (SNMP_ERR_NO_CREATION); return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } abort(); get: switch (value->var.subs[sub - 1]) { case LEAF_hrDiskStorageAccess: value->v.integer = entry->access; return (SNMP_ERR_NOERROR); case LEAF_hrDiskStorageMedia: value->v.integer = entry->media; return (SNMP_ERR_NOERROR); case LEAF_hrDiskStorageRemoveble: value->v.integer = entry->removable; return (SNMP_ERR_NOERROR); case LEAF_hrDiskStorageCapacity: value->v.integer = entry->capacity; return (SNMP_ERR_NOERROR); } abort(); }