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