hostres_storage_tbl.c revision 154137
1/*- 2 * Copyright (c) 2005-2006 The FreeBSD Project 3 * All rights reserved. 4 * 5 * Author: Victor Cruceru <soc-victor@freebsd.org> 6 * 7 * Redistribution of this software and documentation and use in source and 8 * binary forms, with or without modification, are permitted provided that 9 * the following conditions are met: 10 * 11 * 1. Redistributions of source code or documentation must retain the above 12 * copyright notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c 154133 2006-01-09 12:33:45Z harti $ 30 */ 31 32/* 33 * Host Resources MIB for SNMPd. Implementation for hrStorageTable 34 */ 35 36#include <sys/types.h> 37#include <sys/param.h> 38#include <sys/sysctl.h> 39#include <sys/vmmeter.h> 40#include <sys/mount.h> 41 42#include <vm/vm_param.h> 43 44#include <assert.h> 45#include <err.h> 46#include <limits.h> 47#include <memstat.h> 48#include <paths.h> 49#include <stdlib.h> 50#include <string.h> 51#include <syslog.h> 52#include <unistd.h> /*for getpagesize()*/ 53 54#include "hostres_snmp.h" 55#include "hostres_oid.h" 56#include "hostres_tree.h" 57 58/* 59 * This structure is used to hold a SNMP table entry 60 * for HOST-RESOURCES-MIB's hrStorageTable 61 */ 62struct storage_entry { 63 int32_t index; 64 struct asn_oid type; 65 u_char descr[255 + 1]; 66 int32_t allocationUnits; 67 int32_t size; 68 int32_t used; 69 uint32_t allocationFailures; 70#define HR_STORAGE_FOUND 0x001 71 uint32_t flags; /* to be used internally*/ 72 TAILQ_ENTRY(storage_entry) link; 73}; 74TAILQ_HEAD(storage_tbl, storage_entry); 75 76/* 77 * Next structure is used to keep o list of mappings from a specific name 78 * (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the 79 * same index for a specific name at least for the duration of one SNMP agent 80 * run. 81 */ 82struct storage_map_entry { 83 int32_t hrIndex; /* used for hrStorageTblEntry::index */ 84 85 /* map key, also used for hrStorageTblEntry::descr */ 86 u_char a_name[255 + 1]; 87 88 /* 89 * next may be NULL if the respective hrStorageTblEntry 90 * is (temporally) gone 91 */ 92 struct storage_entry *entry; 93 STAILQ_ENTRY(storage_map_entry) link; 94}; 95STAILQ_HEAD(storage_map, storage_map_entry); 96 97/* the head of the list with table's entries */ 98static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl); 99 100/*for consistent table indexing*/ 101static struct storage_map storage_map = 102 STAILQ_HEAD_INITIALIZER(storage_map); 103 104/* last (agent) tick when hrStorageTable was updated */ 105static uint64_t storage_tick; 106 107/* maximum number of ticks between two refreshs */ 108uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100; 109 110/* for kvm_getswapinfo, malloc'd */ 111static struct kvm_swap *swap_devs; 112static size_t swap_devs_len; /* item count for swap_devs */ 113 114/* for getfsstat, malloc'd */ 115static struct statfs *fs_buf; 116static size_t fs_buf_count; /* item count for fs_buf */ 117 118static struct vmtotal mem_stats; 119 120/* next int available for indexing the hrStorageTable */ 121static uint32_t next_storage_index = 1; 122 123/* start of list for memory detailed stats */ 124static struct memory_type_list *mt_list; 125 126/* Constants */ 127static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam; 128static const struct asn_oid OIDX_hrStorageVirtualMemory_c = 129 OIDX_hrStorageVirtualMemory; 130 131/** 132 * Create a new entry into the storage table and, if neccessary, an 133 * entry into the storage map. 134 */ 135static struct storage_entry * 136storage_entry_create(const char *name) 137{ 138 struct storage_entry *entry; 139 struct storage_map_entry *map; 140 141 if ((entry = malloc(sizeof(*entry))) == NULL) { 142 syslog(LOG_WARNING, "%s: %m", __func__); 143 return (NULL); 144 } 145 146 strlcpy(entry->descr, name, sizeof(entry->descr)); 147 148 STAILQ_FOREACH(map, &storage_map, link) 149 if (strcmp(map->a_name, entry->descr) == 0) { 150 entry->index = map->hrIndex; 151 map->entry = entry; 152 break; 153 } 154 155 if (map == NULL) { 156 /* new object - get a new index */ 157 if (next_storage_index > INT_MAX) { 158 syslog(LOG_ERR, 159 "%s: hrStorageTable index wrap", __func__); 160 free(entry); 161 return (NULL); 162 } 163 164 if ((map = malloc(sizeof(*map))) == NULL) { 165 syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ ); 166 free(entry); 167 return (NULL); 168 } 169 170 map->hrIndex = next_storage_index ++; 171 strlcpy(map->a_name, entry->descr, sizeof(map->a_name)); 172 map->entry = entry; 173 174 STAILQ_INSERT_TAIL(&storage_map, map, link); 175 176 HRDBG("%s added into hrStorageMap at index=%d", 177 name, map->hrIndex); 178 } else { 179 HRDBG("%s exists in hrStorageMap index=%d\n", 180 name, map->hrIndex); 181 } 182 183 entry->index = map->hrIndex; 184 185 INSERT_OBJECT_INT(entry, &storage_tbl); 186 187 return (entry); 188} 189 190/** 191 * Delete an entry from the storage table. 192 */ 193static void 194storage_entry_delete(struct storage_entry *entry) 195{ 196 struct storage_map_entry *map; 197 198 assert(entry != NULL); 199 200 TAILQ_REMOVE(&storage_tbl, entry, link); 201 STAILQ_FOREACH(map, &storage_map, link) 202 if (map->entry == entry) { 203 map->entry = NULL; 204 break; 205 } 206 207 free(entry); 208} 209 210/** 211 * Find a table entry by its name. 212 */ 213static struct storage_entry * 214storage_find_by_name(const char *name) 215{ 216 struct storage_entry *entry; 217 218 TAILQ_FOREACH(entry, &storage_tbl, link) 219 if (strncmp(entry->descr, name, 220 sizeof(entry->descr) - 1) == 0) 221 return (entry); 222 223 return (NULL); 224} 225 226/* 227 * VM info. 228 */ 229static void 230storage_OS_get_vm(void) 231{ 232 int mib[2] = { CTL_VM, VM_TOTAL }; 233 size_t len = sizeof(mem_stats); 234 int page_size_bytes; 235 struct storage_entry *entry; 236 237 if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) { 238 syslog(LOG_ERR, 239 "hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) " 240 "failed: %m", __func__); 241 assert(0); 242 return; 243 } 244 245 page_size_bytes = getpagesize(); 246 247 /* Real Memory Metrics */ 248 if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL && 249 (entry = storage_entry_create("Real Memory Metrics")) == NULL) 250 return; /* I'm out of luck now, maybe next time */ 251 252 entry->flags |= HR_STORAGE_FOUND; 253 entry->type = OIDX_hrStorageRam_c; 254 entry->allocationUnits = page_size_bytes; 255 entry->size = mem_stats.t_rm; 256 entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */ 257 entry->allocationFailures = 0; 258 259 /* Shared Real Memory Metrics */ 260 if ((entry = storage_find_by_name("Shared Real Memory Metrics")) == 261 NULL && 262 (entry = storage_entry_create("Shared Real Memory Metrics")) == 263 NULL) 264 return; 265 266 entry->flags |= HR_STORAGE_FOUND; 267 entry->type = OIDX_hrStorageRam_c; 268 entry->allocationUnits = page_size_bytes; 269 entry->size = mem_stats.t_rmshr; 270 /* ACTIVE is not USED - FIXME */ 271 entry->used = mem_stats.t_armshr; 272 entry->allocationFailures = 0; 273} 274 275static void 276storage_OS_get_memstat(void) 277{ 278 struct memory_type *mt_item; 279 struct storage_entry *entry; 280 281 if (mt_list == NULL) { 282 if ((mt_list = memstat_mtl_alloc()) == NULL) 283 /* again? we have a serious problem */ 284 return; 285 } 286 287 if (memstat_sysctl_all(mt_list, 0) < 0) { 288 syslog(LOG_ERR, "memstat_sysctl_all failed: %s", 289 memstat_strerror(memstat_mtl_geterror(mt_list)) ); 290 return; 291 } 292 293 if ((mt_item = memstat_mtl_first(mt_list)) == NULL) { 294 /* usually this is not an error, no errno for this failure*/ 295 HRDBG("memstat_mtl_first failed"); 296 return; 297 } 298 299 do { 300 const char *memstat_name; 301 uint64_t tmp_size; 302 int allocator; 303 char alloc_descr[255 + 1]; 304 305 memstat_name = memstat_get_name(mt_item); 306 307 if (memstat_name == NULL || strlen(memstat_name) == 0) 308 continue; 309 310 switch (allocator = memstat_get_allocator(mt_item)) { 311 312 case ALLOCATOR_MALLOC: 313 snprintf(alloc_descr, sizeof(alloc_descr), 314 "MALLOC: %s", memstat_name); 315 break; 316 317 case ALLOCATOR_UMA: 318 snprintf(alloc_descr, sizeof(alloc_descr), 319 "UMA: %s", memstat_name); 320 break; 321 322 default: 323 snprintf(alloc_descr, sizeof(alloc_descr), 324 "UNKNOWN%d: %s", allocator, memstat_name); 325 break; 326 } 327 328 if ((entry = storage_find_by_name(alloc_descr)) == NULL && 329 (entry = storage_entry_create(alloc_descr)) == NULL) 330 return; 331 332 entry->flags |= HR_STORAGE_FOUND; 333 entry->type = OIDX_hrStorageRam_c; 334 335 if ((tmp_size = memstat_get_size(mt_item)) == 0) 336 tmp_size = memstat_get_sizemask(mt_item); 337 entry->allocationUnits = 338 (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); 339 340 tmp_size = memstat_get_countlimit(mt_item); 341 entry->size = 342 (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); 343 344 tmp_size = memstat_get_count(mt_item); 345 entry->used = 346 (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); 347 348 tmp_size = memstat_get_failures(mt_item); 349 entry->allocationFailures = 350 (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size); 351 352 } while((mt_item = memstat_mtl_next(mt_item)) != NULL); 353} 354 355/** 356 * Get swap info 357 */ 358static void 359storage_OS_get_swap(void) 360{ 361 int nswapdev = 0; 362 size_t len = sizeof(nswapdev); 363 struct storage_entry *entry; 364 char swap_w_prefix[255 + 1]; 365 366 if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) { 367 syslog(LOG_ERR, 368 "hrStorageTable: sysctlbyname(\"vm.nswapdev\") " 369 "failed. %m"); 370 assert(0); 371 return; 372 } 373 374 if (nswapdev <= 0) { 375 HRDBG("vm.nswapdev is %d", nswapdev); 376 return; 377 } 378 379 if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) { 380 swap_devs_len = nswapdev + 1; 381 swap_devs = reallocf(swap_devs, 382 swap_devs_len * sizeof(struct kvm_swap)); 383 384 assert(swap_devs != NULL); 385 if (swap_devs == NULL) { 386 swap_devs_len = 0; 387 return; 388 } 389 } 390 391 nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0); 392 if (nswapdev < 0) { 393 syslog(LOG_ERR, 394 "hrStorageTable: kvm_getswapinfo failed. %m\n"); 395 assert(0); 396 return; 397 } 398 399 for (len = 0; len < (size_t)nswapdev; len++) { 400 memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix)); 401 snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1, 402 "Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname); 403 404 entry = storage_find_by_name(swap_w_prefix); 405 if (entry == NULL) 406 entry = storage_entry_create(swap_w_prefix); 407 408 assert (entry != NULL); 409 if (entry == NULL) 410 return; /* Out of luck */ 411 412 entry->flags |= HR_STORAGE_FOUND; 413 entry->type = OIDX_hrStorageVirtualMemory_c; 414 entry->allocationUnits = getpagesize(); 415 entry->size = swap_devs[len].ksw_total; 416 entry->used = swap_devs[len].ksw_used; 417 entry->allocationFailures = 0; 418 } 419} 420 421/** 422 * Query the underlaying OS for the mounted file systems 423 * anf fill in the respective lists (for hrStorageTable and for hrFSTable) 424 */ 425static void 426storage_OS_get_fs(void) 427{ 428 struct storage_entry *entry; 429 uint64_t used_blocks_count = 0; 430 char fs_string[255+1]; 431 int mounted_fs_count; 432 int i = 0; 433 434 if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) { 435 syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); 436 return; /* out of luck this time */ 437 } 438 439 if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) { 440 fs_buf_count = mounted_fs_count; 441 fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs)); 442 if (fs_buf == NULL) { 443 fs_buf_count = 0; 444 assert(0); 445 return; 446 } 447 } 448 449 if ((mounted_fs_count = getfsstat(fs_buf, 450 fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) { 451 syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m"); 452 return; /* out of luck this time */ 453 } 454 455 HRDBG("got %d mounted FS", mounted_fs_count); 456 457 fs_tbl_pre_refresh(); 458 459 for (i = 0; i < mounted_fs_count; i++) { 460 snprintf(fs_string, sizeof(fs_string), 461 "%s, type: %s, dev: %s", fs_buf[i].f_mntonname, 462 fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname); 463 464 entry = storage_find_by_name(fs_string); 465 if (entry == NULL) 466 entry = storage_entry_create(fs_string); 467 468 assert (entry != NULL); 469 if (entry == NULL) 470 return; /* Out of luck */ 471 472 entry->flags |= HR_STORAGE_FOUND; 473 entry->type = *fs_get_type(&fs_buf[i]); 474 475 if (fs_buf[i].f_bsize > INT_MAX) 476 entry->allocationUnits = INT_MAX; 477 else 478 entry->allocationUnits = fs_buf[i].f_bsize; 479 480 if (fs_buf[i].f_blocks > INT_MAX) 481 entry->size = INT_MAX; 482 else 483 entry->size = fs_buf[i].f_blocks; 484 485 used_blocks_count = fs_buf[i].f_blocks - fs_buf[i].f_bfree; 486 487 if (used_blocks_count > INT_MAX) 488 entry->used = INT_MAX; 489 else 490 entry->used = used_blocks_count; 491 492 entry->allocationFailures = 0; 493 494 /* take care of hrFSTable */ 495 fs_tbl_process_statfs_entry(&fs_buf[i], entry->index); 496 } 497 498 fs_tbl_post_refresh(); 499} 500 501/** 502 * Initialize storage table and populate it. 503 */ 504void 505init_storage_tbl(void) 506{ 507 if ((mt_list = memstat_mtl_alloc()) == NULL) 508 syslog(LOG_ERR, 509 "hrStorageTable: memstat_mtl_alloc() failed: %m"); 510 511 refresh_storage_tbl(1); 512} 513 514void 515fini_storage_tbl(void) 516{ 517 struct storage_map_entry *n1; 518 519 if (swap_devs != NULL) { 520 free(swap_devs); 521 swap_devs = NULL; 522 } 523 swap_devs_len = 0; 524 525 if (fs_buf != NULL) { 526 free(fs_buf); 527 fs_buf = NULL; 528 } 529 fs_buf_count = 0; 530 531 while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) { 532 STAILQ_REMOVE_HEAD(&storage_map, link); 533 if (n1->entry != NULL) { 534 TAILQ_REMOVE(&storage_tbl, n1->entry, link); 535 free(n1->entry); 536 } 537 free(n1); 538 } 539 assert(TAILQ_EMPTY(&storage_tbl)); 540} 541 542void 543refresh_storage_tbl(int force) 544{ 545 struct storage_entry *entry, *entry_tmp; 546 547 if (!force && storage_tick != 0 && 548 this_tick - storage_tick < storage_tbl_refresh) { 549 HRDBG("no refresh needed"); 550 return; 551 } 552 553 /* mark each entry as missing */ 554 TAILQ_FOREACH(entry, &storage_tbl, link) 555 entry->flags &= ~HR_STORAGE_FOUND; 556 557 storage_OS_get_vm(); 558 storage_OS_get_swap(); 559 storage_OS_get_fs(); 560 storage_OS_get_memstat(); 561 562 /* 563 * Purge items that disappeared 564 */ 565 TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp) 566 if (!(entry->flags & HR_STORAGE_FOUND)) 567 storage_entry_delete(entry); 568 569 storage_tick = this_tick; 570 571 HRDBG("refresh DONE"); 572} 573 574/* 575 * This is the implementation for a generated (by our SNMP tool) 576 * function prototype, see hostres_tree.h 577 * It handles the SNMP operations for hrStorageTable 578 */ 579int 580op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value, 581 u_int sub, u_int iidx __unused, enum snmp_op curr_op) 582{ 583 struct storage_entry *entry; 584 585 refresh_storage_tbl(0); 586 587 switch (curr_op) { 588 589 case SNMP_OP_GETNEXT: 590 if ((entry = NEXT_OBJECT_INT(&storage_tbl, 591 &value->var, sub)) == NULL) 592 return (SNMP_ERR_NOSUCHNAME); 593 594 value->var.len = sub + 1; 595 value->var.subs[sub] = entry->index; 596 goto get; 597 598 case SNMP_OP_GET: 599 if ((entry = FIND_OBJECT_INT(&storage_tbl, 600 &value->var, sub)) == NULL) 601 return (SNMP_ERR_NOSUCHNAME); 602 goto get; 603 604 case SNMP_OP_SET: 605 if ((entry = FIND_OBJECT_INT(&storage_tbl, 606 &value->var, sub)) == NULL) 607 return (SNMP_ERR_NO_CREATION); 608 return (SNMP_ERR_NOT_WRITEABLE); 609 610 case SNMP_OP_ROLLBACK: 611 case SNMP_OP_COMMIT: 612 abort(); 613 } 614 abort(); 615 616 get: 617 switch (value->var.subs[sub - 1]) { 618 619 case LEAF_hrStorageIndex: 620 value->v.integer = entry->index; 621 return (SNMP_ERR_NOERROR); 622 623 case LEAF_hrStorageType: 624 value->v.oid = entry->type; 625 return (SNMP_ERR_NOERROR); 626 627 case LEAF_hrStorageDescr: 628 return (string_get(value, entry->descr, -1)); 629 break; 630 631 case LEAF_hrStorageAllocationUnits: 632 value->v.integer = entry->allocationUnits; 633 return (SNMP_ERR_NOERROR); 634 635 case LEAF_hrStorageSize: 636 value->v.integer = entry->size; 637 return (SNMP_ERR_NOERROR); 638 639 case LEAF_hrStorageUsed: 640 value->v.integer = entry->used; 641 return (SNMP_ERR_NOERROR); 642 643 case LEAF_hrStorageAllocationFailures: 644 value->v.uint32 = entry->allocationFailures; 645 return (SNMP_ERR_NOERROR); 646 } 647 abort(); 648} 649