hostres_fs_tbl.c revision 160341
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_fs_tbl.c 160341 2006-07-14 09:07:56Z harti $ 30 */ 31 32/* 33 * Host Resources MIB for SNMPd. Implementation for hrFSTable 34 */ 35 36#include <sys/types.h> 37#include <sys/param.h> 38#include <sys/sysctl.h> 39#include <sys/mount.h> 40 41#include <assert.h> 42#include <err.h> 43#include <stdlib.h> 44#include <string.h> 45#include <syslog.h> 46#include <sysexits.h> 47 48#include "hostres_snmp.h" 49#include "hostres_oid.h" 50#include "hostres_tree.h" 51 52/* 53 * File system access enum 54 */ 55enum hrFSAccess { 56 FS_READ_WRITE = 1, 57 FS_READ_ONLY = 2 58}; 59 60/* maximum length (according to MIB) for fs_entry::mountPoint */ 61#define FS_MP_MLEN (128 + 1) 62 63/* maximum length (according to MIB) for fs_entry::remoteMountPoint */ 64#define FS_RMP_MLEN (128 + 1) 65 66/* 67 * This structure is used to hold a SNMP table entry 68 * for HOST-RESOURCES-MIB's hrFSTable 69 */ 70struct fs_entry { 71 int32_t index; 72 u_char *mountPoint; 73 u_char *remoteMountPoint; 74 const struct asn_oid *type; 75 int32_t access; /* enum hrFSAccess, see above */ 76 int32_t bootable; /* TruthValue */ 77 int32_t storageIndex; /* hrStorageTblEntry::index */ 78 u_char lastFullBackupDate[11]; 79 u_char lastPartialBackupDate[11]; 80#define HR_FS_FOUND 0x001 81 uint32_t flags; /* not in mib table, for internal use */ 82 TAILQ_ENTRY(fs_entry) link; 83}; 84TAILQ_HEAD(fs_tbl, fs_entry); 85 86/* 87 * Next structure is used to keep o list of mappings from a specific name 88 * (a_name) to an entry in the hrFSTblEntry. We are trying to keep the same 89 * index for a specific name at least for the duration of one SNMP agent run. 90 */ 91struct fs_map_entry { 92 int32_t hrIndex; /* used for fs_entry::index */ 93 u_char *a_name; /* map key same as fs_entry::mountPoint */ 94 95 /* may be NULL if the respective hrFSTblEntry is (temporally) gone */ 96 struct fs_entry *entry; 97 STAILQ_ENTRY(fs_map_entry) link; 98}; 99STAILQ_HEAD(fs_map, fs_map_entry); 100 101/* head of the list with hrFSTable's entries */ 102static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl); 103 104/* for consistent table indexing */ 105static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map); 106 107/* next index available for hrFSTable */ 108static uint32_t next_fs_index = 1; 109 110/* last tick when hrFSTable was updated */ 111static uint64_t fs_tick; 112 113/* maximum number of ticks between refreshs */ 114uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100; 115 116/* some constants */ 117static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS; 118static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660; 119static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS; 120static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2; 121static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther; 122static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32; 123static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS; 124static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware; 125static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS; 126static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown; 127 128/* file system type map */ 129static const struct { 130 const char *str; /* the type string */ 131 const struct asn_oid *oid; /* the OID to return */ 132} fs_type_map[] = { 133 { "ufs", &OIDX_hrFSBerkeleyFFS_c }, 134 { "cd9660", &OIDX_hrFSiso9660_c }, 135 { "nfs", &OIDX_hrFSNFS_c }, 136 { "ext2fs", &OIDX_hrFSLinuxExt2_c }, 137 { "procfs", &OIDX_hrFSOther_c }, 138 { "devfs", &OIDX_hrFSOther_c }, 139 { "msdosfs", &OIDX_hrFSFAT32_c }, 140 { "ntfs", &OIDX_hrFSNTFS_c }, 141 { "nwfs", &OIDX_hrFSNetware_c }, 142 { "hpfs", &OIDX_hrFSHPFS_c }, 143 { "smbfs", &OIDX_hrFSOther_c }, 144}; 145#define N_FS_TYPE_MAP (sizeof(fs_type_map) / sizeof(fs_type_map[0])) 146 147/** 148 * Create an entry into the FS table and an entry in the map (if needed). 149 */ 150static struct fs_entry * 151fs_entry_create(const char *name) 152{ 153 struct fs_entry *entry; 154 struct fs_map_entry *map; 155 156 assert(name != NULL); 157 assert(strlen(name) > 0); 158 159 STAILQ_FOREACH(map, &fs_map, link) 160 if (strcmp(map->a_name, name) == 0) 161 break; 162 163 if (map == NULL) { 164 size_t mount_point_len; 165 166 /* new object - get a new index */ 167 if (next_fs_index > INT_MAX) { 168 /* Unrecoverable error - die clean and quicly*/ 169 syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__); 170 errx(EX_SOFTWARE, "hrFSTable index wrap"); 171 } 172 173 if ((map = malloc(sizeof(*map))) == NULL) { 174 syslog(LOG_ERR, "%s: %m", __func__); 175 return (NULL); 176 } 177 178 mount_point_len = strlen(name) + 1; 179 if (mount_point_len > FS_MP_MLEN) 180 mount_point_len = FS_MP_MLEN; 181 182 if ((map->a_name = malloc(mount_point_len)) == NULL) { 183 syslog(LOG_ERR, "%s: %m", __func__); 184 free(map); 185 return (NULL); 186 } 187 188 strlcpy(map->a_name, name, mount_point_len); 189 190 map->hrIndex = next_fs_index++; 191 map->entry = NULL; 192 STAILQ_INSERT_TAIL(&fs_map, map, link); 193 194 HRDBG("%s added into hrFSMap at index=%d", name, map->hrIndex); 195 } else { 196 HRDBG("%s exists in hrFSMap index=%d", name, map->hrIndex); 197 } 198 199 if ((entry = malloc(sizeof(*entry))) == NULL) { 200 syslog(LOG_WARNING, "%s: %m", __func__); 201 return (NULL); 202 } 203 204 if ((entry->mountPoint = strdup(name)) == NULL) { 205 syslog(LOG_ERR, "%s: %m", __func__); 206 free(entry); 207 return (NULL); 208 } 209 210 entry->index = map->hrIndex; 211 map->entry = entry; 212 213 INSERT_OBJECT_INT(entry, &fs_tbl); 214 return (entry); 215} 216 217/** 218 * Delete an entry in the FS table. 219 */ 220static void 221fs_entry_delete(struct fs_entry* entry) 222{ 223 struct fs_map_entry *map; 224 225 assert(entry != NULL); 226 227 TAILQ_REMOVE(&fs_tbl, entry, link); 228 STAILQ_FOREACH(map, &fs_map, link) 229 if (map->entry == entry) { 230 map->entry = NULL; 231 break; 232 } 233 free(entry->mountPoint); 234 free(entry->remoteMountPoint); 235 free(entry); 236} 237 238/** 239 * Find a table entry by its name 240 */ 241static struct fs_entry * 242fs_find_by_name(const char *name) 243{ 244 struct fs_entry *entry; 245 246 TAILQ_FOREACH(entry, &fs_tbl, link) 247 if (strcmp(entry->mountPoint, name) == 0) 248 return (entry); 249 250 return (NULL); 251} 252 253/** 254 * Get rid of all data 255 */ 256void 257fini_fs_tbl(void) 258{ 259 struct fs_map_entry *n1; 260 261 while ((n1 = STAILQ_FIRST(&fs_map)) != NULL) { 262 STAILQ_REMOVE_HEAD(&fs_map, link); 263 if (n1->entry != NULL) { 264 TAILQ_REMOVE(&fs_tbl, n1->entry, link); 265 free(n1->entry->mountPoint); 266 free(n1->entry->remoteMountPoint); 267 free(n1->entry); 268 } 269 free(n1->a_name); 270 free(n1); 271 } 272 assert(TAILQ_EMPTY(&fs_tbl)); 273} 274 275/** 276 * Called before the refreshing is started from the storage table. 277 */ 278void 279fs_tbl_pre_refresh(void) 280{ 281 struct fs_entry *entry; 282 283 /* mark each entry as missisng */ 284 TAILQ_FOREACH(entry, &fs_tbl, link) 285 entry->flags &= ~HR_FS_FOUND; 286} 287 288/** 289 * Called after refreshing from the storage table. 290 */ 291void 292fs_tbl_post_refresh(void) 293{ 294 struct fs_entry *entry, *entry_tmp; 295 296 /* 297 * Purge items that disappeared 298 */ 299 TAILQ_FOREACH_SAFE(entry, &fs_tbl, link, entry_tmp) 300 if (!(entry->flags & HR_FS_FOUND)) 301 fs_entry_delete(entry); 302 303 fs_tick = this_tick; 304} 305 306/* 307 * Refresh the FS table. This is done by forcing a refresh of the storage table. 308 */ 309void 310refresh_fs_tbl(void) 311{ 312 313 if (fs_tick == 0 || this_tick - fs_tick >= fs_tbl_refresh) { 314 refresh_storage_tbl(1); 315 HRDBG("refresh DONE"); 316 } 317} 318 319/** 320 * Get the type OID for a given file system 321 */ 322const struct asn_oid * 323fs_get_type(const struct statfs *fs_p) 324{ 325 u_int t; 326 327 assert(fs_p != NULL); 328 329 for (t = 0; t < N_FS_TYPE_MAP; t++) 330 if (strcmp(fs_type_map[t].str, fs_p->f_fstypename) == 0) 331 return (fs_type_map[t].oid); 332 333 return (&OIDX_hrFSUnknown_c); 334} 335 336/* 337 * Given information returned from statfs(2) either create a new entry into 338 * the fs_tbl or refresh the entry if it is already there. 339 */ 340void 341fs_tbl_process_statfs_entry(const struct statfs *fs_p, int32_t storage_idx) 342{ 343 struct fs_entry *entry; 344 345 assert(fs_p != 0); 346 347 HRDBG("for hrStorageEntry::index %d", storage_idx); 348 349 if (fs_p == NULL) 350 return; 351 352 if ((entry = fs_find_by_name(fs_p->f_mntonname)) != NULL || 353 (entry = fs_entry_create(fs_p->f_mntonname)) != NULL) { 354 entry->flags |= HR_FS_FOUND; 355 356 if (!(fs_p->f_flags & MNT_LOCAL)) { 357 /* this is a remote mount */ 358 entry->remoteMountPoint = strdup(fs_p->f_mntfromname); 359 /* if strdup failed, let it be NULL */ 360 361 } else { 362 entry->remoteMountPoint = strdup(""); 363 /* if strdup failed, let it be NULL */ 364 } 365 366 entry->type = fs_get_type(fs_p); 367 368 if ((fs_p->f_flags & MNT_RDONLY) == MNT_RDONLY) 369 entry->access = FS_READ_ONLY; 370 else 371 entry->access = FS_READ_WRITE; 372 373 /* FIXME - bootable fs ?! */ 374 entry->bootable = TRUTH_MK((fs_p->f_flags & MNT_ROOTFS) 375 == MNT_ROOTFS); 376 377 entry->storageIndex = storage_idx; 378 379 /* Info not available */ 380 memset(entry->lastFullBackupDate, 0, 381 sizeof(entry->lastFullBackupDate)); 382 383 /* Info not available */ 384 memset(entry->lastPartialBackupDate, 0, 385 sizeof(entry->lastPartialBackupDate)); 386 387 handle_partition_fs_index(fs_p->f_mntfromname, entry->index); 388 } 389} 390 391/* 392 * This is the implementation for a generated (by our SNMP "compiler" tool) 393 * function prototype, see hostres_tree.h 394 * It handles the SNMP operations for hrFSTable 395 */ 396int 397op_hrFSTable(struct snmp_context *ctx __unused, struct snmp_value *value, 398 u_int sub, u_int iidx __unused, enum snmp_op curr_op) 399{ 400 struct fs_entry *entry; 401 402 refresh_fs_tbl(); 403 404 switch (curr_op) { 405 406 case SNMP_OP_GETNEXT: 407 if ((entry = NEXT_OBJECT_INT(&fs_tbl, 408 &value->var, sub)) == NULL) 409 return (SNMP_ERR_NOSUCHNAME); 410 value->var.len = sub + 1; 411 value->var.subs[sub] = entry->index; 412 goto get; 413 414 case SNMP_OP_GET: 415 if ((entry = FIND_OBJECT_INT(&fs_tbl, 416 &value->var, sub)) == NULL) 417 return (SNMP_ERR_NOSUCHNAME); 418 goto get; 419 420 case SNMP_OP_SET: 421 if ((entry = FIND_OBJECT_INT(&fs_tbl, 422 &value->var, sub)) == NULL) 423 return (SNMP_ERR_NO_CREATION); 424 return (SNMP_ERR_NOT_WRITEABLE); 425 426 case SNMP_OP_ROLLBACK: 427 case SNMP_OP_COMMIT: 428 abort(); 429 } 430 abort(); 431 get: 432 switch (value->var.subs[sub - 1]) { 433 434 case LEAF_hrFSIndex: 435 value->v.integer = entry->index; 436 return (SNMP_ERR_NOERROR); 437 438 case LEAF_hrFSMountPoint: 439 return (string_get(value, entry->mountPoint, -1)); 440 441 case LEAF_hrFSRemoteMountPoint: 442 if (entry->remoteMountPoint == NULL) 443 return (string_get(value, "", -1)); 444 else 445 return (string_get(value, entry->remoteMountPoint, -1)); 446 break; 447 448 case LEAF_hrFSType: 449 assert(entry->type != NULL); 450 value->v.oid = *(entry->type); 451 return (SNMP_ERR_NOERROR); 452 453 case LEAF_hrFSAccess: 454 value->v.integer = entry->access; 455 return (SNMP_ERR_NOERROR); 456 457 case LEAF_hrFSBootable: 458 value->v.integer = entry->bootable; 459 return (SNMP_ERR_NOERROR); 460 461 case LEAF_hrFSStorageIndex: 462 value->v.integer = entry->storageIndex; 463 return (SNMP_ERR_NOERROR); 464 465 case LEAF_hrFSLastFullBackupDate: 466 return (string_get(value, entry->lastFullBackupDate, 8)); 467 468 case LEAF_hrFSLastPartialBackupDate: 469 return (string_get(value, entry->lastPartialBackupDate, 8)); 470 } 471 abort(); 472} 473