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