Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c (revision 310666) @@ -1,684 +1,684 @@ /*- * 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 #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" #define FREE_DEV_STRUCT(entry_p) do { \ free(entry_p->name); \ free(entry_p->location); \ free(entry_p->descr); \ free(entry_p); \ } while (0) /* * 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 = NULL; struct device_map_entry *map = NULL; size_t name_len; size_t location_len; assert((name[0] != 0) || (location[0] != 0)); if (name[0] == 0 && location[0] == 0) return (NULL); STAILQ_FOREACH(map, &device_map, link) { assert(map->name_key != NULL); assert(map->location_key != NULL); if (strcmp(map->name_key, name) == 0 && strcmp(map->location_key, location) == 0) { break; } } if (map == NULL) { /* new object - get a new index */ if (next_device_index > INT_MAX) { - syslog(LOG_ERR, + syslog(LOG_ERR, "%s: hrDeviceTable index wrap", __func__); /* There isn't much we can do here. * If the next_swins_index is consumed * then we can't add entries to this table * So it is better to exit - if the table is sparsed * at the next agent run we can fill it fully. */ errx(EX_SOFTWARE, "hrDeviceTable index wrap"); /* not reachable */ } if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); return (NULL); } map->entry_p = NULL; name_len = strlen(name) + 1; if (name_len > DEV_NAME_MLEN) name_len = DEV_NAME_MLEN; if ((map->name_key = malloc(name_len)) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); free(map); return (NULL); } location_len = strlen(location) + 1; if (location_len > DEV_LOC_MLEN) location_len = DEV_LOC_MLEN; if ((map->location_key = malloc(location_len )) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); free(map->name_key); free(map); return (NULL); } map->hrIndex = next_device_index++; strlcpy(map->name_key, name, name_len); strlcpy(map->location_key, location, location_len); 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); } if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "hrDeviceTable: %s: %m", __func__); return (NULL); } memset(entry, 0, sizeof(*entry)); entry->index = map->hrIndex; map->entry_p = entry; if ((entry->name = strdup(map->name_key)) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); free(entry); return (NULL); } if ((entry->location = strdup(map->location_key)) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); free(entry->name); free(entry); return (NULL); } /* * From here till the end of this function we reuse name_len * for a different purpose - for device_entry::descr */ if (name[0] != '\0') name_len = strlen(name) + strlen(descr) + strlen(": ") + 1; else name_len = strlen(location) + strlen(descr) + strlen("unknown at : ") + 1; if (name_len > DEV_DESCR_MLEN) name_len = DEV_DESCR_MLEN; if ((entry->descr = malloc(name_len )) == NULL) { syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ ); free(entry->name); free(entry->location); free(entry); return (NULL); } memset(&entry->descr[0], '\0', name_len); if (name[0] != '\0') snprintf(entry->descr, name_len, "%s: %s", name, descr); else snprintf(entry->descr, name_len, "unknown at %s: %s", location, descr); entry->id = &oid_zeroDotZero; /* unknown id - FIXME */ entry->status = (u_int)DS_UNKNOWN; 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. */ 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_DEV_STRUCT(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, const 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 DS_ALIVE: /* probe succeeded */ case DS_NOTPRESENT: /* not probed or probe failed */ return (DS_DOWN); case DS_ATTACHED: /* attach method called */ case DS_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"); again: read_len = read(fd, buf, sizeof(buf)); 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 { if (read_len == sizeof(buf)) goto again; refresh_device_tbl(1); } } /** * 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_DEV_STRUCT(n1->entry_p); } free(n1->name_key); free(n1->location_key); 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: assert(entry->type != NULL); 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(); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c (revision 310666) @@ -1,473 +1,473 @@ /*- * 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 #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 }; /* maximum length (according to MIB) for fs_entry::mountPoint */ #define FS_MP_MLEN (128 + 1) /* maximum length (according to MIB) for fs_entry::remoteMountPoint */ #define FS_RMP_MLEN (128 + 1) /* * 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; u_char *remoteMountPoint; 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 fs_entry::index */ u_char *a_name; /* map key same as fs_entry::mountPoint */ /* 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 }, - { "zfs", &OIDX_hrFSOther_c }, + { "zfs", &OIDX_hrFSOther_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; assert(name != NULL); assert(strlen(name) > 0); STAILQ_FOREACH(map, &fs_map, link) if (strcmp(map->a_name, name) == 0) break; if (map == NULL) { size_t mount_point_len; /* new object - get a new index */ if (next_fs_index > INT_MAX) { /* Unrecoverable error - die clean and quicly*/ - syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__); + syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__); errx(EX_SOFTWARE, "hrFSTable index wrap"); } if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); return (NULL); } mount_point_len = strlen(name) + 1; if (mount_point_len > FS_MP_MLEN) mount_point_len = FS_MP_MLEN; if ((map->a_name = malloc(mount_point_len)) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); free(map); return (NULL); } strlcpy(map->a_name, name, mount_point_len); map->hrIndex = next_fs_index++; map->entry = NULL; 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); } if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); return (NULL); } if ((entry->mountPoint = strdup(name)) == NULL) { syslog(LOG_ERR, "%s: %m", __func__); free(entry); return (NULL); } entry->index = map->hrIndex; map->entry = entry; 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; assert(entry != NULL); TAILQ_REMOVE(&fs_tbl, entry, link); STAILQ_FOREACH(map, &fs_map, link) if (map->entry == entry) { map->entry = NULL; break; } free(entry->mountPoint); free(entry->remoteMountPoint); 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 (strcmp(entry->mountPoint, name) == 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->mountPoint); free(n1->entry->remoteMountPoint); free(n1->entry); } free(n1->a_name); 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; if (!(fs_p->f_flags & MNT_LOCAL)) { /* this is a remote mount */ entry->remoteMountPoint = strdup(fs_p->f_mntfromname); /* if strdup failed, let it be NULL */ } else { entry->remoteMountPoint = strdup(""); /* if strdup failed, let it be NULL */ } 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: if (entry->remoteMountPoint == NULL) return (string_get(value, "", -1)); else return (string_get(value, entry->remoteMountPoint, -1)); break; case LEAF_hrFSType: assert(entry->type != NULL); 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(); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c (revision 310666) @@ -1,630 +1,630 @@ /*- * 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 #include #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" #ifdef PC98 #define HR_FREEBSD_PART_TYPE 0xc494 #else #define HR_FREEBSD_PART_TYPE 165 #endif /* Maximum length for label and id including \0 */ #define PART_STR_MLEN (128 + 1) /* * One row in the hrPartitionTable */ struct partition_entry { asn_subid_t index[2]; u_char *label; /* max allocated len will be PART_STR_MLEN */ u_char *id; /* max allocated len will be PART_STR_MLEN */ int32_t size; int32_t fs_Index; TAILQ_ENTRY(partition_entry) link; #define HR_PARTITION_FOUND 0x001 uint32_t flags; }; TAILQ_HEAD(partition_tbl, partition_entry); /* * This table is used to get a consistent indexing. It saves the name -> index * mapping while we rebuild the partition table. */ struct partition_map_entry { int32_t index; /* partition_entry::index */ u_char *id; /* max allocated len will be PART_STR_MLEN */ /* * next may be NULL if the respective partition_entry * is (temporally) gone. */ struct partition_entry *entry; STAILQ_ENTRY(partition_map_entry) link; }; STAILQ_HEAD(partition_map, partition_map_entry); /* Mapping table for consistent indexing */ static struct partition_map partition_map = STAILQ_HEAD_INITIALIZER(partition_map); /* THE partition table. */ static struct partition_tbl partition_tbl = TAILQ_HEAD_INITIALIZER(partition_tbl); /* next int available for indexing the hrPartitionTable */ static uint32_t next_partition_index = 1; /* * Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK * macro. */ static int partition_entry_cmp(const struct partition_entry *a, const struct partition_entry *b) { assert(a != NULL); assert(b != NULL); if (a->index[0] < b->index[0]) return (-1); if (a->index[0] > b->index[0]) return (+1); if (a->index[1] < b->index[1]) return (-1); if (a->index[1] > b->index[1]) return (+1); return (0); } /* * Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC * macros */ static int partition_idx_cmp(const struct asn_oid *oid, u_int sub, const struct partition_entry *entry) { u_int i; for (i = 0; i < 2 && i < oid->len - sub; i++) { if (oid->subs[sub + i] < entry->index[i]) return (-1); if (oid->subs[sub + i] > entry->index[i]) return (+1); } if (oid->len - sub < 2) return (-1); if (oid->len - sub > 2) return (+1); return (0); } /** * Create a new partition table entry */ static struct partition_entry * partition_entry_create(int32_t ds_index, const char *chunk_name) { struct partition_entry *entry; struct partition_map_entry *map; size_t id_len; /* sanity checks */ assert(chunk_name != NULL); if (chunk_name == NULL || chunk_name[0] == '\0') return (NULL); /* check whether we already have seen this partition */ STAILQ_FOREACH(map, &partition_map, link) if (strcmp(map->id, chunk_name) == 0) break; if (map == NULL) { /* new object - get a new index and create a map */ if (next_partition_index > INT_MAX) { /* Unrecoverable error - die clean and quicly*/ - syslog(LOG_ERR, "%s: hrPartitionTable index wrap", + syslog(LOG_ERR, "%s: hrPartitionTable index wrap", __func__); errx(EX_SOFTWARE, "hrPartitionTable index wrap"); } if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__); return (NULL); } id_len = strlen(chunk_name) + 1; if (id_len > PART_STR_MLEN) id_len = PART_STR_MLEN; if ((map->id = malloc(id_len)) == NULL) { free(map); return (NULL); } map->index = next_partition_index++; strlcpy(map->id, chunk_name, id_len); map->entry = NULL; STAILQ_INSERT_TAIL(&partition_map, map, link); HRDBG("%s added into hrPartitionMap at index=%d", chunk_name, map->index); } else { HRDBG("%s exists in hrPartitionMap index=%d", chunk_name, map->index); } if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__); return (NULL); } memset(entry, 0, sizeof(*entry)); /* create the index */ entry->index[0] = ds_index; entry->index[1] = map->index; map->entry = entry; if ((entry->id = strdup(map->id)) == NULL) { free(entry); return (NULL); } /* * reuse id_len from here till the end of this function * for partition_entry::label */ id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1; if (id_len > PART_STR_MLEN) id_len = PART_STR_MLEN; if ((entry->label = malloc(id_len )) == NULL) { free(entry->id); free(entry); return (NULL); } snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name); INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link, partition_entry_cmp); return (entry); } /** * Delete a partition table entry but keep the map entry intact. */ static void partition_entry_delete(struct partition_entry *entry) { struct partition_map_entry *map; assert(entry != NULL); TAILQ_REMOVE(&partition_tbl, entry, link); STAILQ_FOREACH(map, &partition_map, link) if (map->entry == entry) { map->entry = NULL; break; } free(entry->id); free(entry->label); free(entry); } /** * Find a partition table entry by name. If none is found, return NULL. */ static struct partition_entry * partition_entry_find_by_name(const char *name) { struct partition_entry *entry = NULL; TAILQ_FOREACH(entry, &partition_tbl, link) if (strcmp(entry->id, name) == 0) return (entry); return (NULL); } /** * Find a partition table entry by label. If none is found, return NULL. */ static struct partition_entry * partition_entry_find_by_label(const char *name) { struct partition_entry *entry = NULL; TAILQ_FOREACH(entry, &partition_tbl, link) if (strcmp(entry->label, name) == 0) return (entry); return (NULL); } /** * Process a chunk from libgeom(4). A chunk is either a slice or a partition. * If necessary create a new partition table entry for it. In any case * set the size field of the entry and set the FOUND flag. */ static void handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size) { struct partition_entry *entry; daddr_t k_size; assert(chunk_name != NULL); assert(chunk_name[0] != '\0'); if (chunk_name == NULL || chunk_name == '\0') return; HRDBG("ANALYZE chunk %s", chunk_name); if ((entry = partition_entry_find_by_name(chunk_name)) == NULL) if ((entry = partition_entry_create(ds_index, chunk_name)) == NULL) return; entry->flags |= HR_PARTITION_FOUND; /* actual size may overflow the SNMP type */ k_size = chunk_size / 1024; entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size); } /** * Start refreshing the partition table. A call to this function will * be followed by a call to handleDiskStorage() for every disk, followed * by a single call to the post_refresh function. */ void partition_tbl_pre_refresh(void) { struct partition_entry *entry; /* mark each entry as missing */ TAILQ_FOREACH(entry, &partition_tbl, link) entry->flags &= ~HR_PARTITION_FOUND; } /** * Try to find a geom(4) class by its name. Returns a pointer to that * class if found NULL otherways. */ static struct gclass * find_class(struct gmesh *mesh, const char *name) { struct gclass *classp; LIST_FOREACH(classp, &mesh->lg_class, lg_class) if (strcmp(classp->lg_name, name) == 0) return (classp); return (NULL); } /** * Process all MBR-type partitions from the given disk. */ static void get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name) { struct ggeom *gp; struct gprovider *pp; struct gconfig *conf; long part_type; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { /* We are only interested in partitions from this disk */ if (strcmp(gp->lg_name, disk_dev_name) != 0) continue; /* * Find all the non-BSD providers (these are handled in get_bsd) */ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { LIST_FOREACH(conf, &pp->lg_config, lg_config) { if (conf->lg_name == NULL || conf->lg_val == NULL || strcmp(conf->lg_name, "type") != 0) continue; /* * We are not interested in BSD partitions * (ie ad0s1 is not interesting at this point). * We'll take care of them in detail (slice * by slice) in get_bsd. */ part_type = strtol(conf->lg_val, NULL, 10); if (part_type == HR_FREEBSD_PART_TYPE) break; HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name); HRDBG("Mediasize: %jd", (intmax_t)pp->lg_mediasize / 1024); HRDBG("Sectorsize: %u", pp->lg_sectorsize); HRDBG("Mode: %s", pp->lg_mode); HRDBG("CONFIG: %s: %s", conf->lg_name, conf->lg_val); handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize); } } } } /** * Process all BSD-type partitions from the given disk. */ static void get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name) { struct ggeom *gp; struct gprovider *pp; LIST_FOREACH(gp, &classp->lg_geom, lg_geom) { /* * We are only interested in those geoms starting with * the disk_dev_name passed as parameter to this function. */ if (strncmp(gp->lg_name, disk_dev_name, strlen(disk_dev_name)) != 0) continue; LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { if (pp->lg_name == NULL) continue; handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize); } } } /** * Called from the DiskStorage table for every row. Open the GEOM(4) framework * and process all the partitions in it. * ds_index is the index into the DiskStorage table. * This is done in two steps: for non BSD partitions the geom class "MBR" is * used, for our BSD slices the "BSD" geom class. */ void partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name) { struct gmesh mesh; /* GEOM userland tree */ struct gclass *classp; int error; assert(disk_dev_name != NULL); assert(ds_index > 0); HRDBG("===> getting partitions for %s <===", disk_dev_name); /* try to construct the GEOM tree */ if ((error = geom_gettree(&mesh)) != 0) { syslog(LOG_WARNING, "cannot get GEOM tree: %m"); return; } /* * First try the GEOM "MBR" class. * This is needed for non-BSD slices (aka partitions) * on PC architectures. */ if ((classp = find_class(&mesh, "MBR")) != NULL) { get_mbr(classp, ds_index, disk_dev_name); } else { HRDBG("cannot find \"MBR\" geom class"); } /* * Get the "BSD" GEOM class. * Here we'll find all the info needed about the BSD slices. */ if ((classp = find_class(&mesh, "BSD")) != NULL) { get_bsd_sun(classp, ds_index, disk_dev_name); } else { /* no problem on sparc64 */ HRDBG("cannot find \"BSD\" geom class"); } /* * Get the "SUN" GEOM class. * Here we'll find all the info needed about the BSD slices. */ if ((classp = find_class(&mesh, "SUN")) != NULL) { get_bsd_sun(classp, ds_index, disk_dev_name); } else { /* no problem on i386 */ HRDBG("cannot find \"SUN\" geom class"); } geom_deletetree(&mesh); } /** * Finish refreshing the table. */ void partition_tbl_post_refresh(void) { struct partition_entry *e, *etmp; /* * Purge items that disappeared */ TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp) if (!(e->flags & HR_PARTITION_FOUND)) partition_entry_delete(e); } /* * Finalization routine for hrPartitionTable * It destroys the lists and frees any allocated heap memory */ void fini_partition_tbl(void) { struct partition_map_entry *m; while ((m = STAILQ_FIRST(&partition_map)) != NULL) { STAILQ_REMOVE_HEAD(&partition_map, link); if(m->entry != NULL) { TAILQ_REMOVE(&partition_tbl, m->entry, link); free(m->entry->id); free(m->entry->label); free(m->entry); } free(m->id); free(m); } assert(TAILQ_EMPTY(&partition_tbl)); } /** * Called from the file system code to insert the file system table index * into the partition table entry. Note, that an partition table entry exists * only for local file systems. */ void handle_partition_fs_index(const char *name, int32_t fs_idx) { struct partition_entry *entry; if ((entry = partition_entry_find_by_label(name)) == NULL) { HRDBG("%s IS MISSING from hrPartitionTable", name); return; } HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx); entry->fs_Index = fs_idx; } /* * This is the implementation for a generated (by our SNMP tool) * function prototype, see hostres_tree.h * It handles the SNMP operations for hrPartitionTable */ int op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { struct partition_entry *entry; /* * Refresh the disk storage table (which refreshes the partition * table) if necessary. */ refresh_disk_storage_tbl(0); switch (op) { case SNMP_OP_GETNEXT: if ((entry = NEXT_OBJECT_FUNC(&partition_tbl, &value->var, sub, partition_idx_cmp)) == NULL) return (SNMP_ERR_NOSUCHNAME); value->var.len = sub + 2; value->var.subs[sub] = entry->index[0]; value->var.subs[sub + 1] = entry->index[1]; goto get; case SNMP_OP_GET: if ((entry = FIND_OBJECT_FUNC(&partition_tbl, &value->var, sub, partition_idx_cmp)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto get; case SNMP_OP_SET: if ((entry = FIND_OBJECT_FUNC(&partition_tbl, &value->var, sub, partition_idx_cmp)) == NULL) return (SNMP_ERR_NOT_WRITEABLE); return (SNMP_ERR_NO_CREATION); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } abort(); get: switch (value->var.subs[sub - 1]) { case LEAF_hrPartitionIndex: value->v.integer = entry->index[1]; return (SNMP_ERR_NOERROR); case LEAF_hrPartitionLabel: return (string_get(value, entry->label, -1)); case LEAF_hrPartitionID: return(string_get(value, entry->id, -1)); case LEAF_hrPartitionSize: value->v.integer = entry->size; return (SNMP_ERR_NOERROR); case LEAF_hrPartitionFSIndex: value->v.integer = entry->fs_Index; return (SNMP_ERR_NOERROR); } abort(); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c (revision 310666) @@ -1,492 +1,492 @@ /*- * 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 "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" /* 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); } /** * 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: {%lld, %ld}", (long long)kernel_boot_timestamp.tv_sec, (long)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 occurred. The returned data is a * pointer to a global storage. */ 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)) { + 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 utmpx *utmp; setutxent(); *nu = 0; while ((utmp = getutxent()) != NULL) { if (utmp->ut_type == USER_PROCESS) (*nu)++; } endutxent(); 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_PROC, 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); + 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: + 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(); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c (revision 310666) @@ -1,208 +1,208 @@ /*- * 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 "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."); + 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 */ + 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)(labs(tm->tm_gmtoff) / 3600); str[10] = (u_char)((labs(tm->tm_gmtoff) % 3600) / 60); return (11); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c (revision 310666) @@ -1,662 +1,662 @@ /*- * 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 #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" /* maximum length for descritpion string according to MIB */ #define SE_DESC_MLEN (255 + 1) /* * This structure is used to hold a SNMP table entry * for HOST-RESOURCES-MIB's hrStorageTable */ struct storage_entry { int32_t index; const struct asn_oid *type; u_char *descr; 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 storage_entry::index */ /* map key, also used for storage_entry::descr */ u_char *a_name; /* * next may be NULL if the respective storage_entry * 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 necessary, 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; size_t name_len; assert(name != NULL); assert(strlen(name) > 0); STAILQ_FOREACH(map, &storage_map, link) if (strcmp(map->a_name, name) == 0) break; if (map == NULL) { /* new object - get a new index */ if (next_storage_index > INT_MAX) { - syslog(LOG_ERR, + syslog(LOG_ERR, "%s: hrStorageTable index wrap", __func__); errx(EX_SOFTWARE, "hrStorageTable index wrap"); } if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ ); return (NULL); } name_len = strlen(name) + 1; if (name_len > SE_DESC_MLEN) name_len = SE_DESC_MLEN; if ((map->a_name = malloc(name_len)) == NULL) { free(map); return (NULL); } strlcpy(map->a_name, name, name_len); map->hrIndex = next_storage_index++; 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); } if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); return (NULL); } - memset(entry, 0, sizeof(*entry)); + memset(entry, 0, sizeof(*entry)); entry->index = map->hrIndex; if ((entry->descr = strdup(map->a_name)) == NULL) { free(entry); return (NULL); } map->entry = entry; 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->descr); 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 (strcmp(entry->descr, name) == 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[SE_DESC_MLEN]; 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[SE_DESC_MLEN]; + size_t len = sizeof(nswapdev); + int nswapdev = 0; 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 size, used; int i, mounted_fs_count, units; char fs_string[SE_DESC_MLEN]; 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]); /*XXX - This is wrong*/ units = fs_buf[i].f_bsize; size = fs_buf[i].f_blocks; used = fs_buf[i].f_blocks - fs_buf[i].f_bfree; while (size > INT_MAX) { units <<= 1; size >>= 1; used >>= 1; } entry->allocationUnits = units; entry->size = size; entry->used = used; 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->descr); free(n1->entry); } free(n1->a_name); 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: assert(entry->type != NULL); value->v.oid = *entry->type; return (SNMP_ERR_NOERROR); case LEAF_hrStorageDescr: assert(entry->descr != NULL); 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(); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c (revision 310665) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c (revision 310666) @@ -1,555 +1,555 @@ /* * 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 #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" -#define CONTENTS_FNAME "+CONTENTS" +#define CONTENTS_FNAME "+CONTENTS" enum SWInstalledType { SWI_UNKNOWN = 1, SWI_OPERATING_SYSTEM = 2, SWI_DEVICE_DRIVER = 3, SWI_APPLICATION = 4 }; #define SW_NAME_MLEN (64 + 1) /* * 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; /* max len for this is SW_NAME_MLEN */ const 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; /* swins_entry::index */ u_char *name; /* map key,a copy of swins_entry::name*/ /* * 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; STAILQ_FOREACH(map, &swins_map, link) if (strcmp((const char *)map->name, name) == 0) break; if (map == NULL) { size_t name_len; /* new object - get a new index */ if (next_swins_index > INT_MAX) { - syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap", + syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap", __func__ ); /* There isn't much we can do here. * If the next_swins_index is consumed * then we can't add entries to this table * So it is better to exit - if the table is sparsed * at the next agent run we can fill it fully. */ errx(EX_SOFTWARE, "hrSWInstalledTable index wrap"); } if ((map = malloc(sizeof(*map))) == NULL) { syslog(LOG_ERR, "%s: %m", __func__ ); return (NULL); } name_len = strlen(name) + 1; if (name_len > SW_NAME_MLEN) name_len = SW_NAME_MLEN; if ((map->name = malloc(name_len)) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); free(map); return (NULL); } map->index = next_swins_index++; strlcpy((char *)map->name, name, name_len); STAILQ_INSERT_TAIL(&swins_map, map, link); HRDBG("%s added into hrSWInstalled at %d", name, map->index); } if ((entry = malloc(sizeof(*entry))) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); return (NULL); } memset(entry, 0, sizeof(*entry)); if ((entry->name = strdup(map->name)) == NULL) { syslog(LOG_WARNING, "%s: %m", __func__); free(entry); return (NULL); } 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->name); 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 (strcmp((const char*)entry->name, name) == 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->name); free(n1->entry); } free(n1->name); 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[SW_NAME_MLEN] = ""; struct swins_entry *entry; u_char *boot; struct stat sb; struct tm k_ts; if (uname(&os_id) == -1) { syslog(LOG_WARNING, "%s: %m", __func__); 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; + 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) { + 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: assert(entry->id != NULL); 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(); } }