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 { "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 */ 150 static struct fs_entry * 151 fs_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 */ 220 static void 221 fs_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 */ 241 static struct fs_entry * 242 fs_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 */ 256 void 257 fini_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 */ 278 void 279 fs_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 */ 291 void 292 fs_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 */ 309 void 310 refresh_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 */ 322 const struct asn_oid * 323 fs_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 */ 340 void 341 fs_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 */ 396 int 397 op_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