2 * Copyright (c) 2005-2006 The FreeBSD Project
5 * Author: Victor Cruceru <soc-victor@freebsd.org>
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:
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.
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
33 * Host Resources MIB: hrPartitionTable implementation for SNMPd.
36 #include <sys/types.h>
37 #include <sys/limits.h>
49 #include "hostres_snmp.h"
50 #include "hostres_oid.h"
51 #include "hostres_tree.h"
54 #define HR_FREEBSD_PART_TYPE 0xc494
56 #define HR_FREEBSD_PART_TYPE 165
59 /* Maximum length for label and id including \0 */
60 #define PART_STR_MLEN (128 + 1)
63 * One row in the hrPartitionTable
65 struct partition_entry {
67 u_char *label; /* max allocated len will be PART_STR_MLEN */
68 u_char *id; /* max allocated len will be PART_STR_MLEN */
71 TAILQ_ENTRY(partition_entry) link;
72 #define HR_PARTITION_FOUND 0x001
75 TAILQ_HEAD(partition_tbl, partition_entry);
78 * This table is used to get a consistent indexing. It saves the name -> index
79 * mapping while we rebuild the partition table.
81 struct partition_map_entry {
82 int32_t index; /* partition_entry::index */
83 u_char *id; /* max allocated len will be PART_STR_MLEN */
86 * next may be NULL if the respective partition_entry
87 * is (temporally) gone.
89 struct partition_entry *entry;
90 STAILQ_ENTRY(partition_map_entry) link;
92 STAILQ_HEAD(partition_map, partition_map_entry);
94 /* Mapping table for consistent indexing */
95 static struct partition_map partition_map =
96 STAILQ_HEAD_INITIALIZER(partition_map);
98 /* THE partition table. */
99 static struct partition_tbl partition_tbl =
100 TAILQ_HEAD_INITIALIZER(partition_tbl);
102 /* next int available for indexing the hrPartitionTable */
103 static uint32_t next_partition_index = 1;
106 * Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK
110 partition_entry_cmp(const struct partition_entry *a,
111 const struct partition_entry *b)
116 if (a->index[0] < b->index[0])
119 if (a->index[0] > b->index[0])
122 if (a->index[1] < b->index[1])
125 if (a->index[1] > b->index[1])
132 * Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC
136 partition_idx_cmp(const struct asn_oid *oid, u_int sub,
137 const struct partition_entry *entry)
141 for (i = 0; i < 2 && i < oid->len - sub; i++) {
142 if (oid->subs[sub + i] < entry->index[i])
144 if (oid->subs[sub + i] > entry->index[i])
147 if (oid->len - sub < 2)
149 if (oid->len - sub > 2)
156 * Create a new partition table entry
158 static struct partition_entry *
159 partition_entry_create(int32_t ds_index, const char *chunk_name)
161 struct partition_entry *entry;
162 struct partition_map_entry *map;
166 assert(chunk_name != NULL);
167 if (chunk_name == NULL || chunk_name[0] == '\0')
170 /* check whether we already have seen this partition */
171 STAILQ_FOREACH(map, &partition_map, link)
172 if (strcmp(map->id, chunk_name) == 0)
176 /* new object - get a new index and create a map */
178 if (next_partition_index > INT_MAX) {
179 /* Unrecoverable error - die clean and quicly*/
180 syslog(LOG_ERR, "%s: hrPartitionTable index wrap",
182 errx(EX_SOFTWARE, "hrPartitionTable index wrap");
185 if ((map = malloc(sizeof(*map))) == NULL) {
186 syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__);
190 id_len = strlen(chunk_name) + 1;
191 if (id_len > PART_STR_MLEN)
192 id_len = PART_STR_MLEN;
194 if ((map->id = malloc(id_len)) == NULL) {
199 map->index = next_partition_index++;
201 strlcpy(map->id, chunk_name, id_len);
205 STAILQ_INSERT_TAIL(&partition_map, map, link);
207 HRDBG("%s added into hrPartitionMap at index=%d",
208 chunk_name, map->index);
211 HRDBG("%s exists in hrPartitionMap index=%d",
212 chunk_name, map->index);
215 if ((entry = malloc(sizeof(*entry))) == NULL) {
216 syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__);
219 memset(entry, 0, sizeof(*entry));
221 /* create the index */
222 entry->index[0] = ds_index;
223 entry->index[1] = map->index;
227 if ((entry->id = strdup(map->id)) == NULL) {
233 * reuse id_len from here till the end of this function
234 * for partition_entry::label
236 id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1;
238 if (id_len > PART_STR_MLEN)
239 id_len = PART_STR_MLEN;
241 if ((entry->label = malloc(id_len )) == NULL) {
247 snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name);
249 INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link,
250 partition_entry_cmp);
256 * Delete a partition table entry but keep the map entry intact.
259 partition_entry_delete(struct partition_entry *entry)
261 struct partition_map_entry *map;
263 assert(entry != NULL);
265 TAILQ_REMOVE(&partition_tbl, entry, link);
266 STAILQ_FOREACH(map, &partition_map, link)
267 if (map->entry == entry) {
277 * Find a partition table entry by name. If none is found, return NULL.
279 static struct partition_entry *
280 partition_entry_find_by_name(const char *name)
282 struct partition_entry *entry = NULL;
284 TAILQ_FOREACH(entry, &partition_tbl, link)
285 if (strcmp(entry->id, name) == 0)
292 * Find a partition table entry by label. If none is found, return NULL.
294 static struct partition_entry *
295 partition_entry_find_by_label(const char *name)
297 struct partition_entry *entry = NULL;
299 TAILQ_FOREACH(entry, &partition_tbl, link)
300 if (strcmp(entry->label, name) == 0)
307 * Process a chunk from libgeom(4). A chunk is either a slice or a partition.
308 * If necessary create a new partition table entry for it. In any case
309 * set the size field of the entry and set the FOUND flag.
312 handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size)
314 struct partition_entry *entry;
317 assert(chunk_name != NULL);
318 assert(chunk_name[0] != '\0');
319 if (chunk_name == NULL || chunk_name == '\0')
322 HRDBG("ANALYZE chunk %s", chunk_name);
324 if ((entry = partition_entry_find_by_name(chunk_name)) == NULL)
325 if ((entry = partition_entry_create(ds_index,
326 chunk_name)) == NULL)
329 entry->flags |= HR_PARTITION_FOUND;
331 /* actual size may overflow the SNMP type */
332 k_size = chunk_size / 1024;
333 entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size);
337 * Start refreshing the partition table. A call to this function will
338 * be followed by a call to handleDiskStorage() for every disk, followed
339 * by a single call to the post_refresh function.
342 partition_tbl_pre_refresh(void)
344 struct partition_entry *entry;
346 /* mark each entry as missing */
347 TAILQ_FOREACH(entry, &partition_tbl, link)
348 entry->flags &= ~HR_PARTITION_FOUND;
352 * Try to find a geom(4) class by its name. Returns a pointer to that
353 * class if found NULL otherways.
355 static struct gclass *
356 find_class(struct gmesh *mesh, const char *name)
358 struct gclass *classp;
360 LIST_FOREACH(classp, &mesh->lg_class, lg_class)
361 if (strcmp(classp->lg_name, name) == 0)
367 * Process all MBR-type partitions from the given disk.
370 get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
373 struct gprovider *pp;
374 struct gconfig *conf;
377 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
378 /* We are only interested in partitions from this disk */
379 if (strcmp(gp->lg_name, disk_dev_name) != 0)
383 * Find all the non-BSD providers (these are handled in get_bsd)
385 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
386 LIST_FOREACH(conf, &pp->lg_config, lg_config) {
387 if (conf->lg_name == NULL ||
388 conf->lg_val == NULL ||
389 strcmp(conf->lg_name, "type") != 0)
393 * We are not interested in BSD partitions
394 * (ie ad0s1 is not interesting at this point).
395 * We'll take care of them in detail (slice
396 * by slice) in get_bsd.
398 part_type = strtol(conf->lg_val, NULL, 10);
399 if (part_type == HR_FREEBSD_PART_TYPE)
401 HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name);
402 HRDBG("Mediasize: %jd",
403 (intmax_t)pp->lg_mediasize / 1024);
404 HRDBG("Sectorsize: %u", pp->lg_sectorsize);
405 HRDBG("Mode: %s", pp->lg_mode);
406 HRDBG("CONFIG: %s: %s",
407 conf->lg_name, conf->lg_val);
409 handle_chunk(ds_index, pp->lg_name,
417 * Process all BSD-type partitions from the given disk.
420 get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
423 struct gprovider *pp;
425 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
427 * We are only interested in those geoms starting with
428 * the disk_dev_name passed as parameter to this function.
430 if (strncmp(gp->lg_name, disk_dev_name,
431 strlen(disk_dev_name)) != 0)
434 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
435 if (pp->lg_name == NULL)
437 handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize);
443 * Called from the DiskStorage table for every row. Open the GEOM(4) framework
444 * and process all the partitions in it.
445 * ds_index is the index into the DiskStorage table.
446 * This is done in two steps: for non BSD partitions the geom class "MBR" is
447 * used, for our BSD slices the "BSD" geom class.
450 partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name)
452 struct gmesh mesh; /* GEOM userland tree */
453 struct gclass *classp;
456 assert(disk_dev_name != NULL);
457 assert(ds_index > 0);
459 HRDBG("===> getting partitions for %s <===", disk_dev_name);
461 /* try to construct the GEOM tree */
462 if ((error = geom_gettree(&mesh)) != 0) {
463 syslog(LOG_WARNING, "cannot get GEOM tree: %m");
468 * First try the GEOM "MBR" class.
469 * This is needed for non-BSD slices (aka partitions)
470 * on PC architectures.
472 if ((classp = find_class(&mesh, "MBR")) != NULL) {
473 get_mbr(classp, ds_index, disk_dev_name);
475 HRDBG("cannot find \"MBR\" geom class");
479 * Get the "BSD" GEOM class.
480 * Here we'll find all the info needed about the BSD slices.
482 if ((classp = find_class(&mesh, "BSD")) != NULL) {
483 get_bsd_sun(classp, ds_index, disk_dev_name);
485 /* no problem on sparc64 */
486 HRDBG("cannot find \"BSD\" geom class");
490 * Get the "SUN" GEOM class.
491 * Here we'll find all the info needed about the BSD slices.
493 if ((classp = find_class(&mesh, "SUN")) != NULL) {
494 get_bsd_sun(classp, ds_index, disk_dev_name);
496 /* no problem on i386 */
497 HRDBG("cannot find \"SUN\" geom class");
500 geom_deletetree(&mesh);
504 * Finish refreshing the table.
507 partition_tbl_post_refresh(void)
509 struct partition_entry *e, *etmp;
512 * Purge items that disappeared
514 TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp)
515 if (!(e->flags & HR_PARTITION_FOUND))
516 partition_entry_delete(e);
520 * Finalization routine for hrPartitionTable
521 * It destroys the lists and frees any allocated heap memory
524 fini_partition_tbl(void)
526 struct partition_map_entry *m;
528 while ((m = STAILQ_FIRST(&partition_map)) != NULL) {
529 STAILQ_REMOVE_HEAD(&partition_map, link);
530 if(m->entry != NULL) {
531 TAILQ_REMOVE(&partition_tbl, m->entry, link);
533 free(m->entry->label);
539 assert(TAILQ_EMPTY(&partition_tbl));
543 * Called from the file system code to insert the file system table index
544 * into the partition table entry. Note, that an partition table entry exists
545 * only for local file systems.
548 handle_partition_fs_index(const char *name, int32_t fs_idx)
550 struct partition_entry *entry;
552 if ((entry = partition_entry_find_by_label(name)) == NULL) {
553 HRDBG("%s IS MISSING from hrPartitionTable", name);
556 HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx);
557 entry->fs_Index = fs_idx;
561 * This is the implementation for a generated (by our SNMP tool)
562 * function prototype, see hostres_tree.h
563 * It handles the SNMP operations for hrPartitionTable
566 op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value,
567 u_int sub, u_int iidx __unused, enum snmp_op op)
569 struct partition_entry *entry;
572 * Refresh the disk storage table (which refreshes the partition
573 * table) if necessary.
575 refresh_disk_storage_tbl(0);
579 case SNMP_OP_GETNEXT:
580 if ((entry = NEXT_OBJECT_FUNC(&partition_tbl,
581 &value->var, sub, partition_idx_cmp)) == NULL)
582 return (SNMP_ERR_NOSUCHNAME);
584 value->var.len = sub + 2;
585 value->var.subs[sub] = entry->index[0];
586 value->var.subs[sub + 1] = entry->index[1];
591 if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
592 &value->var, sub, partition_idx_cmp)) == NULL)
593 return (SNMP_ERR_NOSUCHNAME);
597 if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
598 &value->var, sub, partition_idx_cmp)) == NULL)
599 return (SNMP_ERR_NOT_WRITEABLE);
600 return (SNMP_ERR_NO_CREATION);
602 case SNMP_OP_ROLLBACK:
609 switch (value->var.subs[sub - 1]) {
611 case LEAF_hrPartitionIndex:
612 value->v.integer = entry->index[1];
613 return (SNMP_ERR_NOERROR);
615 case LEAF_hrPartitionLabel:
616 return (string_get(value, entry->label, -1));
618 case LEAF_hrPartitionID:
619 return(string_get(value, entry->id, -1));
621 case LEAF_hrPartitionSize:
622 value->v.integer = entry->size;
623 return (SNMP_ERR_NOERROR);
625 case LEAF_hrPartitionFSIndex:
626 value->v.integer = entry->fs_Index;
627 return (SNMP_ERR_NOERROR);