diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt new file mode 100644 index 000000000000..ee8d284af95f --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt @@ -0,0 +1,125 @@ +-- +-- Copyright (c) 2005-2006 +-- Hartmut Brandt +-- All rights reserved. +-- +-- Author: Harti Brandt +-- +-- Redistribution 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 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 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 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$ +-- +-- Additional stuff for the HOST-RESOURCES MIB. +-- +BEGEMOT-HOSTRES-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, TimeTicks + FROM SNMPv2-SMI + begemot + FROM BEGEMOT-MIB; + +begemotHostres MODULE-IDENTITY + LAST-UPDATED "200601030000Z" + ORGANIZATION "German Aerospace Center" + CONTACT-INFO + " Hartmut Brandt + + Postal: German Aerospace Center + Oberpfaffenhofen + 82234 Wessling + Germany + + Fax: +49 8153 28 2843 + + E-mail: harti@freebsd.org" + DESCRIPTION + "The MIB for additional HOST-RESOURCES data." + ::= { begemot 202 } + +begemotHostresObjects OBJECT IDENTIFIER ::= { begemotHostres 1 } + +begemotHrStorageUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the storage table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 1 } + +begemotHrFSUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the FS table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 2 } + +begemotHrDiskStorageUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the disk storage table is cached." + DEFVAL { 300 } + ::= { begemotHostresObjects 3 } + +begemotHrNetworkUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the network table is cached." + DEFVAL { 700 } + ::= { begemotHostresObjects 4 } + +begemotHrSWInstalledUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the hrSWInstalledTable is cached." + DEFVAL { 1200 } + ::= { begemotHostresObjects 5 } + +begemotHrSWRunUpdate OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of ticks the hrSWRunTable and + hrSWRunPerfTable are cached." + DEFVAL { 300 } + ::= { begemotHostresObjects 6 } + +begemotHrPkgDir OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The path to the package DB directory." + DEFVAL { "/var/db/pkg" } + ::= { begemotHostresObjects 7 } + +END diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile new file mode 100644 index 000000000000..475ad994cb46 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile @@ -0,0 +1,82 @@ +# +# 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$ +# + +LPRSRC= ${.CURDIR}/../../../lpr/common_source +.PATH: ${LPRSRC} + +MOD= hostres +SRCS= hostres_begemot.c \ + hostres_device_tbl.c \ + hostres_diskstorage_tbl.c \ + hostres_fs_tbl.c \ + hostres_network_tbl.c \ + hostres_partition_tbl.c \ + hostres_printer_tbl.c \ + hostres_processor_tbl.c \ + hostres_scalars.c \ + hostres_snmp.c \ + hostres_storage_tbl.c \ + hostres_swinstalled_tbl.c \ + hostres_swrun_tbl.c \ + printcap.c + +#Not having NDEBUG defined will enable assertions and a lot of output on stderr +CFLAGS+= -DNDEBUG -I${LPRSRC} +XSYM= host hrStorageOther hrStorageRam hrStorageVirtualMemory \ + hrStorageFixedDisk hrStorageRemovableDisk hrStorageFloppyDisk \ + hrStorageCompactDisc hrStorageRamDisk hrStorageFlashMemory \ + hrStorageNetworkDisk hrDeviceOther hrDeviceUnknown \ + hrDeviceProcessor hrDeviceNetwork hrDevicePrinter \ + hrDeviceDiskStorage hrDeviceVideo hrDeviceAudio \ + hrDeviceCoprocessor hrDeviceKeyboard hrDeviceModem \ + hrDeviceParallelPort hrDevicePointing \ + hrDeviceSerialPort hrDeviceTape hrDeviceClock \ + hrDeviceVolatileMemory hrDeviceNonVolatileMemory \ + hrFSOther hrFSUnknown hrFSBerkeleyFFS hrFSSys5FS hrFSFat\ + hrFSHPFS hrFSHFS hrFSMFS hrFSNTFS hrFSVNode hrFSJournaled \ + hrFSiso9660 hrFSRockRidge hrFSNFS hrFSNetware hrFSAFS hrFSDFS \ + hrFSAppleshare hrFSRFS hrFSDGCFS hrFSBFS hrFSFAT32 hrFSLinuxExt2 + +MAN= snmp_hostres.3 + +DEFS= ${MOD}_tree.def +BMIBS= BEGEMOT-HOSTRES-MIB.txt + +DPADD= ${LIBKVM} ${LIBDEVINFO} ${LIBM} ${LIBDISK} ${LIBMEMSTAT} +LDADD= -lkvm -ldevinfo -lm -ldisk -lmemstat + +.include + +printcap.So: printcap.c + ${CC} ${PICFLAG} -DPIC ${CFLAGS:C/^-W.*//} -c ${.IMPSRC} -o ${.TARGET} + +smilint: + env SMIPATH=.:/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \ + smilint -c /dev/null -l6 -i group-membership BEGEMOT-HOSTRES-MIB diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c new file mode 100644 index 000000000000..f1cc5e3e33d6 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c @@ -0,0 +1,171 @@ +/*- + * Copyright (c) 2005-2006. + * Hartmut Brandt. + * All rights reserved. + * + * Author: Hartmut Brandt + * + * 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$ + */ + +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +int +op_begemot(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op op) +{ + + switch (op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + value->v.uint32 = storage_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + value->v.uint32 = fs_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + value->v.uint32 = disk_storage_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + value->v.uint32 = network_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + value->v.uint32 = swins_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + value->v.uint32 = swrun_tbl_refresh; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + return (string_get(value, pkg_dir, -1)); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + ctx->scratch->int1 = storage_tbl_refresh; + storage_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + ctx->scratch->int1 = fs_tbl_refresh; + fs_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + ctx->scratch->int1 = disk_storage_tbl_refresh; + disk_storage_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + ctx->scratch->int1 = network_tbl_refresh; + network_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + ctx->scratch->int1 = swins_tbl_refresh; + swins_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + ctx->scratch->int1 = swrun_tbl_refresh; + swrun_tbl_refresh = value->v.uint32; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + return (string_save(value, ctx, -1, &pkg_dir)); + } + abort(); + + case SNMP_OP_COMMIT: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + case LEAF_begemotHrFSUpdate: + case LEAF_begemotHrDiskStorageUpdate: + case LEAF_begemotHrNetworkUpdate: + case LEAF_begemotHrSWInstalledUpdate: + case LEAF_begemotHrSWRunUpdate: + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + string_commit(ctx); + return (SNMP_ERR_NOERROR); + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (value->var.subs[sub - 1]) { + + case LEAF_begemotHrStorageUpdate: + storage_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrFSUpdate: + fs_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrDiskStorageUpdate: + disk_storage_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrNetworkUpdate: + network_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWInstalledUpdate: + swins_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrSWRunUpdate: + swrun_tbl_refresh = ctx->scratch->int1; + return (SNMP_ERR_NOERROR); + + case LEAF_begemotHrPkgDir: + string_rollback(ctx, &pkg_dir); + return (SNMP_ERR_NOERROR); + } + abort(); + } + + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c new file mode 100644 index 000000000000..eb1ad9170b69 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c @@ -0,0 +1,612 @@ + /*- + * 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: hrDeviceTable implementation for SNMPd. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * Status of a device + */ +enum DeviceStatus { + DS_UNKNOWN = 1, + DS_RUNNING = 2, + DS_WARNING = 3, + DS_TESTING = 4, + DS_DOWN = 5 +}; + +TAILQ_HEAD(device_tbl, device_entry); + +/* the head of the list with hrDeviceTable's entries */ +static struct device_tbl device_tbl = TAILQ_HEAD_INITIALIZER(device_tbl); + +/* Table used for consistent device table indexing. */ +struct device_map device_map = STAILQ_HEAD_INITIALIZER(device_map); + +/* next int available for indexing the hrDeviceTable */ +static uint32_t next_device_index = 1; + +/* last (agent) tick when hrDeviceTable was updated */ +static uint64_t device_tick = 0; + +/* maximum number of ticks between updates of device table */ +uint32_t device_tbl_refresh = 10 * 100; + +/* socket for /var/run/devd.pipe */ +static int devd_sock = -1; + +/* used to wait notifications from /var/run/devd.pipe */ +static void *devd_fd; + +/* some constants */ +static const struct asn_oid OIDX_hrDeviceProcessor_c = OIDX_hrDeviceProcessor; +static const struct asn_oid OIDX_hrDeviceOther_c = OIDX_hrDeviceOther; + +/** + * Create a new entry out of thin air. + */ +struct device_entry * +device_entry_create(const char *name, const char *location, const char *descr) +{ + struct device_entry *entry; + struct device_map_entry *map; + + assert((name[0] != 0) || (location[0] != 0)); + + if (name[0] == 0 && location[0] == 0) + return (NULL); + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrDeviceTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, name) == 0 && + strcmp(map->location_key, location) == 0) { + entry->index = map->hrIndex; + map->entry_p = entry; + break; + } + + if (map == NULL) { + /* new object - get a new index */ + if (next_device_index > INT_MAX) { + syslog(LOG_ERR, + "%s: hrDeviceTable index wrap", __func__); + free(entry); + return (NULL); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); + free(entry); + return (NULL); + } + + map->hrIndex = next_device_index++; + + strlcpy(map->name_key, name, sizeof(map->name_key)); + strlcpy(map->location_key, location, sizeof(map->location_key)); + + map->entry_p = entry; + + STAILQ_INSERT_TAIL(&device_map, map, link); + HRDBG("%s at %s added into hrDeviceMap at index=%d", + name, location, map->hrIndex); + } else { + HRDBG("%s at %s exists in hrDeviceMap index=%d", + name, location, map->hrIndex); + } + + entry->index = map->hrIndex; + + strlcpy(entry->name, name, sizeof(entry->name)); + strlcpy(entry->location, location, sizeof(entry->location)); + + if (name[0] != '\0') + snprintf(entry->descr, sizeof(entry->descr), "%s: %s", + name, descr); + else + snprintf(entry->descr, sizeof(entry->descr), + "unknown at %s: %s", location, descr); + + entry->id = oid_zeroDotZero; /* unknown id - FIXME */ + entry->status = (u_int)DIS_ATTACHED; + entry->errors = 0; + entry->type = OIDX_hrDeviceOther_c; + + INSERT_OBJECT_INT(entry, &device_tbl); + + return (entry); +} + +/** + * Create a new entry into the device table. + */ +static struct device_entry * +device_entry_create_devinfo(const struct devinfo_dev *dev_p) +{ + + assert(dev_p->dd_name != NULL); + assert(dev_p->dd_location != NULL); + + return (device_entry_create(dev_p->dd_name, dev_p->dd_location, + dev_p->dd_desc)); +} + +/** + * Delete an entry from the device table. + */ +static void +device_entry_delete(struct device_entry *entry) +{ + struct device_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&device_tbl, entry, link); + + STAILQ_FOREACH(map, &device_map, link) + if (map->entry_p == entry) { + map->entry_p = NULL; + break; + } + free(entry); +} + +/** + * Find an entry given its name and location + */ +static struct device_entry * +device_find_by_dev(const struct devinfo_dev *dev_p) +{ + struct device_map_entry *map; + + assert(dev_p != NULL); + + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, dev_p->dd_name) == 0 && + strcmp(map->location_key, dev_p->dd_location) == 0) + return (map->entry_p); + return (NULL); +} + +/** + * Find an entry given its index. + */ +struct device_entry * +device_find_by_index(int32_t idx) +{ + struct device_entry *entry; + + TAILQ_FOREACH(entry, &device_tbl, link) + if (entry->index == idx) + return (entry); + return (NULL); +} + +/** + * Find an device entry given its name. + */ +struct device_entry * +device_find_by_name(const char *dev_name) +{ + struct device_map_entry *map; + + assert(dev_name != NULL); + + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, dev_name) == 0) + return (map->entry_p); + + return (NULL); +} + +/** + * Find out the type of device. CPU only currently. + */ +static void +device_get_type(struct devinfo_dev *dev_p, struct asn_oid *out_type_p) +{ + + assert(dev_p != NULL); + assert(out_type_p != NULL); + + if (dev_p == NULL) + return; + + if (strncmp(dev_p->dd_name, "cpu", strlen("cpu")) == 0 && + strstr(dev_p->dd_location, ".CPU") != NULL) { + *out_type_p = OIDX_hrDeviceProcessor_c; + return; + } +} + +/** + * Get the status of a device + */ +static enum DeviceStatus +device_get_status(struct devinfo_dev *dev) +{ + + assert(dev != NULL); + + switch (dev->dd_state) { + case DIS_ALIVE: /* probe succeeded */ + case DIS_NOTPRESENT: /* not probed or probe failed */ + return (DS_DOWN); + case DIS_ATTACHED: /* attach method called */ + case DIS_BUSY: /* device is open */ + return (DS_RUNNING); + default: + return (DS_UNKNOWN); + } +} + +/** + * Get the info for the given device and then recursively process all + * child devices. + */ +static int +device_collector(struct devinfo_dev *dev, void *arg) +{ + struct device_entry *entry; + + HRDBG("%llu/%llu name='%s' desc='%s' drivername='%s' location='%s'", + (unsigned long long)dev->dd_handle, + (unsigned long long)dev->dd_parent, dev->dd_name, dev->dd_desc, + dev->dd_drivername, dev->dd_location); + + if (dev->dd_name[0] != '\0' || dev->dd_location[0] != '\0') { + HRDBG("ANALYZING dev %s at %s", + dev->dd_name, dev->dd_location); + + if ((entry = device_find_by_dev(dev)) != NULL) { + entry->flags |= HR_DEVICE_FOUND; + entry->status = (u_int)device_get_status(dev); + } else if ((entry = device_entry_create_devinfo(dev)) != NULL) { + device_get_type(dev, &entry->type); + + entry->flags |= HR_DEVICE_FOUND; + entry->status = (u_int)device_get_status(dev); + } + } else { + HRDBG("SKIPPED unknown device at location '%s'", + dev->dd_location ); + } + + return (devinfo_foreach_device_child(dev, device_collector, arg)); +} + +/** + * Create the socket to the device daemon. + */ +static int +create_devd_socket(void) +{ + int d_sock; + struct sockaddr_un devd_addr; + + bzero(&devd_addr, sizeof(struct sockaddr_un)); + + if ((d_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "Failed to create the socket for %s: %m", + PATH_DEVD_PIPE); + return (-1); + } + + devd_addr.sun_family = PF_LOCAL; + devd_addr.sun_len = sizeof(devd_addr); + strlcpy(devd_addr.sun_path, PATH_DEVD_PIPE, + sizeof(devd_addr.sun_path) - 1); + + if (connect(d_sock, (struct sockaddr *)&devd_addr, + sizeof(devd_addr)) == -1) { + syslog(LOG_ERR,"Failed to connect socket for %s: %m", + PATH_DEVD_PIPE); + if (close(d_sock) < 0 ) + syslog(LOG_ERR,"Failed to close socket for %s: %m", + PATH_DEVD_PIPE); + return (-1); + } + + return (d_sock); +} + +/* + * Event on the devd socket. + ** + * We should probably directly process entries here. For simplicity just + * call the refresh routine with the force flag for now. + */ +static void +devd_socket_callback(int fd, void *arg __unused) +{ + char buf[512]; + int read_len = -1; + + assert(fd == devd_sock); + + HRDBG("called"); + + read_len = read(fd, buf, sizeof(buf) - 1); + if (read_len < 0) { + if (errno == EBADF) { + devd_sock = -1; + if (devd_fd != NULL) { + fd_deselect(devd_fd); + devd_fd = NULL; + } + syslog(LOG_ERR, "Closing devd_fd, revert to " + "devinfo polling"); + } + + } else if (read_len == 0) { + syslog(LOG_ERR, "zero bytes read from devd pipe... " + "closing socket!"); + + if (close(devd_sock) < 0 ) + syslog(LOG_ERR, "Failed to close devd socket: %m"); + + devd_sock = -1; + if (devd_fd != NULL) { + fd_deselect(devd_fd); + devd_fd = NULL; + } + syslog(LOG_ERR, "Closing devd_fd, revert to devinfo polling"); + + } else { + switch (buf[0]) { + case '+': + case '-': + case '?': + refresh_device_tbl(1); + return; + default: + syslog(LOG_ERR, "unknown message from devd socket"); + } + } +} + +/** + * Initialize and populate the device table. + */ +void +init_device_tbl(void) +{ + + /* initially populate table for the other tables */ + refresh_device_tbl(1); + + /* no problem if that fails - just use polling mode */ + devd_sock = create_devd_socket(); +} + +/** + * Start devd(8) monitoring. + */ +void +start_device_tbl(struct lmodule *mod) +{ + + if (devd_sock > 0) { + devd_fd = fd_select(devd_sock, devd_socket_callback, NULL, mod); + if (devd_fd == NULL) + syslog(LOG_ERR, "fd_select failed on devd socket: %m"); + } +} + +/** + * Finalization routine for hrDeviceTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_device_tbl(void) +{ + struct device_map_entry *n1; + + if (devd_fd != NULL) + fd_deselect(devd_fd); + + if (devd_sock != -1) + (void)close(devd_sock); + + devinfo_free(); + + while ((n1 = STAILQ_FIRST(&device_map)) != NULL) { + STAILQ_REMOVE_HEAD(&device_map, link); + if (n1->entry_p != NULL) { + TAILQ_REMOVE(&device_tbl, n1->entry_p, link); + free(n1->entry_p); + } + free(n1); + } + assert(TAILQ_EMPTY(&device_tbl)); +} + +/** + * Refresh routine for hrDeviceTable. We don't refresh here if the devd socket + * is open, because in this case we have the actual information always. We + * also don't refresh when the table is new enough (if we don't have a devd + * socket). In either case a refresh can be forced by passing a non-zero value. + */ +void +refresh_device_tbl(int force) +{ + struct device_entry *entry, *entry_tmp; + struct devinfo_dev *dev_root; + static int act = 0; + + if (!force && (devd_sock >= 0 || + (device_tick != 0 && this_tick - device_tick < device_tbl_refresh))){ + HRDBG("no refresh needed"); + return; + } + + if (act) { + syslog(LOG_ERR, "%s: recursive call", __func__); + return; + } + + if (devinfo_init() != 0) { + syslog(LOG_ERR,"%s: devinfo_init failed: %m", __func__); + return; + } + + act = 1; + if ((dev_root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL){ + syslog(LOG_ERR, "%s: can't get the root device: %m", __func__); + goto out; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &device_tbl, link) + entry->flags &= ~HR_DEVICE_FOUND; + + if (devinfo_foreach_device_child(dev_root, device_collector, NULL)) + syslog(LOG_ERR, "%s: devinfo_foreach_device_child failed", + __func__); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &device_tbl, link, entry_tmp) { + /* + * If HR_DEVICE_IMMUTABLE bit is set then this means that + * this entry was not detected by the above + * devinfo_foreach_device() call. So we are not deleting + * it there. + */ + if (!(entry->flags & HR_DEVICE_FOUND) && + !(entry->flags & HR_DEVICE_IMMUTABLE)) + device_entry_delete(entry); + } + + device_tick = this_tick; + + /* + * Force a refresh for the hrDiskStorageTable + * XXX Why not the other dependen tables? + */ + refresh_disk_storage_tbl(1); + + out: + devinfo_free(); + act = 0; +} + +/** + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrDeviceTable + */ +int +op_hrDeviceTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct device_entry *entry; + + refresh_device_tbl(0); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&device_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(&device_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&device_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_hrDeviceIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceType: + value->v.oid = entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceDescr: + return (string_get(value, entry->descr, -1)); + + case LEAF_hrDeviceID: + value->v.oid = entry->id; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceStatus: + value->v.integer = entry->status; + return (SNMP_ERR_NOERROR); + + case LEAF_hrDeviceErrors: + value->v.uint32 = entry->errors; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c new file mode 100644 index 000000000000..e3d33a0763d6 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c @@ -0,0 +1,626 @@ +/*- + * 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 "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) +{ + + assert(entry != NULL); + + TAILQ_REMOVE(&disk_tbl, entry, link); + free(entry); +} + +/** + * 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); + + if (entry->media == DSM_HARDDISK) { + /* XXX libdisk crashes if a empty cdrom device is opened */ + 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; + + /* 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; + + /* 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 + * XXX somehow we should remove it if it disappears + */ + 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: %m", mddev); + return (-1); + } + + 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(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c new file mode 100644 index 000000000000..51e718181615 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c @@ -0,0 +1,440 @@ +/*- + * 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 hrFSTable + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * File system access enum + */ +enum hrFSAccess { + FS_READ_WRITE = 1, + FS_READ_ONLY = 2 +}; + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrFSTable + */ +struct fs_entry { + int32_t index; + u_char mountPoint[128 + 1]; + u_char remoteMountPoint[128 + 1]; + const struct asn_oid *type; + int32_t access; /* enum hrFSAccess, see above */ + int32_t bootable; /* TruthValue */ + int32_t storageIndex; /* hrStorageTblEntry::index */ + u_char lastFullBackupDate[11]; + u_char lastPartialBackupDate[11]; +#define HR_FS_FOUND 0x001 + uint32_t flags; /* not in mib table, for internal use */ + TAILQ_ENTRY(fs_entry) link; +}; +TAILQ_HEAD(fs_tbl, fs_entry); + +/* + * Next structure is used to keep o list of mappings from a specific name + * (a_name) to an entry in the hrFSTblEntry. We are trying to keep the same + * index for a specific name at least for the duration of one SNMP agent run. + */ +struct fs_map_entry { + int32_t hrIndex; /* used for hrFSTblEntry::index */ + u_char a_name[128 + 1];/* map key */ + + /* may be NULL if the respective hrFSTblEntry is (temporally) gone */ + struct fs_entry *entry; + STAILQ_ENTRY(fs_map_entry) link; +}; +STAILQ_HEAD(fs_map, fs_map_entry); + +/* head of the list with hrFSTable's entries */ +static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl); + +/* for consistent table indexing */ +static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map); + +/* next index available for hrFSTable */ +static uint32_t next_fs_index = 1; + +/* last tick when hrFSTable was updated */ +static uint64_t fs_tick; + +/* maximum number of ticks between refreshs */ +uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100; + +/* some constants */ +static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS; +static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660; +static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS; +static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2; +static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther; +static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32; +static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS; +static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware; +static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS; +static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown; + +/* file system type map */ +static const struct { + const char *str; /* the type string */ + const struct asn_oid *oid; /* the OID to return */ +} fs_type_map[] = { + { "ufs", &OIDX_hrFSBerkeleyFFS_c }, + { "cd9660", &OIDX_hrFSiso9660_c }, + { "nfs", &OIDX_hrFSNFS_c }, + { "ext2fs", &OIDX_hrFSLinuxExt2_c }, + { "procfs", &OIDX_hrFSOther_c }, + { "devfs", &OIDX_hrFSOther_c }, + { "msdosfs", &OIDX_hrFSFAT32_c }, + { "ntfs", &OIDX_hrFSNTFS_c }, + { "nwfs", &OIDX_hrFSNetware_c }, + { "hpfs", &OIDX_hrFSHPFS_c }, + { "smbfs", &OIDX_hrFSOther_c }, +}; +#define N_FS_TYPE_MAP (sizeof(fs_type_map) / sizeof(fs_type_map[0])) + +/** + * Create an entry into the FS table and an entry in the map (if needed). + */ +static struct fs_entry * +fs_entry_create(const char *name) +{ + struct fs_entry *entry; + struct fs_map_entry *map; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + + strlcpy(entry->mountPoint, name, sizeof(entry->mountPoint)); + + STAILQ_FOREACH(map, &fs_map, link) + if (strncmp(map->a_name, entry->mountPoint, + sizeof(map->a_name) - 1) == 0) { + entry->index = map->hrIndex; + map->entry = entry; + break; + } + + if (map == NULL) { + /* new object - get a new index */ + if (next_fs_index > INT_MAX) { + /* XXX no other sensible reaction? */ + syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__); + return (NULL); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + free(entry); + return (NULL); + } + + map->hrIndex = next_fs_index++; + strlcpy(map->a_name, entry->mountPoint, sizeof(map->a_name)); + map->entry = entry; + + STAILQ_INSERT_TAIL(&fs_map, map, link); + + HRDBG("%s added into hrFSMap at index=%d", name, map->hrIndex); + } else { + HRDBG("%s exists in hrFSMap index=%d", name, map->hrIndex); + } + + entry->index = map->hrIndex; + + INSERT_OBJECT_INT(entry, &fs_tbl); + + return (entry); +} + +/** + * Delete an entry in the FS table. + */ +static void +fs_entry_delete(struct fs_entry* entry) +{ + struct fs_map_entry *map; + + TAILQ_REMOVE(&fs_tbl, entry, link); + STAILQ_FOREACH(map, &fs_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + + free(entry); +} + +/** + * Find a table entry by its name + */ +static struct fs_entry * +fs_find_by_name(const char *name) +{ + struct fs_entry *entry; + + TAILQ_FOREACH(entry, &fs_tbl, link) + if (strncmp(entry->mountPoint, name, + sizeof(entry->mountPoint) - 1) == 0) + return (entry); + + return (NULL); +} + +/** + * Get rid of all data + */ +void +fini_fs_tbl(void) +{ + struct fs_map_entry *n1; + + while ((n1 = STAILQ_FIRST(&fs_map)) != NULL) { + STAILQ_REMOVE_HEAD(&fs_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&fs_tbl, n1->entry, link); + free(n1->entry); + } + free(n1); + } + assert(TAILQ_EMPTY(&fs_tbl)); +} + +/** + * Called before the refreshing is started from the storage table. + */ +void +fs_tbl_pre_refresh(void) +{ + struct fs_entry *entry; + + /* mark each entry as missisng */ + TAILQ_FOREACH(entry, &fs_tbl, link) + entry->flags &= ~HR_FS_FOUND; +} + +/** + * Called after refreshing from the storage table. + */ +void +fs_tbl_post_refresh(void) +{ + struct fs_entry *entry, *entry_tmp; + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &fs_tbl, link, entry_tmp) + if (!(entry->flags & HR_FS_FOUND)) + fs_entry_delete(entry); + + fs_tick = this_tick; +} + +/* + * Refresh the FS table. This is done by forcing a refresh of the storage table. + */ +void +refresh_fs_tbl(void) +{ + + if (fs_tick == 0 || this_tick - fs_tick >= fs_tbl_refresh) { + refresh_storage_tbl(1); + HRDBG("refresh DONE"); + } +} + +/** + * Get the type OID for a given file system + */ +const struct asn_oid * +fs_get_type(const struct statfs *fs_p) +{ + u_int t; + + assert(fs_p != NULL); + + for (t = 0; t < N_FS_TYPE_MAP; t++) + if (strcmp(fs_type_map[t].str, fs_p->f_fstypename) == 0) + return (fs_type_map[t].oid); + + return (&OIDX_hrFSUnknown_c); +} + +/* + * Given information returned from statfs(2) either create a new entry into + * the fs_tbl or refresh the entry if it is already there. + */ +void +fs_tbl_process_statfs_entry(const struct statfs *fs_p, int32_t storage_idx) +{ + struct fs_entry *entry; + + assert(fs_p != 0); + + HRDBG("for hrStorageEntry::index %d", storage_idx); + + if (fs_p == NULL) + return; + + if ((entry = fs_find_by_name(fs_p->f_mntonname)) != NULL || + (entry = fs_entry_create(fs_p->f_mntonname)) != NULL) { + entry->flags |= HR_FS_FOUND; + + strcpy(entry->mountPoint, fs_p->f_mntonname); + + if (!(fs_p->f_flags & MNT_LOCAL)) + /* this is a remote mount */ + strcpy(entry->remoteMountPoint, fs_p->f_mntfromname); + else + entry->remoteMountPoint[0] = '\0'; + + entry->type = fs_get_type(fs_p); + + if ((fs_p->f_flags & MNT_RDONLY) == MNT_RDONLY) + entry->access = FS_READ_ONLY; + else + entry->access = FS_READ_WRITE; + + /* FIXME - bootable fs ?! */ + entry->bootable = TRUTH_MK((fs_p->f_flags & MNT_ROOTFS) + == MNT_ROOTFS); + + entry->storageIndex = storage_idx; + + /* Info not available */ + memset(entry->lastFullBackupDate, 0, + sizeof(entry->lastFullBackupDate)); + + /* Info not available */ + memset(entry->lastPartialBackupDate, 0, + sizeof(entry->lastPartialBackupDate)); + + handle_partition_fs_index(fs_p->f_mntfromname, entry->index); + } +} + +/* + * This is the implementation for a generated (by our SNMP "compiler" tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrFSTable + */ +int +op_hrFSTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct fs_entry *entry; + + refresh_fs_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&fs_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(&fs_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&fs_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_hrFSIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSMountPoint: + return (string_get(value, entry->mountPoint, -1)); + + case LEAF_hrFSRemoteMountPoint: + return (string_get(value, entry->remoteMountPoint, -1)); + break; + + case LEAF_hrFSType: + value->v.oid = *entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSAccess: + value->v.integer = entry->access; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSBootable: + value->v.integer = entry->bootable; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSStorageIndex: + value->v.integer = entry->storageIndex; + return (SNMP_ERR_NOERROR); + + case LEAF_hrFSLastFullBackupDate: + return (string_get(value, entry->lastFullBackupDate, 8)); + + case LEAF_hrFSLastPartialBackupDate: + return (string_get(value, entry->lastPartialBackupDate, 8)); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c new file mode 100644 index 000000000000..a98241cd62eb --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c @@ -0,0 +1,302 @@ +/*- + * 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 implementation for SNMPd: instrumentation for + * hrNetworkTable + */ + +#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" + +#include + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrNetworkTable + */ +struct network_entry { + int32_t index; + int32_t ifIndex; + TAILQ_ENTRY(network_entry) link; +#define HR_NETWORK_FOUND 0x001 + uint32_t flags; + +}; +TAILQ_HEAD(network_tbl, network_entry); + +/* the head of the list with hrNetworkTable's entries */ +static struct network_tbl network_tbl = TAILQ_HEAD_INITIALIZER(network_tbl); + +/* last (agent) tick when hrNetworkTable was updated */ +static uint64_t network_tick; + +/* maximum number of ticks between updates of network table */ +uint32_t network_tbl_refresh = HR_NETWORK_TBL_REFRESH * 100; + +/* Constants */ +static const struct asn_oid OIDX_hrDeviceNetwork_c = OIDX_hrDeviceNetwork; + +/** + * Create a new entry into the network table + */ +static struct network_entry * +network_entry_create(const struct device_entry *devEntry) +{ + struct network_entry *entry; + + assert(devEntry != NULL); + if (devEntry == NULL) + return (NULL); + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + + memset(entry, 0, sizeof(*entry)); + entry->index = devEntry->index; + INSERT_OBJECT_INT(entry, &network_tbl); + + return (entry); +} + +/** + * Delete an entry in the network table + */ +static void +network_entry_delete(struct network_entry* entry) +{ + + TAILQ_REMOVE(&network_tbl, entry, link); + free(entry); +} + +/** + * Fetch the interfaces from the mibII module, get their real name from the + * kernel and try to find it in the device table. + */ +static void +network_get_interfaces(void) +{ + struct device_entry *dev; + struct network_entry *net; + struct mibif *ifp; + int name[6]; + size_t len; + char *dname; + + name[0] = CTL_NET; + name[1] = PF_LINK; + name[2] = NETLINK_GENERIC; + name[3] = IFMIB_IFDATA; + name[5] = IFDATA_DRIVERNAME; + + for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) { + HRDBG("%s %s", ifp->name, ifp->descr); + + name[4] = ifp->sysindex; + + /* get the original name */ + len = 0; + if (sysctl(name, 6, NULL, &len, 0, 0) < 0) { + syslog(LOG_ERR, "sysctl(net.link.ifdata.%d." + "drivername): %m", ifp->sysindex); + continue; + } + if ((dname = malloc(len)) == NULL) { + syslog(LOG_ERR, "malloc: %m"); + continue; + } + if (sysctl(name, 6, dname, &len, 0, 0) < 0) { + syslog(LOG_ERR, "sysctl(net.link.ifdata.%d." + "drivername): %m", ifp->sysindex); + free(dname); + continue; + } + + HRDBG("got device %s (%s)", ifp->name, dname); + + if ((dev = device_find_by_name(dname)) == NULL) { + HRDBG("%s not in hrDeviceTable", dname); + free(dname); + continue; + } + HRDBG("%s found in hrDeviceTable", dname); + + dev->type = OIDX_hrDeviceNetwork_c; + dev->flags |= HR_DEVICE_IMMUTABLE; + + free(dname); + + /* Then check hrNetworkTable for this device */ + TAILQ_FOREACH(net, &network_tbl, link) + if (net->index == dev->index) + break; + + if (net == NULL && (net = network_entry_create(dev)) == NULL) + continue; + + net->flags |= HR_NETWORK_FOUND; + net->ifIndex = ifp->index; + } + + network_tick = this_tick; +} + +/** + * Finalization routine for hrNetworkTable. + * It destroys the lists and frees any allocated heap memory. + */ +void +fini_network_tbl(void) +{ + struct network_entry *n1; + + while ((n1 = TAILQ_FIRST(&network_tbl)) != NULL) { + TAILQ_REMOVE(&network_tbl, n1, link); + free(n1); + } +} + +/** + * Get the interface list from mibII only at this point to be sure that + * it is there already. + */ +void +start_network_tbl(void) +{ + + mib_refresh_iflist(); + network_get_interfaces(); +} + +/** + * Refresh the table. + */ +static void +refresh_network_tbl(void) +{ + struct network_entry *entry, *entry_tmp; + + if (this_tick - network_tick < network_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &network_tbl, link) + entry->flags &= ~HR_NETWORK_FOUND; + + network_get_interfaces(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &network_tbl, link, entry_tmp) { + if (!(entry->flags & HR_NETWORK_FOUND)) + network_entry_delete(entry); + } + + HRDBG("refresh DONE"); +} + +/* + * This is the implementation for a generated (by our SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrNetworkTable + */ +int +op_hrNetworkTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct network_entry *entry; + + refresh_network_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&network_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(&network_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&network_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_hrNetworkIfIndex: + value->v.integer = entry->ifIndex; + return (SNMP_ERR_NOERROR); + + } + abort(); +} 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 000000000000..d6e55c01dd51 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c @@ -0,0 +1,415 @@ +/*- + * 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: hrPartitionTable implementation for SNMPd. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * One row in the hrPartitionTable + */ +struct partition_entry { + struct asn_oid index; + u_char label[128 + 1]; + u_char id[128 + 1]; + 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; /* hrPartitionTblEntry::index */ + u_char id[128 + 1]; + + /* + * 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; + +/** + * Create a new partition table entry + */ +static struct partition_entry * +partition_entry_create(int32_t ds_index, const struct chunk *chunk) +{ + struct partition_entry *entry; + struct partition_map_entry *map = NULL; + + /* sanity checks */ + assert(chunk != NULL); + assert(chunk->name != NULL); + if (chunk == NULL || chunk->name == NULL || chunk->name[0] == '\0') + return (NULL); + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + /* check whether we already have seen this partition */ + STAILQ_FOREACH(map, &partition_map, link) + if (strcmp(map->id, chunk->name) == 0 ) { + map->entry = entry; + break; + } + + if (map == NULL) { + /* new object - get a new index and create a map */ + if (next_partition_index > INT_MAX) { + syslog(LOG_ERR, "%s: hrPartitionTable index wrap", + __func__); + errx(1, "hrPartitionTable index wrap"); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__); + free(entry); + return (NULL); + } + + map->index = next_partition_index++; + + memset(map->id, 0, sizeof(map->id)); + strncpy(map->id, chunk->name, sizeof(map->id) - 1); + + map->entry = entry; + 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); + } + + /* create the index */ + entry->index.len = 2; + entry->index.subs[0] = ds_index; + entry->index.subs[1] = map->index; + + memset(&entry->id[0], 0, sizeof(entry->id)); + strncpy(entry->id, chunk->name, sizeof(entry->id) - 1); + + snprintf(entry->label, sizeof(entry->label) - 1, + "%s%s", _PATH_DEV, chunk->name); + + INSERT_OBJECT_OID(entry, &partition_tbl); + + 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); +} + +/** + * 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 libdisk. 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 struct chunk* chunk, + const struct disk *disk) +{ + struct partition_entry *entry = NULL; + daddr_t k_size; + + assert(chunk != NULL); + if (chunk == NULL) + return; + + if (chunk->type == unused) { + HRDBG("SKIP unused chunk %s", chunk->name); + 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)) == NULL) + return; + + entry->flags |= HR_PARTITION_FOUND; + + /* actual size may overflow the SNMP type */ + k_size = chunk->size / (1024 / disk->sector_size); + entry->size = (k_size > (daddr_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 = NULL; + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &partition_tbl, link) + entry->flags &= ~HR_PARTITION_FOUND; +} + +/** + * Called from the DiskStorage table for every row. Open the device and + * process all the partitions in it. ds_index is the index into the DiskStorage + * table. + */ +void +partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name) +{ + struct disk *disk; + struct chunk *chunk; + struct chunk *partt; + + assert(disk_dev_name != NULL); + assert(ds_index > 0); + + if ((disk = Open_Disk(disk_dev_name)) == NULL) { + syslog(LOG_ERR, "%s: cannot Open_Disk()", disk_dev_name); + return; + } + + for (chunk = disk->chunks->part; chunk != NULL; chunk = chunk->next) { + handle_chunk(ds_index, chunk, disk); + for (partt = chunk->part; partt != NULL; partt = partt->next) + handle_chunk(ds_index, partt, disk); + } + Free_Disk(disk); +} + +/** + * 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); + } + 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_OID(&partition_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + + index_append(&value->var, sub, &entry->index); + goto get; + + case SNMP_OP_GET: + if ((entry = FIND_OBJECT_OID(&partition_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_OID(&partition_tbl, + &value->var, sub)) == 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.subs[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(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c new file mode 100644 index 000000000000..c29b29b3a6cd --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c @@ -0,0 +1,398 @@ +/*- + * 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 implementation for SNMPd: instrumentation for + * hrPrinterTable + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +#include +#include "lp.h" + +/* Constants */ +static const struct asn_oid OIDX_hrDevicePrinter_c = OIDX_hrDevicePrinter; + +enum PrinterStatus { + PS_OTHER = 1, + PS_UNKNOWN = 2, + PS_IDLE = 3, + PS_PRINTING = 4, + PS_WARMUP = 5 +}; + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrPrinterTable. + */ +struct printer_entry { + int32_t index; + int32_t status; /* values from PrinterStatus enum above */ + u_char detectedErrorState[2]; + TAILQ_ENTRY(printer_entry) link; +#define HR_PRINTER_FOUND 0x001 + uint32_t flags; + +}; +TAILQ_HEAD(printer_tbl, printer_entry); + +/* the hrPrinterTable */ +static struct printer_tbl printer_tbl = TAILQ_HEAD_INITIALIZER(printer_tbl); + +/* last (agent) tick when hrPrinterTable was updated */ +static uint64_t printer_tick; + +/** + * Create entry into the printer table. + */ +static struct printer_entry * +printer_entry_create(const struct device_entry *devEntry) +{ + struct printer_entry *entry = NULL; + + assert(devEntry != NULL); + if (devEntry == NULL) + return (NULL); + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "hrPrinterTable: %s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + entry->index = devEntry->index; + INSERT_OBJECT_INT(entry, &printer_tbl); + return (entry); +} + +/** + * Delete entry from the printer table. + */ +static void +printer_entry_delete(struct printer_entry *entry) +{ + + assert(entry != NULL); + if (entry == NULL) + return; + + TAILQ_REMOVE(&printer_tbl, entry, link); + free(entry); +} + +/** + * Find a printer by its index + */ +static struct printer_entry * +printer_find_by_index(int32_t idx) +{ + struct printer_entry *entry; + + TAILQ_FOREACH(entry, &printer_tbl, link) + if (entry->index == idx) + return (entry); + + return (NULL); +} + +/** + * Get the status of a printer + */ +static enum PrinterStatus +get_printer_status(const struct printer *pp) +{ + char statfile[MAXPATHLEN]; + char lockfile[MAXPATHLEN]; + char fline[128]; + int fd; + FILE *f = NULL; + enum PrinterStatus ps = PS_UNKNOWN; + + if (pp->lock_file[0] == '/') + strlcpy(lockfile, pp->lock_file, sizeof(lockfile)); + else + snprintf(lockfile, sizeof(lockfile), "%s/%s", + pp->spool_dir, pp->lock_file); + + fd = open(lockfile, O_RDONLY); + if (fd < 0 || flock(fd, LOCK_SH | LOCK_NB) == 0) { + ps = PS_IDLE; + goto LABEL_DONE; + } + + if (pp->status_file[0] == '/') + strlcpy(statfile, pp->status_file, sizeof(statfile)); + else + snprintf(statfile, sizeof(statfile), "%s/%s", + pp->spool_dir, pp->status_file); + + f = fopen(statfile, "r"); + if (f == NULL) { + syslog(LOG_ERR, "cannot open status file: %s", strerror(errno)); + ps = PS_UNKNOWN; + goto LABEL_DONE; + } + + memset(&fline[0], '\0', sizeof(line)); + if (fgets(fline, sizeof(fline) -1, f) == NULL) { + ps = PS_UNKNOWN; + goto LABEL_DONE; + } + + if (strstr(fline, "is ready and printing") != NULL) { + ps = PS_PRINTING; + goto LABEL_DONE; + } + + if (strstr(fline, "to become ready (offline?)") != NULL) { + ps = PS_OTHER; + goto LABEL_DONE; + } + +LABEL_DONE: + if (fd >= 0) + (void)close(fd); /* unlocks as well */ + + if (f != NULL) + (void)fclose(f); + + return (ps); +} + +/** + * Called for each printer found in /etc/printcap. + */ +static void +handle_printer(struct printer *pp) +{ + struct device_entry *dev_entry; + struct printer_entry *printer_entry; + char dev_only[128]; + struct stat sb; + + if (pp->remote_host != NULL) { + HRDBG("skipped %s -- remote", pp->printer); + return; + } + + if (strncmp(pp->lp, _PATH_DEV, strlen(_PATH_DEV)) != 0) { + HRDBG("skipped %s [device %s] -- remote", pp->printer, pp->lp); + return; + } + + memset(dev_only, '\0', sizeof(dev_only)); + snprintf(dev_only, sizeof(dev_only), "%s", pp->lp + strlen(_PATH_DEV)); + + HRDBG("printer %s has device %s", pp->printer, dev_only); + + if (stat(pp->lp, &sb) < 0) { + if (errno == ENOENT) { + HRDBG("skipped %s -- device %s missing", + pp->printer, pp->lp); + return; + } + } + + if ((dev_entry = device_find_by_name(dev_only)) == NULL) { + HRDBG("%s not in hrDeviceTable", pp->lp); + return; + } + HRDBG("%s found in hrDeviceTable", pp->lp); + + dev_entry->type = OIDX_hrDevicePrinter_c; + dev_entry->flags |= HR_DEVICE_IMMUTABLE; + + /* Then check hrPrinterTable for this device */ + if ((printer_entry = printer_find_by_index(dev_entry->index)) == NULL && + (printer_entry = printer_entry_create(dev_entry)) == NULL) + return; + + printer_entry->flags |= HR_PRINTER_FOUND; + printer_entry->status = get_printer_status(pp); + memset(printer_entry->detectedErrorState, 0, + sizeof(printer_entry->detectedErrorState)); +} + +static void +hrPrinter_get_OS_entries(void) +{ + int status, more; + struct printer myprinter, *pp = &myprinter; + + init_printer(pp); + HRDBG("---->Getting printers ....."); + more = firstprinter(pp, &status); + if (status) + goto errloop; + + while (more) { + do { + HRDBG("---->Got printer %s", pp->printer); + + handle_printer(pp); + more = nextprinter(pp, &status); +errloop: + if (status) + syslog(LOG_WARNING, + "hrPrinterTable: printcap entry for %s " + "has errors, skipping", + pp->printer ? pp->printer : ""); + } while (more && status); + } + + lastprinter(); + printer_tick = this_tick; +} + +/** + * Init the things for hrPrinterTable + */ +void +init_printer_tbl(void) +{ + + hrPrinter_get_OS_entries(); +} + +/** + * Finalization routine for hrPrinterTable + * It destroys the lists and frees any allocated heap memory + */ +void +fini_printer_tbl(void) +{ + struct printer_entry *n1; + + while ((n1 = TAILQ_FIRST(&printer_tbl)) != NULL) { + TAILQ_REMOVE(&printer_tbl, n1, link); + free(n1); + } +} + +/** + * Refresh the printer table if needed. + */ +void +refresh_printer_tbl(void) +{ + struct printer_entry *entry; + struct printer_entry *entry_tmp; + + if (this_tick <= printer_tick) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &printer_tbl, link) + entry->flags &= ~HR_PRINTER_FOUND; + + hrPrinter_get_OS_entries(); + + /* + * Purge items that disappeared + */ + entry = TAILQ_FIRST(&printer_tbl); + while (entry != NULL) { + entry_tmp = TAILQ_NEXT(entry, link); + if (!(entry->flags & HR_PRINTER_FOUND)) + printer_entry_delete(entry); + entry = entry_tmp; + } + + printer_tick = this_tick; + + HRDBG("refresh DONE "); +} + +int +op_hrPrinterTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct printer_entry *entry; + + refresh_printer_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&printer_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(&printer_tbl, &value->var, + sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&printer_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_hrPrinterStatus: + value->v.integer = entry->status; + return (SNMP_ERR_NOERROR); + + case LEAF_hrPrinterDetectedErrorState: + return (string_get(value, entry->detectedErrorState, + sizeof(entry->detectedErrorState))); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c new file mode 100644 index 000000000000..2df4d3e31030 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c @@ -0,0 +1,506 @@ +/*- + * 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 hrProcessorTable + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrProcessorTable. + * Note that index is external being allocated & maintained + * by the hrDeviceTable code.. + */ +struct processor_entry { + int32_t index; + struct asn_oid frwId; + int32_t load; + TAILQ_ENTRY(processor_entry) link; + u_char cpu_no; /* which cpu, counted from 0 */ + pid_t idle_pid; /* PID of idle process for this CPU */ + + /* the samples from the last minute, as required by MIB */ + double samples[MAX_CPU_SAMPLES]; + + /* current sample to fill in next time, must be < MAX_CPU_SAMPLES */ + uint32_t cur_sample_idx; + + /* number of useful samples */ + uint32_t sample_cnt; +}; +TAILQ_HEAD(processor_tbl, processor_entry); + +/* the head of the list with hrDeviceTable's entries */ +static struct processor_tbl processor_tbl = + TAILQ_HEAD_INITIALIZER(processor_tbl); + +/* number of processors in dev tbl */ +static int32_t detected_processor_count; + +/* sysctlbyname(hw.ncpu) */ +static int hw_ncpu; + +/* sysctlbyname(kern.{ccpu,fscale}) */ +static fixpt_t ccpu; +static int fscale; + +/* tick of PDU where we have refreshed the processor table last */ +static uint64_t proctbl_tick; + +/* periodic timer used to get cpu load stats */ +static void *cpus_load_timer; + +/* + * Average the samples. The entire algorithm seems to be wrong XXX. + */ +static int +get_avg_load(struct processor_entry *e) +{ + u_int i; + double sum = 0.0; + + assert(e != NULL); + + if (e->sample_cnt == 0) + return (0); + + for (i = 0; i < e->sample_cnt; i++) + sum += e->samples[i]; + + return ((int)floor((double)sum/(double)e->sample_cnt)); +} + +/* + * Stolen from /usr/src/bin/ps/print.c. The idle process should never + * be swapped out :-) + */ +static double +processor_getpcpu(struct kinfo_proc *ki_p) +{ + + if (ccpu == 0 || fscale == 0) + return (0.0); + +#define fxtofl(fixpt) ((double)(fixpt) / fscale) + return (100.0 * fxtofl(ki_p->ki_pctcpu) / + (1.0 - exp(ki_p->ki_swtime * log(fxtofl(ccpu))))); +} + +/** + * Save a new sample + */ +static void +save_sample(struct processor_entry *e, struct kinfo_proc *kp) +{ + + e->samples[e->cur_sample_idx] = 100.0 - processor_getpcpu(kp); + e->load = get_avg_load(e); + e->cur_sample_idx = (e->cur_sample_idx + 1) % MAX_CPU_SAMPLES; + + if (++e->sample_cnt > MAX_CPU_SAMPLES) + e->sample_cnt = MAX_CPU_SAMPLES; +} + +/** + * Create a new entry into the processor table. + */ +static struct processor_entry * +proc_create_entry(u_int cpu_no, struct device_map_entry *map) +{ + struct device_entry *dev; + struct processor_entry *entry; + char name[128]; + + /* + * If there is no map entry create one by creating a device table + * entry. + */ + if (map == NULL) { + snprintf(name, sizeof(name), "cpu%u", cpu_no); + if ((dev = device_entry_create(name, "", "")) == NULL) + return (NULL); + dev->flags |= HR_DEVICE_IMMUTABLE; + STAILQ_FOREACH(map, &device_map, link) + if (strcmp(map->name_key, name) == 0) + break; + if (map == NULL) + abort(); + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_ERR, "hrProcessorTable: %s malloc " + "failed: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + + entry->index = map->hrIndex; + entry->load = 0; + entry->cpu_no = (u_char)cpu_no; + entry->idle_pid = 0; + entry->frwId = oid_zeroDotZero; /* unknown id FIXME */ + + INSERT_OBJECT_INT(entry, &processor_tbl); + + HRDBG("CPU %d added with SNMP index=%d", + entry->cpu_no, entry->index); + + return (entry); +} + +/** + * Get the PIDs for the idle processes of the CPUs. + */ +static void +processor_get_pids(void) +{ + struct kinfo_proc *plist, *kp; + int i; + int nproc; + int cpu; + int nchars; + struct processor_entry *entry; + + plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc); + if (plist == NULL || nproc < 0) { + syslog(LOG_ERR, "hrProcessor: kvm_getprocs() failed: %m"); + return; + } + + for (i = 0, kp = plist; i < nproc; i++, kp++) { + if (!IS_KERNPROC(kp)) + continue; + + if (strcmp(kp->ki_ocomm, "idle") == 0) { + /* single processor system */ + cpu = 0; + } else if (sscanf(kp->ki_ocomm, "idle: cpu%d%n", &cpu, &nchars) + == 1 && (u_int)nchars == strlen(kp->ki_ocomm)) { + /* MP system */ + } else + /* not an idle process */ + continue; + + HRDBG("'%s' proc with pid %d is on CPU #%d (last on #%d)", + kp->ki_ocomm, kp->ki_pid, kp->ki_oncpu, kp->ki_lastcpu); + + TAILQ_FOREACH(entry, &processor_tbl, link) + if (entry->cpu_no == kp->ki_lastcpu) + break; + + if (entry == NULL) { + /* create entry on non-ACPI systems */ + if ((entry = proc_create_entry(cpu, NULL)) == NULL) + continue; + + detected_processor_count++; + } + + entry->idle_pid = kp->ki_pid; + HRDBG("CPU no. %d with SNMP index=%d has idle PID %d", + entry->cpu_no, entry->index, entry->idle_pid); + + save_sample(entry, plist); + } +} + +/** + * Scan the device map table for CPUs and create an entry into the + * processor table for each CPU. Then fetch the idle PIDs for all CPUs. + */ +static void +create_proc_table(void) +{ + struct device_map_entry *map; + struct processor_entry *entry; + int cpu_no; + + detected_processor_count = 0; + + /* + * Because hrProcessorTable depends on hrDeviceTable, + * the device detection must be performed at this point. + * If not, no entries will be present in the hrProcessor Table. + * + * For non-ACPI system the processors are not in the device table, + * therefor insert them when getting the idle pids. XXX + */ + STAILQ_FOREACH(map, &device_map, link) + if (strncmp(map->name_key, "cpu", strlen("cpu")) == 0 && + strstr(map->location_key, ".CPU") != NULL) { + if (sscanf(map->name_key,"cpu%d", &cpu_no) != 1) { + syslog(LOG_ERR, "hrProcessorTable: Failed to " + "get cpu no. from device named '%s'", + map->name_key); + continue; + } + + if ((entry = proc_create_entry(cpu_no, map)) == NULL) + continue; + + detected_processor_count++; + } + + HRDBG("%d CPUs detected", detected_processor_count); + + processor_get_pids(); +} + +/** + * Free the processor table + */ +static void +free_proc_table(void) +{ + struct processor_entry *n1; + + while ((n1 = TAILQ_FIRST(&processor_tbl)) != NULL) { + TAILQ_REMOVE(&processor_tbl, n1, link); + free(n1); + detected_processor_count--; + } + + assert(detected_processor_count == 0); + detected_processor_count = 0; +} + +/** + * Init the things for hrProcessorTable. + * Scan the device table for processor entries. + */ +void +init_processor_tbl(void) +{ + size_t len; + + /* get various parameters from the kernel */ + len = sizeof(ccpu); + if (sysctlbyname("kern.ccpu", &ccpu, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.ccpu) failed"); + ccpu = 0; + } + + len = sizeof(fscale); + if (sysctlbyname("kern.fscale", &fscale, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.fscale) failed"); + fscale = 0; + } + + /* create the initial processor table */ + create_proc_table(); +} + +/** + * Finalization routine for hrProcessorTable. + * It destroys the lists and frees any allocated heap memory. + */ +void +fini_processor_tbl(void) +{ + + if (cpus_load_timer != NULL) { + timer_stop(cpus_load_timer); + cpus_load_timer = NULL; + } + + free_proc_table(); +} + +/** + * Make sure that the number of processors announced by the kernel hw.ncpu + * is equal to the number of processors we have found in the device table. + * If they differ rescan the device table. + */ +static void +processor_refill_tbl(void) +{ + + HRDBG("hw_ncpu=%d detected_processor_count=%d", hw_ncpu, + detected_processor_count); + + if (hw_ncpu <= 0) { + size_t size = sizeof(hw_ncpu); + + if (sysctlbyname("hw.ncpu", &hw_ncpu, &size, NULL, 0) == -1 || + size != sizeof(hw_ncpu)) { + syslog(LOG_ERR, "hrProcessorTable: " + "sysctl(hw.ncpu) failed: %m"); + hw_ncpu = 0; + return; + } + } + + if (hw_ncpu != detected_processor_count) { + free_proc_table(); + create_proc_table(); + } +} + +/** + * Refresh all values in the processor table. We call this once for + * every PDU that accesses the table. + */ +static void +refresh_processor_tbl(void) +{ + struct processor_entry *entry; + int need_pids; + struct kinfo_proc *plist; + int nproc; + + processor_refill_tbl(); + + need_pids = 0; + TAILQ_FOREACH(entry, &processor_tbl, link) { + if (entry->idle_pid <= 0) { + need_pids = 1; + continue; + } + + assert(hrState_g.kd != NULL); + + plist = kvm_getprocs(hr_kd, KERN_PROC_PID, + entry->idle_pid, &nproc); + if (plist == NULL || nproc != 1) { + syslog(LOG_ERR, "%s: missing item with " + "PID = %d for CPU #%d\n ", __func__, + entry->idle_pid, entry->cpu_no); + need_pids = 1; + continue; + } + save_sample(entry, plist); + } + + if (need_pids == 1) + processor_get_pids(); + + proctbl_tick = this_tick; +} + +/** + * This function is called MAX_CPU_SAMPLES times per minute to collect the + * CPU load. + */ +static void +get_cpus_samples(void *arg __unused) +{ + + HRDBG("[%llu] ENTER", get_ticks()); + refresh_processor_tbl(); + HRDBG("[%llu] EXIT", get_ticks()); +} + +/** + * Called to start this table. We need to start the periodic idle + * time collection. + */ +void +start_processor_tbl(struct lmodule *mod) +{ + + /* + * Start the cpu stats collector + * The semantics of timer_start parameters is in "SNMP ticks"; + * we have 100 "SNMP ticks" per second, thus we are trying below + * to get MAX_CPU_SAMPLES per minute + */ + cpus_load_timer = timer_start_repeat(100, 100 * 60 / MAX_CPU_SAMPLES, + get_cpus_samples, NULL, mod); +} + +/** + * Access routine for the processor table. + */ +int +op_hrProcessorTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op) +{ + struct processor_entry *entry; + + if (this_tick != proctbl_tick) + refresh_processor_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&processor_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(&processor_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&processor_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_hrProcessorFrwID: + value->v.oid = entry->frwId; + return (SNMP_ERR_NOERROR); + + case LEAF_hrProcessorLoad: + value->v.integer = entry->load; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c new file mode 100644 index 000000000000..f8f6b47b8534 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c @@ -0,0 +1,514 @@ +/*- + * 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 scalars implementation for SNMPd. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* file pointer to keep an open instance of utmp */ +static FILE *utmp_fp; + +/* boot timestamp in centi-seconds */ +static uint64_t kernel_boot; + +/* physical memory size in Kb */ +static uint64_t phys_mem_size; + +/* boot line (malloced) */ +static u_char *boot_line; + +/* maximum number of processes */ +static uint32_t max_proc; + +/** + * Free all static data + */ +void +fini_scalars(void) +{ + + free(boot_line); + + if (utmp_fp != NULL) + (void)fclose(utmp_fp); +} + +/** + * Get system uptime in hundredths of seconds since the epoch + * Returns 0 in case of an error + */ +static int +OS_getSystemUptime(uint32_t *ut) +{ + struct timeval right_now; + uint64_t now; + + if (kernel_boot == 0) { + /* first time, do the sysctl */ + struct timeval kernel_boot_timestamp; + int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t len = sizeof(kernel_boot_timestamp); + + if (sysctl(mib, 2, &kernel_boot_timestamp, + &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl KERN_BOOTTIME failed: %m"); + return (SNMP_ERR_GENERR); + } + + HRDBG("boot timestamp from kernel: {%ld, %ld}", + kernel_boot_timestamp.tv_sec, + kernel_boot_timestamp.tv_usec); + + kernel_boot = ((uint64_t)kernel_boot_timestamp.tv_sec * 100) + + (kernel_boot_timestamp.tv_usec / 10000); + } + + if (gettimeofday(&right_now, NULL) < 0) { + syslog(LOG_ERR, "gettimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + now = ((uint64_t)right_now.tv_sec * 100) + (right_now.tv_usec / 10000); + + if (now - kernel_boot > UINT32_MAX) + *ut = UINT32_MAX; + else + *ut = now - kernel_boot; + + return (SNMP_ERR_NOERROR); +} + +/** + * Get system local date and time in a foramt suitable for DateAndTime TC: + * field octets contents range + * ----- ------ -------- ----- + * 1 1-2 year* 0..65536 + * 2 3 month 1..12 + * 3 4 day 1..31 + * 4 5 hour 0..23 + * 5 6 minutes 0..59 + * 6 7 seconds 0..60 + * (use 60 for leap-second) + * 7 8 deci-seconds 0..9 + * 8 9 direction from UTC '+' / '-' + * 9 10 hours from UTC* 0..13 + * 10 11 minutes from UTC 0..59 + * + * * Notes: + * - the value of year is in network-byte order + * - daylight saving time in New Zealand is +13 + * + * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be + * displayed as: + * + * 1992-5-26,13:30:15.0,-4:0 + * + * Returns -1 in case of an error or the length of the string (8 or 11) + * Actually returns always 11 on freebsd + */ +static int +OS_getSystemDate(struct snmp_value *value) +{ + u_char s_date_time[11]; + struct tm tloc_tm; + time_t tloc_time_t; + struct timeval right_now; + int string_len; + + if (gettimeofday(&right_now, NULL) < 0) { + syslog(LOG_ERR, "gettimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + + tloc_time_t = right_now.tv_sec; + + if (localtime_r(&tloc_time_t, &tloc_tm) == NULL) { + syslog(LOG_ERR, "localtime_r() failed: %m "); + return (SNMP_ERR_GENERR); + } + + string_len = make_date_time(s_date_time, &tloc_tm, + right_now.tv_usec / 100000); + + return (string_get(value, s_date_time, string_len)); +} + +/** + * Get kernel boot path. For FreeBSD it seems that no arguments are + * present. Returns NULL if an error occured. The returned data is a + * pointer to a global strorage. + */ +int +OS_getSystemInitialLoadParameters(u_char **params) +{ + + if (boot_line == NULL) { + int mib[2] = { CTL_KERN, KERN_BOOTFILE }; + char *buf; + size_t buf_len = 0; + + /* get the needed buffer len */ + if (sysctl(mib, 2, NULL, &buf_len, NULL, 0) != 0) { + syslog(LOG_ERR, + "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m"); + return (SNMP_ERR_GENERR); + } + + if ((buf = malloc(buf_len)) == NULL) { + syslog(LOG_ERR, "malloc failed"); + return (SNMP_ERR_GENERR); + } + if (sysctl(mib, 2, buf, &buf_len, NULL, 0)) { + syslog(LOG_ERR, + "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m"); + free(buf); + return (SNMP_ERR_GENERR); + } + + boot_line = buf; + HRDBG("kernel boot file: %s", boot_line); + } + + *params = boot_line; + return (SNMP_ERR_NOERROR); +} + +/** + * Get number of current users which are logged in + */ +static int +OS_getSystemNumUsers(uint32_t *nu) +{ + struct utmp utmp; + static int first_time = 1; + + if (utmp_fp == NULL) { + if (!first_time) + return (SNMP_ERR_GENERR); + first_time = 0; + if ((utmp_fp = fopen(_PATH_UTMP, "r")) == NULL) { + syslog(LOG_ERR, "fopen(%s) failed: %m", _PATH_UTMP); + return (SNMP_ERR_GENERR); + } + } + + /* start with the begining of the utmp file */ + (void)rewind(utmp_fp); + + *nu = 0; + while (fread(&utmp, sizeof(utmp), 1, utmp_fp) == 1) { + if (utmp.ut_name[0] != '\0' && utmp.ut_line[0] != '\0') { + if (getpwnam(utmp.ut_name) == NULL) + continue; + (*nu)++; + } + } + + return (SNMP_ERR_NOERROR); +} + +/** + * Get number of current processes existing into the system + */ +static int +OS_getSystemProcesses(uint32_t *proc_count) +{ + int pc; + + if (hr_kd == NULL) + return (SNMP_ERR_GENERR); + + if (kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &pc) == NULL) { + syslog(LOG_ERR, "kvm_getprocs failed: %m"); + return (SNMP_ERR_GENERR); + } + + *proc_count = pc; + return (SNMP_ERR_NOERROR); +} + +/** + * Get maximum number of processes allowed on this system + */ +static int +OS_getSystemMaxProcesses(uint32_t *mproc) +{ + + if (max_proc == 0) { + int mib[2] = { CTL_KERN, KERN_MAXPROC }; + int mp; + size_t len = sizeof(mp); + + if (sysctl(mib, 2, &mp, &len, NULL, 0) == -1) { + syslog(LOG_ERR, "sysctl KERN_MAXPROC failed: %m"); + return (SNMP_ERR_GENERR); + } + max_proc = mp; + } + + *mproc = max_proc; + return (SNMP_ERR_NOERROR); +} + +/* + * Get the physical memeory size in Kbytes. + * Returns SNMP error code. + */ +static int +OS_getMemorySize(uint32_t *ms) +{ + + if (phys_mem_size == 0) { + int mib[2] = { CTL_HW, HW_PHYSMEM }; + u_long physmem; + size_t len = sizeof(physmem); + + if (sysctl(mib, 2, &physmem, &len, NULL, 0) == -1) { + syslog(LOG_ERR, + "sysctl({ CTL_HW, HW_PHYSMEM }) failed: %m"); + return (SNMP_ERR_GENERR); + } + + phys_mem_size = physmem / 1024; + } + + if (phys_mem_size > UINT32_MAX) + *ms = UINT32_MAX; + else + *ms = phys_mem_size; + return (SNMP_ERR_NOERROR); +} + +/* + * Try to use the s_date_time parameter as a DateAndTime TC to fill in + * the second parameter. + * Returns 0 on succes and -1 for an error. + * Bug: time zone info is not used + */ +static struct timeval * +OS_checkSystemDateInput(const u_char *str, u_int len) +{ + struct tm tm_to_set; + time_t t; + struct timeval *tv; + + if (len != 8 && len != 11) + return (NULL); + + if (str[2] == 0 || str[2] > 12 || + str[3] == 0 || str[3] > 31 || + str[4] > 23 || str[5] > 59 || str[6] > 60 || str[7] > 9) + return (NULL); + + tm_to_set.tm_year = ((str[0] << 8) + str[1]) - 1900; + tm_to_set.tm_mon = str[2] - 1; + tm_to_set.tm_mday = str[3]; + tm_to_set.tm_hour = str[4]; + tm_to_set.tm_min = str[5]; + tm_to_set.tm_sec = str[6]; + tm_to_set.tm_isdst = 0; + + /* now make UTC from it */ + if ((t = timegm(&tm_to_set)) == (time_t)-1) + return (NULL); + + /* now apply timezone if specified */ + if (len == 11) { + if (str[9] > 13 || str[10] > 59) + return (NULL); + if (str[8] == '+') + t += 3600 * str[9] + 60 * str[10]; + else + t -= 3600 * str[9] + 60 * str[10]; + } + + if ((tv = malloc(sizeof(*tv))) == NULL) + return (NULL); + + tv->tv_sec = t; + tv->tv_usec = (int32_t)str[7] * 100000; + + return (tv); +} + +/* + * Set system date and time. Timezone is not changed + */ +static int +OS_setSystemDate(const struct timeval *timeval_to_set) +{ + if (settimeofday(timeval_to_set, NULL) == -1) { + syslog(LOG_ERR, "settimeofday failed: %m"); + return (SNMP_ERR_GENERR); + } + return (SNMP_ERR_NOERROR); +} + +/* + * prototype of this function was genrated by gensnmptree tool in header file + * hostres_tree.h + * Returns SNMP_ERR_NOERROR on success + */ +int +op_hrSystem(struct snmp_context *ctx, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + int err; + u_char *str; + + switch (curr_op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemUptime: + return (OS_getSystemUptime(&value->v.uint32)); + + case LEAF_hrSystemDate: + return (OS_getSystemDate(value)); + + case LEAF_hrSystemInitialLoadDevice: + value->v.uint32 = 0; /* FIXME */ + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadParameters: + if ((err = OS_getSystemInitialLoadParameters(&str)) != + SNMP_ERR_NOERROR) + return (err); + return (string_get(value, str, -1)); + + case LEAF_hrSystemNumUsers: + return (OS_getSystemNumUsers(&value->v.uint32)); + + case LEAF_hrSystemProcesses: + return (OS_getSystemProcesses(&value->v.uint32)); + + case LEAF_hrSystemMaxProcesses: + return (OS_getSystemMaxProcesses(&value->v.uint32)); + } + abort(); + + case SNMP_OP_SET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + if ((ctx->scratch->ptr1 = + OS_checkSystemDateInput(value->v.octetstring.octets, + value->v.octetstring.len)) == NULL) + return (SNMP_ERR_WRONG_VALUE); + + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + return (SNMP_ERR_NOT_WRITEABLE); + + } + abort(); + + case SNMP_OP_ROLLBACK: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + free(ctx->scratch->ptr1); + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + abort(); + } + abort(); + + case SNMP_OP_COMMIT: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSystemDate: + (void)OS_setSystemDate(ctx->scratch->ptr1); + free(ctx->scratch->ptr1); + return (SNMP_ERR_NOERROR); + + case LEAF_hrSystemInitialLoadDevice: + case LEAF_hrSystemInitialLoadParameters: + abort(); + } + abort(); + + case SNMP_OP_GETNEXT: + abort(); + } + abort(); +} + +/* + * prototype of this function was genrated by gensnmptree tool + * in the header file hostres_tree.h + * Returns SNMP_ERR_NOERROR on success + */ +int +op_hrStorage(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrMemorySize: + return (OS_getMemorySize(&value->v.uint32)); + } + abort(); + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c new file mode 100644 index 000000000000..47112da77708 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c @@ -0,0 +1,209 @@ +/*- + * 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. + * + * This C file contains code developed by Poul-Henning Kamp under the + * following license: + * + * FreeBSD: src/sbin/mdconfig/mdconfig.c,v 1.33.2.1 2004/09/14 03:32:21 jmg Exp + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $FreeBSD$ + */ + +/* + * Host Resources MIB implementation for bsnmpd. + */ + +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* Internal id got after we'll register this module with the agent */ +static u_int host_registration_id = 0; + +/* This our hostres module */ +static struct lmodule *hostres_module; + +/* See the generated file hostres_oid.h */ +static const struct asn_oid oid_host = OIDX_host; + +/* descriptor to access kernel memory */ +kvm_t *hr_kd; + +/* + * HOST RESOURCES mib module finalization hook. + * Returns 0 on success, < 0 on error + */ +static int +hostres_fini(void) +{ + + if (hr_kd != NULL) + (void)kvm_close(hr_kd); + + fini_storage_tbl(); + fini_fs_tbl(); + fini_processor_tbl(); + fini_disk_storage_tbl(); + fini_device_tbl(); + fini_partition_tbl(); + fini_network_tbl(); + fini_printer_tbl(); + + fini_swrun_tbl(); + fini_swins_tbl(); + + fini_scalars(); + + if (host_registration_id > 0) + or_unregister(host_registration_id); + + HRDBG("done."); + return (0); +} + +/* + * HOST RESOURCES mib module initialization hook. + * Returns 0 on success, < 0 on error + */ +static int +hostres_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) +{ + + hostres_module = mod; + + /* + * NOTE: order of these calls is important here! + */ + if ((hr_kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, + "kvm_open")) == NULL) { + syslog(LOG_ERR, "kvm_open failed: %m "); + return (-1); + } + + /* + * The order is relevant here, because some table depend on each other. + */ + init_device_tbl(); + + /* populates partition table too */ + if (init_disk_storage_tbl()) { + hostres_fini(); + return (-1); + } + init_processor_tbl(); + init_printer_tbl(); + + /* + * populate storage and FS tables. Must be done after device + * initialisation because the FS refresh code calls into the + * partition refresh code. + */ + init_storage_tbl(); + + + /* also the hrSWRunPerfTable's support is initialized here */ + init_swrun_tbl(); + init_swins_tbl(); + + HRDBG("done."); + + return (0); +} + +/* + * HOST RESOURCES mib module start operation + * returns nothing + */ +static void +hostres_start(void) +{ + + host_registration_id = or_register(&oid_host, + "The MIB module for Host Resource MIB (RFC 2790).", + hostres_module); + + start_device_tbl(hostres_module); + start_processor_tbl(hostres_module); + start_network_tbl(); + + HRDBG("done."); +} + +/* this identifies the HOST RESOURCES mib module */ +const struct snmp_module config = { + "This module implements the host resource mib (rfc 2790)", + hostres_init, + hostres_fini, + NULL, /* idle function, do not use it */ + NULL, + NULL, + hostres_start, + NULL, /* proxy a PDU */ + hostres_ctree, /* see the generated hostres_tree.h */ + hostres_CTREE_SIZE, /* see the generated hostres_tree.h */ + NULL +}; + +/** + * Make an SNMP DateAndTime from a struct tm. This should be in the library. + */ +int +make_date_time(u_char *str, const struct tm *tm, u_int decisecs) +{ + + str[0] = (u_char)((tm->tm_year + 1900) >> 8); + str[1] = (u_char)(tm->tm_year + 1900); + str[2] = tm->tm_mon + 1; + str[3] = tm->tm_mday; + str[4] = tm->tm_hour; + str[5] = tm->tm_min; + str[6] = tm->tm_sec; + str[7] = decisecs; + if (tm->tm_gmtoff < 0) + str[8] = '-'; + else + str[8] = '+'; + + str[9] = (u_char)(abs(tm->tm_gmtoff) / 3600); + str[10] = (u_char)((abs(tm->tm_gmtoff) % 3600) / 60); + + return (11); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h new file mode 100644 index 000000000000..acac2384878d --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h @@ -0,0 +1,301 @@ +/* + * 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. + * + * Host Resources MIB for SNMPd. + * + * $FreeBSD$ + */ + +#ifndef HOSTRES_SNMP_H_1132245017 +#define HOSTRES_SNMP_H_1132245017 + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Default package directory for hrSWInstalledTable. Can be overridden + * via SNMP or configuration file. + */ +#define PATH_PKGDIR "/var/db/pkg" + +/* + * These are the default maximum caching intervals for the various tables + * in seconds. They can be overridden from the configuration file. + */ +#define HR_STORAGE_TBL_REFRESH 7 +#define HR_FS_TBL_REFRESH 7 +#define HR_DISK_TBL_REFRESH 7 +#define HR_NETWORK_TBL_REFRESH 7 +#define HR_SWINS_TBL_REFRESH 120 +#define HR_SWRUN_TBL_REFRESH 3 + +struct tm; +struct statfs; + +/* a debug macro */ +#ifndef NDEBUG + +#define HRDBG(...) do { \ + fprintf(stderr, "HRDEBUG: %s: ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (0) + +#else + +#define HRDBG(...) do { } while (0) + +#endif /*NDEBUG*/ + +/* path to devd(8) output pipe */ +#define PATH_DEVD_PIPE "/var/run/devd.pipe" + +#define IS_KERNPROC(kp) (((kp)->ki_flag & P_KTHREAD) == P_KTHREAD) + +enum snmpTCTruthValue { + SNMP_TRUE = 1, + SNMP_FALSE= 2 +}; + +/* The number of CPU load samples per one minute, per each CPU */ +#define MAX_CPU_SAMPLES 4 + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrDeviceTable + */ +struct device_entry { + int32_t index; + struct asn_oid type; + u_char descr[64 + 1]; + struct asn_oid id; + int32_t status; /* enum DeviceStatus */ + uint32_t errors; + +#define HR_DEVICE_FOUND 0x001 + /* not dectected by libdevice, so don't try to refresh it*/ +#define HR_DEVICE_IMMUTABLE 0x002 + + /* next 3 are not from the SNMP mib table, only to be used internally */ + uint32_t flags; + u_char name[32 + 1]; + u_char location[128 + 1]; + TAILQ_ENTRY(device_entry) link; +}; + +/* + * Next structure is used to keep o list of mappings from a specific + * name (a_name) to an entry in the hrFSTblEntry; + * We are trying to keep the same index for a specific name at least + * for the duration of one SNMP agent run. + */ +struct device_map_entry { + int32_t hrIndex; /* used for hrDeviceTblEntry::index */ + + /* map key is the pair (name_key, location_key) */ + u_char name_key[32 + 1]; /* copy of device name */ + u_char location_key[128 + 1]; + + /* + * Next may be NULL if the respective hrDeviceTblEntry + * is (temporally) gone. + */ + struct device_entry *entry_p; + STAILQ_ENTRY(device_map_entry) link; +}; +STAILQ_HEAD(device_map, device_map_entry); + +/* descriptor to access kernel memory */ +extern kvm_t *hr_kd; + +/* Table used for consistent device table indexing. */ +extern struct device_map device_map; + +/* Maximum number of ticks between two updates for hrStorageTable */ +extern uint32_t storage_tbl_refresh; + +/* Maximum number of ticks between updated of FS table */ +extern uint32_t fs_tbl_refresh; + +/* maximum number of ticks between updates of SWRun and SWRunPerf table */ +extern uint32_t swrun_tbl_refresh; + +/* Maximum number of ticks between device table refreshs. */ +extern uint32_t device_tbl_refresh; + +/* maximum number of ticks between refreshs */ +extern uint32_t disk_storage_tbl_refresh; + +/* maximum number of ticks between updates of network table */ +extern uint32_t swins_tbl_refresh; + +/* maximum number of ticks between updates of network table */ +extern uint32_t network_tbl_refresh; + +/* package directory */ +extern u_char *pkg_dir; + +/* Initialize and populate storage table */ +void init_storage_tbl(void); + +/* Finalization routine for hrStorageTable. */ +void fini_storage_tbl(void); + +/* Refresh routine for hrStorageTable. */ +void refresh_storage_tbl(int); + +/* + * Get the type of filesystem referenced in a struct statfs * - + * used by FSTbl and StorageTbl functions. + */ +const struct asn_oid *fs_get_type(const struct statfs *); + +/* + * Because hrFSTable depends to hrStorageTable we are + * refreshing hrFSTable by refreshing hrStorageTable. + * When one entry "of type" fs from hrStorageTable is refreshed + * then the corresponding entry from hrFSTable is refreshed + * FS_tbl_pre_refresh_v() is called before refeshing fs part of hrStorageTable + */ +void fs_tbl_pre_refresh(void); +void fs_tbl_process_statfs_entry(const struct statfs *, int32_t); + +/* Called after refreshing fs part of hrStorageTable */ +void fs_tbl_post_refresh(void); + +/* Refresh the FS table if neccessary. */ +void refresh_fs_tbl(void); + +/* Finalization routine for hrFSTable. */ +void fini_fs_tbl(void); + +/* Init the things for both of hrSWRunTable and hrSWRunPerfTable */ +void init_swrun_tbl(void); + +/* Finalization routine for both of hrSWRunTable and hrSWRunPerfTable */ +void fini_swrun_tbl(void); + +/* Init and populate hrDeviceTable */ +void init_device_tbl(void); + +/* start devd monitoring */ +void start_device_tbl(struct lmodule *); + +/* Finalization routine for hrDeviceTable */ +void fini_device_tbl(void); + +/* Refresh routine for hrDeviceTable. */ +void refresh_device_tbl(int); + +/* Find an item in hrDeviceTbl by its entry->index. */ +struct device_entry *device_find_by_index(int32_t); + +/* Find an item in hrDeviceTbl by name. */ +struct device_entry *device_find_by_name(const char *); + +/* Create a new entry out of thin air. */ +struct device_entry *device_entry_create(const char *, const char *, + const char *); + +/* Init the things for hrProcessorTable. */ +void init_processor_tbl(void); + +/* Finalization routine for hrProcessorTable. */ +void fini_processor_tbl(void); + +/* Start the processor table CPU load collector. */ +void start_processor_tbl(struct lmodule *); + +/* Init the things for hrDiskStorageTable */ +int init_disk_storage_tbl(void); + +/* Finalization routine for hrDiskStorageTable. */ +void fini_disk_storage_tbl(void); + +/* Refresh routine for hrDiskStorageTable. */ +void refresh_disk_storage_tbl(int); + +/* Finalization routine for hrPartitionTable. */ +void fini_partition_tbl(void); + +/* Finalization routine for hrNetworkTable. */ +void fini_network_tbl(void); + +/* populate network table */ +void start_network_tbl(void); + +/* initialize installed software table */ +void init_swins_tbl(void); + +/* finalize installed software table */ +void fini_swins_tbl(void); + +/* refresh the hrSWInstalledTable if necessary */ +void refresh_swins_tbl(void); + +/* Init the things for hrPrinterTable */ +void init_printer_tbl(void); + +/* Finalization routine for hrPrinterTable. */ +void fini_printer_tbl(void); + +/* Refresh printer table */ +void refresh_printer_tbl(void); + +/* get boot command line */ +int OS_getSystemInitialLoadParameters(u_char **); + +/* Start refreshing the partition table */ +void partition_tbl_post_refresh(void); + +/* Handle refresh for the given disk */ +void partition_tbl_handle_disk(int32_t, const char *); + +/* Finish refreshing the partition table. */ +void partition_tbl_pre_refresh(void); + +/* Set the FS index in a partition table entry */ +void handle_partition_fs_index(const char *, int32_t); + +/* Make an SNMP DateAndTime from a struct tm. */ +int make_date_time(u_char *, const struct tm *, u_int); + +/* Free all static data */ +void fini_scalars(void); + +#endif /* HOSTRES_SNMP_H_1132245017 */ diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c new file mode 100644 index 000000000000..851d0f71ec89 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c @@ -0,0 +1,648 @@ +/*- + * 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 hrStorageTable + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include /*for getpagesize()*/ + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrStorageTable + */ +struct storage_entry { + int32_t index; + struct asn_oid type; + u_char descr[255 + 1]; + int32_t allocationUnits; + int32_t size; + int32_t used; + uint32_t allocationFailures; +#define HR_STORAGE_FOUND 0x001 + uint32_t flags; /* to be used internally*/ + TAILQ_ENTRY(storage_entry) link; +}; +TAILQ_HEAD(storage_tbl, storage_entry); + +/* + * Next structure is used to keep o list of mappings from a specific name + * (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the + * same index for a specific name at least for the duration of one SNMP agent + * run. + */ +struct storage_map_entry { + int32_t hrIndex; /* used for hrStorageTblEntry::index */ + + /* map key, also used for hrStorageTblEntry::descr */ + u_char a_name[255 + 1]; + + /* + * next may be NULL if the respective hrStorageTblEntry + * is (temporally) gone + */ + struct storage_entry *entry; + STAILQ_ENTRY(storage_map_entry) link; +}; +STAILQ_HEAD(storage_map, storage_map_entry); + +/* the head of the list with table's entries */ +static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl); + +/*for consistent table indexing*/ +static struct storage_map storage_map = + STAILQ_HEAD_INITIALIZER(storage_map); + +/* last (agent) tick when hrStorageTable was updated */ +static uint64_t storage_tick; + +/* maximum number of ticks between two refreshs */ +uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100; + +/* for kvm_getswapinfo, malloc'd */ +static struct kvm_swap *swap_devs; +static size_t swap_devs_len; /* item count for swap_devs */ + +/* for getfsstat, malloc'd */ +static struct statfs *fs_buf; +static size_t fs_buf_count; /* item count for fs_buf */ + +static struct vmtotal mem_stats; + +/* next int available for indexing the hrStorageTable */ +static uint32_t next_storage_index = 1; + +/* start of list for memory detailed stats */ +static struct memory_type_list *mt_list; + +/* Constants */ +static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam; +static const struct asn_oid OIDX_hrStorageVirtualMemory_c = + OIDX_hrStorageVirtualMemory; + +/** + * Create a new entry into the storage table and, if neccessary, an + * entry into the storage map. + */ +static struct storage_entry * +storage_entry_create(const char *name) +{ + struct storage_entry *entry; + struct storage_map_entry *map; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + + strlcpy(entry->descr, name, sizeof(entry->descr)); + + STAILQ_FOREACH(map, &storage_map, link) + if (strcmp(map->a_name, entry->descr) == 0) { + entry->index = map->hrIndex; + map->entry = entry; + break; + } + + if (map == NULL) { + /* new object - get a new index */ + if (next_storage_index > INT_MAX) { + syslog(LOG_ERR, + "%s: hrStorageTable index wrap", __func__); + free(entry); + return (NULL); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ ); + free(entry); + return (NULL); + } + + map->hrIndex = next_storage_index ++; + strlcpy(map->a_name, entry->descr, sizeof(map->a_name)); + map->entry = entry; + + STAILQ_INSERT_TAIL(&storage_map, map, link); + + HRDBG("%s added into hrStorageMap at index=%d", + name, map->hrIndex); + } else { + HRDBG("%s exists in hrStorageMap index=%d\n", + name, map->hrIndex); + } + + entry->index = map->hrIndex; + + INSERT_OBJECT_INT(entry, &storage_tbl); + + return (entry); +} + +/** + * Delete an entry from the storage table. + */ +static void +storage_entry_delete(struct storage_entry *entry) +{ + struct storage_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&storage_tbl, entry, link); + STAILQ_FOREACH(map, &storage_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + + free(entry); +} + +/** + * Find a table entry by its name. + */ +static struct storage_entry * +storage_find_by_name(const char *name) +{ + struct storage_entry *entry; + + TAILQ_FOREACH(entry, &storage_tbl, link) + if (strncmp(entry->descr, name, + sizeof(entry->descr) - 1) == 0) + return (entry); + + return (NULL); +} + +/* + * VM info. + */ +static void +storage_OS_get_vm(void) +{ + int mib[2] = { CTL_VM, VM_TOTAL }; + size_t len = sizeof(mem_stats); + int page_size_bytes; + struct storage_entry *entry; + + if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) { + syslog(LOG_ERR, + "hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) " + "failed: %m", __func__); + assert(0); + return; + } + + page_size_bytes = getpagesize(); + + /* Real Memory Metrics */ + if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL && + (entry = storage_entry_create("Real Memory Metrics")) == NULL) + return; /* I'm out of luck now, maybe next time */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = OIDX_hrStorageRam_c; + entry->allocationUnits = page_size_bytes; + entry->size = mem_stats.t_rm; + entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */ + entry->allocationFailures = 0; + + /* Shared Real Memory Metrics */ + if ((entry = storage_find_by_name("Shared Real Memory Metrics")) == + NULL && + (entry = storage_entry_create("Shared Real Memory Metrics")) == + NULL) + return; + + entry->flags |= HR_STORAGE_FOUND; + entry->type = OIDX_hrStorageRam_c; + entry->allocationUnits = page_size_bytes; + entry->size = mem_stats.t_rmshr; + /* ACTIVE is not USED - FIXME */ + entry->used = mem_stats.t_armshr; + entry->allocationFailures = 0; +} + +static void +storage_OS_get_memstat(void) +{ + struct memory_type *mt_item; + struct storage_entry *entry; + + if (mt_list == NULL) { + if ((mt_list = memstat_mtl_alloc()) == NULL) + /* again? we have a serious problem */ + return; + } + + if (memstat_sysctl_all(mt_list, 0) < 0) { + syslog(LOG_ERR, "memstat_sysctl_all failed: %s", + memstat_strerror(memstat_mtl_geterror(mt_list)) ); + return; + } + + if ((mt_item = memstat_mtl_first(mt_list)) == NULL) { + /* usually this is not an error, no errno for this failure*/ + HRDBG("memstat_mtl_first failed"); + return; + } + + do { + const char *memstat_name; + uint64_t tmp_size; + int allocator; + char alloc_descr[255 + 1]; + + memstat_name = memstat_get_name(mt_item); + + if (memstat_name == NULL || strlen(memstat_name) == 0) + continue; + + switch (allocator = memstat_get_allocator(mt_item)) { + + case ALLOCATOR_MALLOC: + snprintf(alloc_descr, sizeof(alloc_descr), + "MALLOC: %s", memstat_name); + break; + + case ALLOCATOR_UMA: + snprintf(alloc_descr, sizeof(alloc_descr), + "UMA: %s", memstat_name); + break; + + default: + snprintf(alloc_descr, sizeof(alloc_descr), + "UNKNOWN%d: %s", allocator, memstat_name); + break; + } + + if ((entry = storage_find_by_name(alloc_descr)) == NULL && + (entry = storage_entry_create(alloc_descr)) == NULL) + return; + + entry->flags |= HR_STORAGE_FOUND; + entry->type = OIDX_hrStorageRam_c; + + if ((tmp_size = memstat_get_size(mt_item)) == 0) + tmp_size = memstat_get_sizemask(mt_item); + entry->allocationUnits = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_countlimit(mt_item); + entry->size = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_count(mt_item); + entry->used = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + tmp_size = memstat_get_failures(mt_item); + entry->allocationFailures = + (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); + + } while((mt_item = memstat_mtl_next(mt_item)) != NULL); +} + +/** + * Get swap info + */ +static void +storage_OS_get_swap(void) +{ + int nswapdev = 0; + size_t len = sizeof(nswapdev); + struct storage_entry *entry; + char swap_w_prefix[255 + 1]; + + if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) { + syslog(LOG_ERR, + "hrStorageTable: sysctlbyname(\"vm.nswapdev\") " + "failed. %m"); + assert(0); + return; + } + + if (nswapdev <= 0) { + HRDBG("vm.nswapdev is %d", nswapdev); + return; + } + + if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) { + swap_devs_len = nswapdev + 1; + swap_devs = reallocf(swap_devs, + swap_devs_len * sizeof(struct kvm_swap)); + + assert(swap_devs != NULL); + if (swap_devs == NULL) { + swap_devs_len = 0; + return; + } + } + + nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0); + if (nswapdev < 0) { + syslog(LOG_ERR, + "hrStorageTable: kvm_getswapinfo failed. %m\n"); + assert(0); + return; + } + + for (len = 0; len < (size_t)nswapdev; len++) { + memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix)); + snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1, + "Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname); + + entry = storage_find_by_name(swap_w_prefix); + if (entry == NULL) + entry = storage_entry_create(swap_w_prefix); + + assert (entry != NULL); + if (entry == NULL) + return; /* Out of luck */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = OIDX_hrStorageVirtualMemory_c; + entry->allocationUnits = getpagesize(); + entry->size = swap_devs[len].ksw_total; + entry->used = swap_devs[len].ksw_used; + entry->allocationFailures = 0; + } +} + +/** + * Query the underlaying OS for the mounted file systems + * anf fill in the respective lists (for hrStorageTable and for hrFSTable) + */ +static void +storage_OS_get_fs(void) +{ + struct storage_entry *entry; + uint64_t used_blocks_count = 0; + char fs_string[255+1]; + int mounted_fs_count; + int i = 0; + + if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) { + syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); + return; /* out of luck this time */ + } + + if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) { + fs_buf_count = mounted_fs_count; + fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs)); + if (fs_buf == NULL) { + fs_buf_count = 0; + assert(0); + return; + } + } + + if ((mounted_fs_count = getfsstat(fs_buf, + fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) { + syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); + return; /* out of luck this time */ + } + + HRDBG("got %d mounted FS", mounted_fs_count); + + fs_tbl_pre_refresh(); + + for (i = 0; i < mounted_fs_count; i++) { + snprintf(fs_string, sizeof(fs_string), + "%s, type: %s, dev: %s", fs_buf[i].f_mntonname, + fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname); + + entry = storage_find_by_name(fs_string); + if (entry == NULL) + entry = storage_entry_create(fs_string); + + assert (entry != NULL); + if (entry == NULL) + return; /* Out of luck */ + + entry->flags |= HR_STORAGE_FOUND; + entry->type = *fs_get_type(&fs_buf[i]); + + if (fs_buf[i].f_bsize > INT_MAX) + entry->allocationUnits = INT_MAX; + else + entry->allocationUnits = fs_buf[i].f_bsize; + + if (fs_buf[i].f_blocks > INT_MAX) + entry->size = INT_MAX; + else + entry->size = fs_buf[i].f_blocks; + + used_blocks_count = fs_buf[i].f_blocks - fs_buf[i].f_bfree; + + if (used_blocks_count > INT_MAX) + entry->used = INT_MAX; + else + entry->used = used_blocks_count; + + entry->allocationFailures = 0; + + /* take care of hrFSTable */ + fs_tbl_process_statfs_entry(&fs_buf[i], entry->index); + } + + fs_tbl_post_refresh(); +} + +/** + * Initialize storage table and populate it. + */ +void +init_storage_tbl(void) +{ + if ((mt_list = memstat_mtl_alloc()) == NULL) + syslog(LOG_ERR, + "hrStorageTable: memstat_mtl_alloc() failed: %m"); + + refresh_storage_tbl(1); +} + +void +fini_storage_tbl(void) +{ + struct storage_map_entry *n1; + + if (swap_devs != NULL) { + free(swap_devs); + swap_devs = NULL; + } + swap_devs_len = 0; + + if (fs_buf != NULL) { + free(fs_buf); + fs_buf = NULL; + } + fs_buf_count = 0; + + while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) { + STAILQ_REMOVE_HEAD(&storage_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&storage_tbl, n1->entry, link); + free(n1->entry); + } + free(n1); + } + assert(TAILQ_EMPTY(&storage_tbl)); +} + +void +refresh_storage_tbl(int force) +{ + struct storage_entry *entry, *entry_tmp; + + if (!force && storage_tick != 0 && + this_tick - storage_tick < storage_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &storage_tbl, link) + entry->flags &= ~HR_STORAGE_FOUND; + + storage_OS_get_vm(); + storage_OS_get_swap(); + storage_OS_get_fs(); + storage_OS_get_memstat(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp) + if (!(entry->flags & HR_STORAGE_FOUND)) + storage_entry_delete(entry); + + storage_tick = this_tick; + + HRDBG("refresh DONE"); +} + +/* + * This is the implementation for a generated (by our SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrStorageTable + */ +int +op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct storage_entry *entry; + + refresh_storage_tbl(0); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&storage_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(&storage_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&storage_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_hrStorageIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageType: + value->v.oid = entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageDescr: + return (string_get(value, entry->descr, -1)); + break; + + case LEAF_hrStorageAllocationUnits: + value->v.integer = entry->allocationUnits; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageSize: + value->v.integer = entry->size; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageUsed: + value->v.integer = entry->used; + return (SNMP_ERR_NOERROR); + + case LEAF_hrStorageAllocationFailures: + value->v.uint32 = entry->allocationFailures; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c new file mode 100644 index 000000000000..1c21a9f64065 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c @@ -0,0 +1,528 @@ +/* + * 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 implementation for SNMPd: instrumentation for + * hrSWInstalledTable + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + +#define CONTENTS_FNAME "+CONTENTS" + +enum SWInstalledType { + SWI_UNKNOWN = 1, + SWI_OPERATING_SYSTEM = 2, + SWI_DEVICE_DRIVER = 3, + SWI_APPLICATION = 4 +}; + +#define NAMELEN 64 /* w/o \0 */ + +/* + * This structure is used to hold a SNMP table entry + * for HOST-RESOURCES-MIB's hrSWInstalledTable + */ +struct swins_entry { + int32_t index; + u_char name[NAMELEN + 1]; + struct asn_oid id; + int32_t type; /* from enum SWInstalledType */ + u_char date[11]; + u_int date_len; + +#define HR_SWINSTALLED_FOUND 0x001 +#define HR_SWINSTALLED_IMMUTABLE 0x002 + uint32_t flags; + + TAILQ_ENTRY(swins_entry) link; +}; +TAILQ_HEAD(swins_tbl, swins_entry); + +/* + * Table to keep a conistent mapping between software and indexes. + */ +struct swins_map_entry { + int32_t index; /* hrSWInstalledTblEntry::index */ + u_char name[NAMELEN + 1]; /* map key */ + + /* + * next may be NULL if the respective hrSWInstalledTblEntry + * is (temporally) gone + */ + struct swins_entry *entry; + + STAILQ_ENTRY(swins_map_entry) link; +}; +STAILQ_HEAD(swins_map, swins_map_entry); + +/* map for consistent indexing */ +static struct swins_map swins_map = STAILQ_HEAD_INITIALIZER(swins_map); + +/* the head of the list with hrSWInstalledTable's entries */ +static struct swins_tbl swins_tbl = TAILQ_HEAD_INITIALIZER(swins_tbl); + +/* next int available for indexing the hrSWInstalledTable */ +static uint32_t next_swins_index = 1; + +/* last (agent) tick when hrSWInstalledTable was updated */ +static uint64_t swins_tick; + +/* maximum number of ticks between updates of network table */ +uint32_t swins_tbl_refresh = HR_SWINS_TBL_REFRESH * 100; + +/* package directory */ +u_char *pkg_dir; + +/* last change of package list */ +static time_t os_pkg_last_change; + +/** + * Create a new entry into the hrSWInstalledTable + */ +static struct swins_entry * +swins_entry_create(const char *name) +{ + struct swins_entry *entry; + struct swins_map_entry *map; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + strlcpy((char*)entry->name, name, sizeof(entry->name)); + + STAILQ_FOREACH(map, &swins_map, link) + if (strcmp((const char *)map->name, + (const char *)entry->name) == 0) + break; + + if (map == NULL) { + /* new object - get a new index */ + if (next_swins_index > INT_MAX) { + syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap", + __func__ ); + free(entry); + return (NULL); + } + + if ((map = malloc(sizeof(*map))) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__ ); + free(entry); + return (NULL); + } + map->index = next_swins_index++; + strcpy((char *)map->name, (const char *)entry->name); + + STAILQ_INSERT_TAIL(&swins_map, map, link); + + HRDBG("%s added into hrSWInstalled at %d", name, map->index); + } + entry->index = map->index; + map->entry = entry; + + INSERT_OBJECT_INT(entry, &swins_tbl); + + return (entry); +} + +/** + * Delete an entry in the hrSWInstalledTable + */ +static void +swins_entry_delete(struct swins_entry *entry) +{ + struct swins_map_entry *map; + + assert(entry != NULL); + + TAILQ_REMOVE(&swins_tbl, entry, link); + + STAILQ_FOREACH(map, &swins_map, link) + if (map->entry == entry) { + map->entry = NULL; + break; + } + + free(entry); +} + +/** + * Find an entry given it's name + */ +static struct swins_entry * +swins_find_by_name(const char *name) +{ + struct swins_entry *entry; + + TAILQ_FOREACH(entry, &swins_tbl, link) + if (strncmp((const char*)entry->name, name, + sizeof(entry->name) - 1) == 0) + return (entry); + return (NULL); +} + +/** + * Finalize this table + */ +void +fini_swins_tbl(void) +{ + struct swins_map_entry *n1; + + while ((n1 = STAILQ_FIRST(&swins_map)) != NULL) { + STAILQ_REMOVE_HEAD(&swins_map, link); + if (n1->entry != NULL) { + TAILQ_REMOVE(&swins_tbl, n1->entry, link); + free(n1->entry); + } + free(n1); + } + assert(TAILQ_EMPTY(&swins_tbl)); +} + +/** + * Get the *running* O/S identification + */ +static void +swins_get_OS_ident(void) +{ + struct utsname os_id; + char os_string[NAMELEN + 1]; + struct swins_entry *entry; + u_char *boot; + struct stat sb; + struct tm k_ts; + + if (uname(&os_id) == -1) + return; + + snprintf(os_string, sizeof(os_string), "%s: %s", + os_id.sysname, os_id.version); + + if ((entry = swins_find_by_name(os_string)) != NULL || + (entry = swins_entry_create(os_string)) == NULL) + return; + + entry->flags |= (HR_SWINSTALLED_FOUND | HR_SWINSTALLED_IMMUTABLE); + entry->id = oid_zeroDotZero; + entry->type = (int32_t)SWI_OPERATING_SYSTEM; + memset(entry->date, 0, sizeof(entry->date)); + + if (OS_getSystemInitialLoadParameters(&boot) == SNMP_ERR_NOERROR && + strlen(boot) > 0 && stat(boot, &sb) == 0 && + localtime_r(&sb.st_ctime, &k_ts) != NULL) + entry->date_len = make_date_time(entry->date, &k_ts, 0); +} + +/** + * Read the installed packages + */ +static int +swins_get_packages(void) +{ + struct stat sb; + DIR *p_dir; + struct dirent *ent; + struct tm k_ts; + char *pkg_file; + struct swins_entry *entry; + int ret = 0; + + if (pkg_dir == NULL) + /* initialisation may have failed */ + return (-1); + + if (stat(pkg_dir, &sb) != 0) { + syslog(LOG_ERR, "hrSWInstalledTable: stat(\"%s\") failed: %m", + pkg_dir); + return (-1); + } + if (!S_ISDIR(sb.st_mode)) { + syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" is not a directory", + pkg_dir); + return (-1); + } + if (sb.st_ctime <= os_pkg_last_change) { + HRDBG("no need to rescan installed packages -- " + "directory time-stamp unmodified"); + + TAILQ_FOREACH(entry, &swins_tbl, link) + entry->flags |= HR_SWINSTALLED_FOUND; + + return (0); + } + + if ((p_dir = opendir(pkg_dir)) == NULL) { + syslog(LOG_ERR, "hrSWInstalledTable: opendir(\"%s\") failed: " + "%m", pkg_dir); + return (-1); + } + + while (errno = 0, (ent = readdir(p_dir)) != NULL) { + HRDBG(" pkg file: %s", ent->d_name); + + /* check that the contents file is a regular file */ + if (asprintf(&pkg_file, "%s/%s/%s", pkg_dir, ent->d_name, + CONTENTS_FNAME) == -1) + continue; + + if (stat(pkg_file, &sb) != 0 ) { + free(pkg_file); + continue; + } + + if (!S_ISREG(sb.st_mode)) { + syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" not a " + "regular file -- skipped", pkg_file); + free(pkg_file); + continue; + } + free(pkg_file); + + /* read directory timestamp on package */ + if (asprintf(&pkg_file, "%s/%s", pkg_dir, ent->d_name) == -1) + continue; + + if (stat(pkg_file, &sb) == -1 || + localtime_r(&sb.st_ctime, &k_ts) == NULL) { + free(pkg_file); + continue; + } + free(pkg_file); + + /* update or create entry */ + if ((entry = swins_find_by_name(ent->d_name)) == NULL && + (entry = swins_entry_create(ent->d_name)) == NULL) { + ret = -1; + goto PKG_LOOP_END; + } + + entry->flags |= HR_SWINSTALLED_FOUND; + entry->id = oid_zeroDotZero; + entry->type = (int32_t)SWI_APPLICATION; + + entry->date_len = make_date_time(entry->date, &k_ts, 0); + } + + if (errno != 0) { + syslog(LOG_ERR, "hrSWInstalledTable: readdir_r(\"%s\") failed:" + " %m", pkg_dir); + ret = -1; + } else { + /* + * save the timestamp of directory + * to avoid any further scanning + */ + os_pkg_last_change = sb.st_ctime; + } + PKG_LOOP_END: + (void)closedir(p_dir); + return (ret); +} + +/** + * Refresh the installed software table. + */ +void +refresh_swins_tbl(void) +{ + int ret; + struct swins_entry *entry, *entry_tmp; + + if (this_tick - swins_tick < swins_tbl_refresh) { + HRDBG("no refresh needed"); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &swins_tbl, link) + entry->flags &= ~HR_SWINSTALLED_FOUND; + + ret = swins_get_packages(); + + TAILQ_FOREACH_SAFE(entry, &swins_tbl, link, entry_tmp) + if (!(entry->flags & HR_SWINSTALLED_FOUND) && + !(entry->flags & HR_SWINSTALLED_IMMUTABLE)) + swins_entry_delete(entry); + + if (ret == 0) + swins_tick = this_tick; +} + +/** + * Create and populate the package table + */ +void +init_swins_tbl(void) +{ + + if ((pkg_dir = malloc(sizeof(PATH_PKGDIR))) == NULL) { + syslog(LOG_ERR, "%s: %m", __func__); + } else + strcpy(pkg_dir, PATH_PKGDIR); + + swins_get_OS_ident(); + refresh_swins_tbl(); + + HRDBG("init done"); +} + +/** + * SNMP handler + */ +int +op_hrSWInstalledTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op) +{ + struct swins_entry *entry; + + refresh_swins_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swins_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(&swins_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swins_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_hrSWInstalledIndex: + value->v.integer = entry->index; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledName: + return (string_get(value, entry->name, -1)); + break; + + case LEAF_hrSWInstalledID: + value->v.oid = entry->id; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledType: + value->v.integer = entry->type; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWInstalledDate: + return (string_get(value, entry->date, entry->date_len)); + } + abort(); +} + +/** + * Scalars + */ +int +op_hrSWInstalled(struct snmp_context *ctx __unused, + struct snmp_value *value __unused, u_int sub, + u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only SNMP GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); + + get: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWInstalledLastChange: + case LEAF_hrSWInstalledLastUpdateTime: + /* + * We always update the entire table so these two tick + * values should be equal. + */ + refresh_swins_tbl(); + if (swins_tick <= start_tick) + value->v.uint32 = 0; + else { + uint64_t lastChange = swins_tick - start_tick; + + /* may overflow the SNMP type */ + value->v.uint32 = + (lastChange > UINT_MAX ? UINT_MAX : lastChange); + } + + return (SNMP_ERR_NOERROR); + + default: + abort(); + } +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c new file mode 100644 index 000000000000..bf9ecab022a0 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c @@ -0,0 +1,751 @@ +/* + * 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 hrSWRunTable + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "hostres_snmp.h" +#include "hostres_oid.h" +#include "hostres_tree.h" + + +/* + * Ugly thing: PID_MAX, NO_PID defined only in kernel + */ +#define NO_PID 100000 + +enum SWRunType { + SRT_UNKNOWN = 1, + SRT_OPERATING_SYSTEM = 2, + SRT_DEVICE_DRIVER = 3, + SRT_APPLICATION = 4 + +}; + +enum SWRunStatus { + SRS_RUNNING = 1, + SRS_RUNNABLE = 2, + SRS_NOT_RUNNABLE = 3, + SRS_INVALID = 4 +}; + +/* + * This structure is used to hold a SNMP table entry + * for both hrSWRunTable and hrSWRunPerfTable because + * hrSWRunPerfTable AUGMENTS hrSWRunTable + */ +struct swrun_entry { + int32_t index; + u_char name[64 + 1]; + struct asn_oid id; + u_char path[128 + 1]; + u_char parameters[128 + 1]; + int32_t type; /* enum SWRunType */ + int32_t status; /* enum SWRunStatus */ + int32_t perfCPU; + int32_t perfMemory; +#define HR_SWRUN_FOUND 0x001 + uint32_t flags; + uint64_t r_tick; /* tick when entry refreshed */ + TAILQ_ENTRY(swrun_entry) link; +}; +TAILQ_HEAD(swrun_tbl, swrun_entry); + +/* the head of the list with hrSWRunTable's entries */ +static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl); + +/* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */ +static uint64_t swrun_tick; + +/* maximum number of ticks between updates of SWRun and SWRunPerf table */ +uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100; + +/* the value of the MIB object with the same name */ +static int32_t SWOSIndex; + +/** + * Malloc a new entry and add it to the list + * associated to this table. The item identified by + * the index parameter must not exist in this list. + */ +static struct swrun_entry * +swrun_entry_create(int32_t idx) +{ + struct swrun_entry *entry; + + if ((entry = malloc(sizeof(*entry))) == NULL) { + syslog(LOG_WARNING, "%s: %m", __func__); + return (NULL); + } + memset(entry, 0, sizeof(*entry)); + entry->index = idx; + + INSERT_OBJECT_INT(entry, &swrun_tbl); + return (entry); +} + +/** + * Unlink the entry from the list and then free its heap memory + */ +static void +swrun_entry_delete(struct swrun_entry *entry) +{ + + assert(entry != NULL); + + TAILQ_REMOVE(&swrun_tbl, entry, link); + + free(entry); +} + +/** + * Search one item by its index, return NULL if none found + */ +static struct swrun_entry * +swrun_entry_find_by_index(int32_t idx) +{ + struct swrun_entry *entry; + + TAILQ_FOREACH(entry, &swrun_tbl, link) + if (entry->index == idx) + return (entry); + return (NULL); +} + +/** + * Translate the kernel's process status to the SNMP one. + */ +static enum SWRunStatus +swrun_OS_get_proc_status(const struct kinfo_proc *kp) +{ + + assert(kp != NULL); + if(kp == NULL) { + return (SRS_INVALID); + } + + /* + * I'm using the old style flags - they look cleaner to me, + * at least for the purpose of this SNMP table + */ + switch (kp->ki_stat) { + + case SSTOP: + return (SRS_NOT_RUNNABLE); + + case SWAIT: + case SLOCK: + case SSLEEP: + return (SRS_RUNNABLE); + + case SZOMB: + return (SRS_INVALID); + + case SIDL: + case SRUN: + return (SRS_RUNNING); + + default: + syslog(LOG_ERR,"Unknown process state: %d", kp->ki_stat); + return (SRS_INVALID); + } +} + +/** + * Make an SNMP table entry from a kernel one. + */ +static void +kinfo_proc_to_swrun_entry(const struct kinfo_proc *kp, + struct swrun_entry *entry) +{ + char **argv = NULL; + uint64_t cpu_time = 0; + + strlcpy((char*)entry->name, kp->ki_ocomm, sizeof(entry->name)); + + entry->id = oid_zeroDotZero; /* unknown id - FIXME */ + + entry->path[0] = '\0'; + entry->parameters[0] = '\0'; + + assert(hrState_g.kd != NULL); + + argv = kvm_getargv(hr_kd, kp, sizeof(entry->parameters) - 1); + if(argv != NULL){ + memset(entry->parameters, '\0', sizeof(entry->parameters)); + + /* + * FIXME + * Path seems to not be available. + * Try to hack the info in argv[0]; + * this argv is under control of the program so this info + * is not realiable + */ + if(*argv != NULL && (*argv)[0] == '/') { + memset(entry->path, '\0', sizeof(entry->path)); + strlcpy((char*)entry->path, *argv, sizeof(entry->path)); + } + + argv++; /* skip the first one which was used for path */ + + while (argv != NULL && *argv != NULL ) { + if (entry->parameters[0] != 0) { + /* + * add a space between parameters, + * except before the first one + */ + strlcat((char *)entry->parameters, + " ", sizeof(entry->parameters)); + } + strlcat((char *)entry->parameters, *argv, + sizeof(entry->parameters)); + argv++; + } + } + + entry->type = (int32_t)(IS_KERNPROC(kp) ? SRT_OPERATING_SYSTEM : + SRT_APPLICATION); + + entry->status = (int32_t)swrun_OS_get_proc_status(kp); + cpu_time = kp->ki_runtime / 100000; /* centi-seconds */ + + /* may overflow the snmp type */ + entry->perfCPU = (cpu_time > (uint64_t)INT_MAX ? INT_MAX : cpu_time); + entry->perfMemory = kp->ki_size / 1024; /* in kilo-bytes */ + entry->r_tick = get_ticks(); +} + +/** + * Create a table entry for a KLD + */ +static void +kld_file_stat_to_swrun(const struct kld_file_stat *kfs, + struct swrun_entry *entry) +{ + + assert(kfs != NULL); + assert(entry != NULL); + + strlcpy((char *)entry->name, kfs->name, sizeof(entry->name)); + + /* FIXME: can we find the location where the module was loaded from? */ + entry->path[0] = '\0'; + + /* no parameters for kernel files (.ko) of for the kernel */ + entry->parameters[0] = '\0'; + + entry->id = oid_zeroDotZero; /* unknown id - FIXME */ + + if (strcmp(kfs->name, "kernel") == 0) { + entry->type = (int32_t)SRT_OPERATING_SYSTEM; + SWOSIndex = entry->index; + } else { + entry->type = (int32_t)SRT_DEVICE_DRIVER; /* well, not really */ + } + entry->status = (int32_t)SRS_RUNNING; + entry->perfCPU = 0; /* Info not available */ + entry->perfMemory = kfs->size / 1024; /* in kilo-bytes */ + entry->r_tick = get_ticks(); +} + +/** + * Get all visible proceses including the kernel visible threads + */ +static void +swrun_OS_get_procs(void) +{ + struct kinfo_proc *plist, *kp; + int i; + int nproc; + struct swrun_entry *entry; + + plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc); + if (plist == NULL || nproc < 0) { + syslog(LOG_ERR, "kvm_getprocs() failed: %m"); + return; + } + for (i = 0, kp = plist; i < nproc; i++, kp++) { + /* + * The SNMP table's index must begin from 1 (as specified by + * this table definition), the PIDs are starting from 0 + * so we are translating the PIDs to +1 + */ + entry = swrun_entry_find_by_index((int32_t)kp->ki_pid + 1); + if (entry == NULL) { + /* new entry - get memory for it */ + entry = swrun_entry_create((int32_t)kp->ki_pid + 1); + if (entry == NULL) + continue; + } + entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ + + kinfo_proc_to_swrun_entry(kp, entry); + } +} + +/* + * Get kernel items: first the kernel itself, then the loaded modules. + */ +static void +swrun_OS_get_kinfo(void) +{ + int fileid; + struct swrun_entry *entry; + struct kld_file_stat stat; + + for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) { + stat.version = sizeof(struct kld_file_stat); + if (kldstat(fileid, &stat) < 0) { + syslog(LOG_ERR, "kldstat() failed: %m"); + continue; + } + + /* + * kernel and kernel files (*.ko) will be indexed starting with + * NO_PID + 1; NO_PID is PID_MAX + 1 thus it will be no risk to + * overlap with real PIDs which are in range of 1 .. NO_PID + */ + entry = swrun_entry_find_by_index(NO_PID + 1 + stat.id); + if (entry == NULL) { + /* new entry - get memory for it */ + entry = swrun_entry_create(NO_PID + 1 + stat.id); + if (entry == NULL) + continue; + } + entry->flags |= HR_SWRUN_FOUND; /* mark it as found */ + + kld_file_stat_to_swrun(&stat, entry); + } +} + +/** + * Refresh the hrSWRun and hrSWRunPert tables. + */ +static void +refresh_swrun_tbl(void) +{ + + struct swrun_entry *entry, *entry_tmp; + + if (this_tick - swrun_tick < swrun_tbl_refresh) { + HRDBG("no refresh needed "); + return; + } + + /* mark each entry as missing */ + TAILQ_FOREACH(entry, &swrun_tbl, link) + entry->flags &= ~HR_SWRUN_FOUND; + + swrun_OS_get_procs(); + swrun_OS_get_kinfo(); + + /* + * Purge items that disappeared + */ + TAILQ_FOREACH_SAFE(entry, &swrun_tbl, link, entry_tmp) + if (!(entry->flags & HR_SWRUN_FOUND)) + swrun_entry_delete(entry); + + swrun_tick = this_tick; + + HRDBG("refresh DONE"); +} + +/** + * Update the information in this entry + */ +static void +fetch_swrun_entry(struct swrun_entry *entry) +{ + struct kinfo_proc *plist; + int nproc; + struct kld_file_stat stat; + + assert(entry != NULL); + + if (entry->index >= NO_PID + 1) { + /* + * kernel and kernel files (*.ko) will be indexed + * starting with NO_PID + 1; NO_PID is PID_MAX + 1 + * thus it will be no risk to overlap with real PIDs + * which are in range of 1 .. NO_PID + */ + stat.version = sizeof(stat); + if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { + /* + * not found, it's gone. Mark it as invalid for now, it + * will be removed from the list at next global refersh + */ + HRDBG("missing item with kid=%d", + entry->index - NO_PID - 1); + entry->status = (int32_t)SRS_INVALID; + } else + kld_file_stat_to_swrun(&stat, entry); + + } else { + /* this is a process */ + assert(hrState_g.kd != NULL); + plist = kvm_getprocs(hr_kd, KERN_PROC_PID, + entry->index - 1, &nproc); + if (plist == NULL || nproc != 1) { + HRDBG("missing item with PID=%d", entry->index - 1); + entry->status = (int32_t)SRS_INVALID; + } else + kinfo_proc_to_swrun_entry(plist, entry); + } +} + +/** + * Invalidate entry. For KLDs we try to unload it, for processes we SIGKILL it. + */ +static int +invalidate_swrun_entry(struct swrun_entry *entry, int commit) +{ + struct kinfo_proc *plist; + int nproc; + struct kld_file_stat stat; + + assert(entry != NULL); + + if (entry->index >= NO_PID + 1) { + /* this is a kernel item */ + HRDBG("atempt to unload KLD %d", + entry->index - NO_PID - 1); + + if (entry->index == SWOSIndex) { + /* can't invalidate the kernel itself */ + return (SNMP_ERR_NOT_WRITEABLE); + } + + stat.version = sizeof(stat); + if (kldstat(entry->index - NO_PID - 1, &stat) == -1) { + /* + * not found, it's gone. Mark it as invalid for now, it + * will be removed from the list at next global + * refresh + */ + HRDBG("missing item with kid=%d", + entry->index - NO_PID - 1); + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + /* + * There is no way to try to unload a module. There seems + * also no way to find out whether it is busy without unloading + * it. We can assume that it is busy, if the reference count + * is larger than 2, but if it is 1 nothing helps. + */ + if (!commit) { + if (stat.refs > 1) + return (SNMP_ERR_NOT_WRITEABLE); + return (SNMP_ERR_NOERROR); + } + if (kldunload(stat.id) == -1) { + syslog(LOG_ERR,"kldunload for %d/%s failed: %m", + stat.id, stat.name); + if (errno == EBUSY) + return (SNMP_ERR_NOT_WRITEABLE); + else + return (SNMP_ERR_RES_UNAVAIL); + } + } else { + /* this is a process */ + assert(hrState_g.kd != NULL); + + plist = kvm_getprocs(hr_kd, KERN_PROC_PID, + entry->index - 1, &nproc); + if (plist == NULL || nproc != 1) { + HRDBG("missing item with PID=%d", entry->index - 1); + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + if (IS_KERNPROC(plist)) { + /* you don't want to do this */ + return (SNMP_ERR_NOT_WRITEABLE); + } + if (kill(entry->index - 1, commit ? SIGKILL : 0) < 0) { + syslog(LOG_ERR,"kill (%d, SIGKILL) failed: %m", + entry->index - 1); + if (errno == ESRCH) { + /* race: just gone */ + entry->status = (int32_t)SRS_INVALID; + return (SNMP_ERR_NOERROR); + } + return (SNMP_ERR_GENERR); + } + } + return (SNMP_ERR_NOERROR); +} + +/** + * Popuplate the hrSWRunTable. + */ +void +init_swrun_tbl(void) +{ + + refresh_swrun_tbl(); + HRDBG("done"); +} + +/** + * Finalize the hrSWRunTable. + */ +void +fini_swrun_tbl(void) +{ + struct swrun_entry *n1; + + while ((n1 = TAILQ_FIRST(&swrun_tbl)) != NULL) { + TAILQ_REMOVE(&swrun_tbl, n1, link); + free(n1); + } +} + +/* + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It hanldes the SNMP operations for hrSWRunTable + */ +int +op_hrSWRunTable(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + struct swrun_entry *entry; + int ret; + + refresh_swrun_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swrun_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(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NO_CREATION); + + if (entry->r_tick < this_tick) + fetch_swrun_entry(entry); + + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunStatus: + if (value->v.integer != (int32_t)SRS_INVALID) + return (SNMP_ERR_WRONG_VALUE); + + if (entry->status == (int32_t)SRS_INVALID) + return (SNMP_ERR_NOERROR); + + /* + * Here we have a problem with the entire SNMP + * model: if we kill now, we cannot rollback. + * If we kill in the commit code, we cannot + * return an error. Because things may change between + * SET and COMMIT this is impossible to handle + * correctly. + */ + return (invalidate_swrun_entry(entry, 0)); + } + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + return (SNMP_ERR_NOERROR); + + case SNMP_OP_COMMIT: + if ((entry = FIND_OBJECT_INT(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOERROR); + + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunStatus: + if (value->v.integer == (int32_t)SRS_INVALID && + entry->status != (int32_t)SRS_INVALID) + (void)invalidate_swrun_entry(entry, 1); + return (SNMP_ERR_NOERROR); + } + abort(); + } + abort(); + + get: + ret = SNMP_ERR_NOERROR; + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWRunIndex: + value->v.integer = entry->index; + break; + + case LEAF_hrSWRunName: + ret = string_get(value, entry->name, -1); + break; + + case LEAF_hrSWRunID: + value->v.oid = entry->id; + break; + + case LEAF_hrSWRunPath: + ret = string_get(value, entry->path, -1); + break; + + case LEAF_hrSWRunParameters: + ret = string_get(value, entry->parameters, -1); + break; + + case LEAF_hrSWRunType: + value->v.integer = entry->type; + break; + + case LEAF_hrSWRunStatus: + value->v.integer = entry->status; + break; + + default: + abort(); + } + return (ret); +} + +/** + * Scalar(s) in the SWRun group + */ +int +op_hrSWRun(struct snmp_context *ctx __unused, struct snmp_value *value, + u_int sub, u_int iidx __unused, enum snmp_op curr_op) +{ + + /* only SNMP GET is possible */ + switch (curr_op) { + + case SNMP_OP_GET: + goto get; + + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + + case SNMP_OP_ROLLBACK: + case SNMP_OP_COMMIT: + case SNMP_OP_GETNEXT: + abort(); + } + abort(); + + get: + switch (value->var.subs[sub - 1]) { + + case LEAF_hrSWOSIndex: + value->v.uint32 = SWOSIndex; + return (SNMP_ERR_NOERROR); + + default: + abort(); + } +} + +/* + * This is the implementation for a generated (by a SNMP tool) + * function prototype, see hostres_tree.h + * It handles the SNMP operations for hrSWRunPerfTable + */ +int +op_hrSWRunPerfTable(struct snmp_context *ctx __unused, + struct snmp_value *value, u_int sub, u_int iidx __unused, + enum snmp_op curr_op ) +{ + struct swrun_entry *entry; + + refresh_swrun_tbl(); + + switch (curr_op) { + + case SNMP_OP_GETNEXT: + if ((entry = NEXT_OBJECT_INT(&swrun_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(&swrun_tbl, + &value->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + goto get; + + case SNMP_OP_SET: + if ((entry = FIND_OBJECT_INT(&swrun_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_hrSWRunPerfCPU: + value->v.integer = entry->perfCPU; + return (SNMP_ERR_NOERROR); + + case LEAF_hrSWRunPerfMem: + value->v.integer = entry->perfMemory; + return (SNMP_ERR_NOERROR); + } + abort(); +} diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def new file mode 100644 index 000000000000..e92d903f5728 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def @@ -0,0 +1,292 @@ +# +# Copyright (c) 2005-2006 The FreeBSD Project +# All rights reserved. +# +# Author: Victor Cruceru +# +# Redistribution 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 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$ +# +# This is the .def file for both HOST-RESOURCES-MIB and HOST-RESOURCES-TYPES +# + +(1 internet + (2 mgmt + (1 mib_2 + (25 host + (1 hrSystem + (1 hrSystemUptime TIMETICKS op_hrSystem GET) + (2 hrSystemDate OCTETSTRING op_hrSystem GET SET) + (3 hrSystemInitialLoadDevice INTEGER op_hrSystem GET SET) + (4 hrSystemInitialLoadParameters OCTETSTRING op_hrSystem GET SET) + (5 hrSystemNumUsers GAUGE op_hrSystem GET) + (6 hrSystemProcesses GAUGE op_hrSystem GET) + (7 hrSystemMaxProcesses INTEGER op_hrSystem GET) + ) + (2 hrStorage + (1 hrStorageTypes + (1 hrStorageOther + ) + (2 hrStorageRam + ) + (3 hrStorageVirtualMemory + ) + (4 hrStorageFixedDisk + ) + (5 hrStorageRemovableDisk + ) + (6 hrStorageFloppyDisk + ) + (7 hrStorageCompactDisc + ) + (8 hrStorageRamDisk + ) + (9 hrStorageFlashMemory + ) + (10 hrStorageNetworkDisk + ) + ) + (2 hrMemorySize INTEGER op_hrStorage GET) + (3 hrStorageTable + (1 hrStorageEntry : INTEGER op_hrStorageTable + (1 hrStorageIndex INTEGER GET) + (2 hrStorageType OID GET) + (3 hrStorageDescr OCTETSTRING GET) + (4 hrStorageAllocationUnits INTEGER GET) + (5 hrStorageSize INTEGER GET SET) + (6 hrStorageUsed INTEGER GET) + (7 hrStorageAllocationFailures COUNTER GET) + ) + ) + ) + (3 hrDevice + (1 hrDeviceTypes + (1 hrDeviceOther + ) + (2 hrDeviceUnknown + ) + (3 hrDeviceProcessor + ) + (4 hrDeviceNetwork + ) + (5 hrDevicePrinter + ) + (6 hrDeviceDiskStorage + ) + (10 hrDeviceVideo + ) + (11 hrDeviceAudio + ) + (12 hrDeviceCoprocessor + ) + (13 hrDeviceKeyboard + ) + (14 hrDeviceModem + ) + (15 hrDeviceParallelPort + ) + (16 hrDevicePointing + ) + (17 hrDeviceSerialPort + ) + (18 hrDeviceTape + ) + (19 hrDeviceClock + ) + (20 hrDeviceVolatileMemory + ) + (21 hrDeviceNonVolatileMemory + ) + ) + (2 hrDeviceTable + (1 hrDeviceEntry : INTEGER op_hrDeviceTable + (1 hrDeviceIndex INTEGER GET) + (2 hrDeviceType OID GET) + (3 hrDeviceDescr OCTETSTRING GET) + (4 hrDeviceID OID GET) + (5 hrDeviceStatus INTEGER GET) + (6 hrDeviceErrors COUNTER GET) + ) + ) + (3 hrProcessorTable + (1 hrProcessorEntry : INTEGER op_hrProcessorTable + (1 hrProcessorFrwID OID GET) + (2 hrProcessorLoad INTEGER GET) + ) + ) + (4 hrNetworkTable + (1 hrNetworkEntry : INTEGER op_hrNetworkTable + (1 hrNetworkIfIndex INTEGER GET) + ) + ) + (5 hrPrinterTable + (1 hrPrinterEntry : INTEGER op_hrPrinterTable + (1 hrPrinterStatus INTEGER GET) + (2 hrPrinterDetectedErrorState OCTETSTRING GET) + ) + ) + (6 hrDiskStorageTable + (1 hrDiskStorageEntry : INTEGER op_hrDiskStorageTable + (1 hrDiskStorageAccess INTEGER GET) + (2 hrDiskStorageMedia INTEGER GET) + (3 hrDiskStorageRemoveble INTEGER GET) + (4 hrDiskStorageCapacity INTEGER GET) + ) + ) + (7 hrPartitionTable + (1 hrPartitionEntry : INTEGER INTEGER op_hrPartitionTable + (1 hrPartitionIndex INTEGER GET) + (2 hrPartitionLabel OCTETSTRING GET) + (3 hrPartitionID OCTETSTRING GET) + (4 hrPartitionSize INTEGER GET) + (5 hrPartitionFSIndex INTEGER GET) + ) + ) + (8 hrFSTable + (1 hrFSEntry : INTEGER op_hrFSTable + (1 hrFSIndex INTEGER GET) + (2 hrFSMountPoint OCTETSTRING GET) + (3 hrFSRemoteMountPoint OCTETSTRING GET) + (4 hrFSType OID GET) + (5 hrFSAccess INTEGER GET) + (6 hrFSBootable INTEGER GET) + (7 hrFSStorageIndex INTEGER GET) + (8 hrFSLastFullBackupDate OCTETSTRING GET SET) + (9 hrFSLastPartialBackupDate OCTETSTRING GET SET) + ) + ) + (9 hrFSTypes + (1 hrFSOther + ) + (2 hrFSUnknown + ) + (3 hrFSBerkeleyFFS + ) + (4 hrFSSys5FS + ) + (5 hrFSFat + ) + (6 hrFSHPFS + ) + (7 hrFSHFS + ) + (8 hrFSMFS + ) + (9 hrFSNTFS + ) + (10 hrFSVNode + ) + (11 hrFSJournaled + ) + (12 hrFSiso9660 + ) + (13 hrFSRockRidge + ) + (14 hrFSNFS + ) + (15 hrFSNetware + ) + (16 hrFSAFS + ) + (17 hrFSDFS + ) + (18 hrFSAppleshare + ) + (19 hrFSRFS + ) + (20 hrFSDGCFS + ) + (21 hrFSBFS + ) + (22 hrFSFAT32 + ) + (23 hrFSLinuxExt2 + ) + ) + ) + (4 hrSWRun + (1 hrSWOSIndex INTEGER op_hrSWRun GET) + (2 hrSWRunTable + (1 hrSWRunEntry : INTEGER op_hrSWRunTable + (1 hrSWRunIndex INTEGER GET) + (2 hrSWRunName OCTETSTRING GET) + (3 hrSWRunID OID GET) + (4 hrSWRunPath OCTETSTRING GET) + (5 hrSWRunParameters OCTETSTRING GET) + (6 hrSWRunType INTEGER GET) + (7 hrSWRunStatus INTEGER GET SET) + ) + ) + ) + (5 hrSWRunPerf + (1 hrSWRunPerfTable + (1 hrSWRunPerfEntry : INTEGER op_hrSWRunPerfTable + (1 hrSWRunPerfCPU INTEGER GET) + (2 hrSWRunPerfMem INTEGER GET) + ) + ) + ) + (6 hrSWInstalled + (1 hrSWInstalledLastChange TIMETICKS op_hrSWInstalled GET) + (2 hrSWInstalledLastUpdateTime TIMETICKS op_hrSWInstalled GET) + (3 hrSWInstalledTable + (1 hrSWInstalledEntry : INTEGER op_hrSWInstalledTable + (1 hrSWInstalledIndex INTEGER GET) + (2 hrSWInstalledName OCTETSTRING GET) + (3 hrSWInstalledID OID GET) + (4 hrSWInstalledType INTEGER GET) + (5 hrSWInstalledDate OCTETSTRING GET) + ) + ) + ) + (7 hrMIBAdminInfo + (1 hostResourcesMibModule + ) + (2 hrMIBCompliances + ) + (3 hrMIBGroups + ) + ) + ) + ) + ) + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (202 begemotHostres + (1 begemotHostresObjects + (1 begemotHrStorageUpdate TIMETICKS op_begemot GET SET) + (2 begemotHrFSUpdate TIMETICKS op_begemot GET SET) + (3 begemotHrDiskStorageUpdate TIMETICKS op_begemot GET SET) + (4 begemotHrNetworkUpdate TIMETICKS op_begemot GET SET) + (5 begemotHrSWInstalledUpdate TIMETICKS op_begemot GET SET) + (6 begemotHrSWRunUpdate TIMETICKS op_begemot GET SET) + (7 begemotHrPkgDir OCTETSTRING op_begemot GET SET) + ) + ) + ) + ) + ) + ) +) diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 new file mode 100644 index 000000000000..5715fd086cfe --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 @@ -0,0 +1,85 @@ +.\" +.\" Copyright (C) 2005-2006 +.\" The FreeBSD Project. +.\" All rights reserved. +.\" +.\" Author: Harti Brandt +.\" +.\" Redistribution 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 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 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 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$ +.\" +.Dd January 3, 2006 +.Dt snmp_hostres 3 +.Os +.Sh NAME +.Nm snmp_hostres +.Nd "host resources module for snmpd. +.Sh LIBRARY +.Pq begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so" +.Sh DESCRIPTION +The +.Nm snmp_hostres +module implements the HOST-RESOURCES-MIB as standardized in RFC 2790. +.Sh RESTRICTIONS +Not all information in the MIB is meaningful in FreeBSD or is available. +The following variables are not implemented or carry no information: +.Bl -tag -width "XXXXXXXXX" +.It Va hrFSType +There are several types of file systems for which no appropriate OID +exists yet which are supported by FreeBSD. +For smbfs, procfs and devfs +.Va hrFSOther +is returned. +In all other cases +.Va hrFSUnknown . +.It Va hrFSBootable +It is questionable what bootable means here. +Does it mean that the BIOS is available to start a boot on that file system +or does it mean that there is something bootable? +In either case this information isn't available so this variable returns True +for the root file system (which is not necessarily correct) and False for +all others. +.It Va hrFSLastFullBackupDate +.It Va hrFSLastPartialBackupDate +This is not available and always returns an empty string. +Theoretically this could be retrieved from /etc/dumpdates, which would +hardly be correct given the different ways of doing backups. +.It Va hrDiskStorageTable +Floppy devices are currently not reported. +Also the names of the disks are hard-coded in the module. +.El +.Sh FILES +.Bl -tag -width "XXXXXXXXX" +.It Pa /usr/share/snmp/defs/hostres_tree.def +The description of the MIB tree implemented by +.Nm . +.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-TYPES.txt +.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-MIB.txt +.It Pa /usr/share/snmp/mibs/BEGEMOT-HOSTRES-MIB.txt +This is the MIB that is implemented by this module. +.El +.Sh SEE ALSO +.Xr gensnmptree 1 , +.Xr snmpmod 3 +.Sh AUTHORS +.An Victor Cruceru Aq soc-victor@freebsd.org