xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c (revision 3d11b6c8f01e1fca5936a11d6996448467851a94)
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: hrPartitionTable implementation for SNMPd.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/limits.h>
38 
39 #include <assert.h>
40 #include <err.h>
41 #include <inttypes.h>
42 #include <libgeom.h>
43 #include <paths.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 
48 #include "hostres_snmp.h"
49 #include "hostres_oid.h"
50 #include "hostres_tree.h"
51 
52 #ifdef PC98
53 #define HR_FREEBSD_PART_TYPE	0xc494
54 #else
55 #define	HR_FREEBSD_PART_TYPE	165
56 #endif
57 
58 /*
59  * One row in the hrPartitionTable
60  */
61 struct partition_entry {
62 	struct asn_oid	index;
63 	u_char		label[128 + 1];
64 	u_char		id[128 + 1];
65 	int32_t		size;
66 	int32_t		fs_Index;
67 	TAILQ_ENTRY(partition_entry) link;
68 #define HR_PARTITION_FOUND		0x001
69 	uint32_t	flags;
70 };
71 TAILQ_HEAD(partition_tbl, partition_entry);
72 
73 /*
74  * This table is used to get a consistent indexing. It saves the name -> index
75  * mapping while we rebuild the partition table.
76  */
77 struct partition_map_entry {
78 	int32_t		index;		/* hrPartitionTblEntry::index */
79 	u_char		id[128 + 1];
80 
81 	/*
82 	 * next may be NULL if the respective partition_entry
83 	 * is (temporally) gone.
84 	 */
85 	struct partition_entry	*entry;
86 	STAILQ_ENTRY(partition_map_entry) link;
87 };
88 STAILQ_HEAD(partition_map, partition_map_entry);
89 
90 /* Mapping table for consistent indexing */
91 static struct partition_map partition_map =
92     STAILQ_HEAD_INITIALIZER(partition_map);
93 
94 /* THE partition table. */
95 static struct partition_tbl partition_tbl =
96     TAILQ_HEAD_INITIALIZER(partition_tbl);
97 
98 /* next int available for indexing the hrPartitionTable */
99 static uint32_t next_partition_index = 1;
100 
101 /**
102  * Create a new partition table entry
103  */
104 static struct partition_entry *
105 partition_entry_create(int32_t ds_index, const char *chunk_name)
106 {
107 	struct partition_entry *entry;
108 	struct partition_map_entry *map = NULL;
109 
110 	/* sanity checks */
111 	assert(chunk_name != NULL);
112 	if (chunk_name == NULL || chunk_name[0] == '\0')
113 		return (NULL);
114 
115 	if ((entry = malloc(sizeof(*entry))) == NULL) {
116 		syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__);
117 		return (NULL);
118 	}
119 	memset(entry, 0, sizeof(*entry));
120 
121 	/* check whether we already have seen this partition */
122 	STAILQ_FOREACH(map, &partition_map, link)
123 		if (strcmp(map->id, chunk_name) == 0 ) {
124 			map->entry = entry;
125 			break;
126 		}
127 
128 	if (map == NULL) {
129 		/* new object - get a new index and create a map */
130 		if (next_partition_index > INT_MAX) {
131 		        syslog(LOG_ERR, "%s: hrPartitionTable index wrap",
132 			    __func__);
133 			errx(1, "hrPartitionTable index wrap");
134 		}
135 
136 		if ((map = malloc(sizeof(*map))) == NULL) {
137 			syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__);
138 			free(entry);
139 			return (NULL);
140 		}
141 
142 		map->index = next_partition_index++;
143 
144 		strlcpy(map->id, chunk_name, sizeof(map->id));
145 
146 		map->entry = entry;
147 		STAILQ_INSERT_TAIL(&partition_map, map, link);
148 
149 		HRDBG("%s added into hrPartitionMap at index=%d",
150 		    chunk_name, map->index);
151 
152 	} else {
153 		HRDBG("%s exists in hrPartitionMap index=%d",
154 		    chunk_name, map->index);
155 	}
156 
157 	/* create the index */
158 	entry->index.len = 2;
159 	entry->index.subs[0] = ds_index;
160 	entry->index.subs[1] = map->index;
161 
162 	strlcpy(entry->id, chunk_name, sizeof(entry->id));
163 
164 	snprintf(entry->label, sizeof(entry->label) - 1,
165 	    "%s%s", _PATH_DEV, chunk_name);
166 
167 	INSERT_OBJECT_OID(entry, &partition_tbl);
168 
169 	return (entry);
170 }
171 
172 /**
173  * Delete a partition table entry but keep the map entry intact.
174  */
175 static void
176 partition_entry_delete(struct partition_entry *entry)
177 {
178 	struct partition_map_entry *map;
179 
180 	assert(entry != NULL);
181 
182 	TAILQ_REMOVE(&partition_tbl, entry, link);
183 	STAILQ_FOREACH(map, &partition_map, link)
184 		if (map->entry == entry) {
185 			map->entry = NULL;
186 			break;
187 		}
188 
189 	free(entry);
190 }
191 
192 /**
193  * Find a partition table entry by name. If none is found, return NULL.
194  */
195 static struct partition_entry *
196 partition_entry_find_by_name(const char *name)
197 {
198 	struct partition_entry *entry =  NULL;
199 
200 	TAILQ_FOREACH(entry, &partition_tbl, link)
201 		if (strcmp(entry->id, name) == 0)
202 			return (entry);
203 
204 	return (NULL);
205 }
206 
207 /**
208  * Find a partition table entry by label. If none is found, return NULL.
209  */
210 static struct partition_entry *
211 partition_entry_find_by_label(const char *name)
212 {
213 	struct partition_entry *entry =  NULL;
214 
215 	TAILQ_FOREACH(entry, &partition_tbl, link)
216 		if (strcmp(entry->label, name) == 0)
217 			return (entry);
218 
219 	return (NULL);
220 }
221 
222 /**
223  * Process a chunk from libgeom(4). A chunk is either a slice or a partition.
224  * If necessary create a new partition table entry for it. In any case
225  * set the size field of the entry and set the FOUND flag.
226  */
227 static void
228 handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size)
229 {
230 	struct partition_entry *entry = NULL;
231 	daddr_t k_size;
232 
233 	assert(chunk_name != NULL);
234 	assert(chunk_name[0] != '\0');
235 	if (chunk_name == NULL || chunk_name == '\0')
236 		return;
237 
238 	HRDBG("ANALYZE chunk %s", chunk_name);
239 
240 	if ((entry = partition_entry_find_by_name(chunk_name)) == NULL)
241 		if ((entry = partition_entry_create(ds_index,
242 		    chunk_name)) == NULL)
243 			return;
244 
245 	entry->flags |= HR_PARTITION_FOUND;
246 
247 	/* actual size may overflow the SNMP type */
248 	k_size = chunk_size / 1024;
249 	entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size);
250 }
251 
252 /**
253  * Start refreshing the partition table. A call to this function will
254  * be followed by a call to handleDiskStorage() for every disk, followed
255  * by a single call to the post_refresh function.
256  */
257 void
258 partition_tbl_pre_refresh(void)
259 {
260 	struct partition_entry *entry = NULL;
261 
262 	/* mark each entry as missing */
263 	TAILQ_FOREACH(entry, &partition_tbl, link)
264 		entry->flags &= ~HR_PARTITION_FOUND;
265 }
266 
267 /**
268  * Try to find a geom(4) class by its name. Returns a pointer to that
269  * class if found NULL otherways.
270  */
271 static struct gclass *
272 find_class(struct gmesh *mesh, const char *name)
273 {
274 	struct gclass *classp;
275 
276 	LIST_FOREACH(classp, &mesh->lg_class, lg_class)
277 		if (strcmp(classp->lg_name, name) == 0)
278 			return (classp);
279 	return (NULL);
280 }
281 
282 /**
283  * Process all MBR-type partitions from the given disk.
284  */
285 static void
286 get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
287 {
288 	struct ggeom *gp;
289 	struct gprovider *pp;
290 	struct gconfig *conf;
291 	long part_type;
292 
293 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
294 		/* We are only interested in partitions from this disk */
295 		if (strcmp(gp->lg_name, disk_dev_name) != 0)
296 			continue;
297 
298 		/*
299 		 * Find all the non-BSD providers (these are handled in get_bsd)
300 		 */
301 		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
302 			LIST_FOREACH(conf, &pp->lg_config, lg_config) {
303 				if (conf->lg_name == NULL ||
304 				    conf->lg_val == NULL ||
305 				    strcmp(conf->lg_name, "type") != 0)
306 					continue;
307 
308 				/*
309 				 * We are not interested in BSD partitions
310 				 * (ie ad0s1 is not interesting at this point).
311 				 * We'll take care of them in detail (slice
312 				 * by slice) in get_bsd.
313 				 */
314 				part_type = strtol(conf->lg_val, NULL, 10);
315 				if (part_type == HR_FREEBSD_PART_TYPE)
316 					break;
317 				HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name);
318 				HRDBG("Mediasize: %jd",
319 				    (intmax_t)pp->lg_mediasize / 1024);
320 				HRDBG("Sectorsize: %u", pp->lg_sectorsize);
321 				HRDBG("Mode: %s", pp->lg_mode);
322 				HRDBG("CONFIG: %s: %s",
323 				    conf->lg_name, conf->lg_val);
324 
325 				handle_chunk(ds_index, pp->lg_name,
326 				    pp->lg_mediasize);
327 			}
328 		}
329 	}
330 }
331 
332 /**
333  * Process all BSD-type partitions from the given disk.
334  */
335 static void
336 get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
337 {
338 	struct ggeom *gp;
339 	struct gprovider *pp;
340 
341 	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
342 		/*
343 		 * We are only interested in those geoms starting with
344 		 * the disk_dev_name passed as parameter to this function.
345 		 */
346 		if (strncmp(gp->lg_name, disk_dev_name,
347 		    strlen(disk_dev_name)) != 0)
348 			continue;
349 
350 		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
351 			if (pp->lg_name == NULL)
352 				continue;
353 			handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize);
354 		}
355 	}
356 }
357 
358 /**
359  * Called from the DiskStorage table for every row. Open the GEOM(4) framework
360  * and process all the partitions in it.
361  * ds_index is the index into the DiskStorage table.
362  * This is done in two steps: for non BSD partitions the geom class "MBR" is
363  * used, for our BSD slices the "BSD" geom class.
364  */
365 void
366 partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name)
367 {
368 	struct gmesh mesh;	/* GEOM userland tree */
369 	struct gclass *classp;
370 	int error;
371 
372 	assert(disk_dev_name != NULL);
373 	assert(ds_index > 0);
374 
375      	HRDBG("===> getting partitions for %s <===", disk_dev_name);
376 
377 	/* try to construct the GEOM tree */
378 	if ((error = geom_gettree(&mesh)) != 0) {
379 		syslog(LOG_WARNING, "cannot get GEOM tree: %m");
380 		return;
381 	}
382 
383 	/*
384 	 * First try the GEOM "MBR" class.
385 	 * This is needed for non-BSD slices (aka partitions)
386 	 * on PC architectures.
387 	 */
388 	if ((classp = find_class(&mesh, "MBR")) != NULL) {
389 		get_mbr(classp, ds_index, disk_dev_name);
390 	} else {
391 		HRDBG("cannot find \"MBR\" geom class");
392 	}
393 
394 	/*
395 	 * Get the "BSD" GEOM class.
396 	 * Here we'll find all the info needed about the BSD slices.
397 	 */
398 	if ((classp = find_class(&mesh, "BSD")) != NULL) {
399 		get_bsd_sun(classp, ds_index, disk_dev_name);
400 	} else {
401 		/* no problem on sparc64 */
402 		HRDBG("cannot find \"BSD\" geom class");
403 	}
404 
405 	/*
406 	 * Get the "SUN" GEOM class.
407 	 * Here we'll find all the info needed about the BSD slices.
408 	 */
409 	if ((classp = find_class(&mesh, "SUN")) != NULL) {
410 		get_bsd_sun(classp, ds_index, disk_dev_name);
411 	} else {
412 		/* no problem on i386 */
413 		HRDBG("cannot find \"SUN\" geom class");
414 	}
415 
416 	geom_deletetree(&mesh);
417 }
418 
419 /**
420  * Finish refreshing the table.
421  */
422 void
423 partition_tbl_post_refresh(void)
424 {
425 	struct partition_entry *e, *etmp;
426 
427 	/*
428 	 * Purge items that disappeared
429 	 */
430 	TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp)
431 		if (!(e->flags & HR_PARTITION_FOUND))
432 			partition_entry_delete(e);
433 }
434 
435 /*
436  * Finalization routine for hrPartitionTable
437  * It destroys the lists and frees any allocated heap memory
438  */
439 void
440 fini_partition_tbl(void)
441 {
442 	struct partition_map_entry *m;
443 
444      	while ((m = STAILQ_FIRST(&partition_map)) != NULL) {
445 		STAILQ_REMOVE_HEAD(&partition_map, link);
446 		if(m->entry != NULL) {
447 			TAILQ_REMOVE(&partition_tbl, m->entry, link);
448 			free(m->entry);
449 		}
450 		free(m);
451      	}
452 	assert(TAILQ_EMPTY(&partition_tbl));
453 }
454 
455 /**
456  * Called from the file system code to insert the file system table index
457  * into the partition table entry. Note, that an partition table entry exists
458  * only for local file systems.
459  */
460 void
461 handle_partition_fs_index(const char *name, int32_t fs_idx)
462 {
463 	struct partition_entry *entry;
464 
465 	if ((entry = partition_entry_find_by_label(name)) == NULL) {
466 		HRDBG("%s IS MISSING from hrPartitionTable", name);
467 		return;
468 	}
469 	HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx);
470 	entry->fs_Index = fs_idx;
471 }
472 
473 /*
474  * This is the implementation for a generated (by our SNMP tool)
475  * function prototype, see hostres_tree.h
476  * It handles the SNMP operations for hrPartitionTable
477  */
478 int
479 op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value,
480     u_int sub, u_int iidx __unused, enum snmp_op op)
481 {
482 	struct partition_entry *entry;
483 
484 	/*
485 	 * Refresh the disk storage table (which refreshes the partition
486 	 * table) if necessary.
487 	 */
488 	refresh_disk_storage_tbl(0);
489 
490 	switch (op) {
491 
492 	case SNMP_OP_GETNEXT:
493 		if ((entry = NEXT_OBJECT_OID(&partition_tbl,
494 		    &value->var, sub)) == NULL)
495 			return (SNMP_ERR_NOSUCHNAME);
496 
497 		index_append(&value->var, sub, &entry->index);
498 		goto get;
499 
500 	case SNMP_OP_GET:
501 		if ((entry = FIND_OBJECT_OID(&partition_tbl,
502 		    &value->var, sub)) == NULL)
503 			return (SNMP_ERR_NOSUCHNAME);
504 		goto get;
505 
506 	case SNMP_OP_SET:
507 		if ((entry = FIND_OBJECT_OID(&partition_tbl,
508 		    &value->var, sub)) == NULL)
509 			return (SNMP_ERR_NOT_WRITEABLE);
510 		return (SNMP_ERR_NO_CREATION);
511 
512 	case SNMP_OP_ROLLBACK:
513 	case SNMP_OP_COMMIT:
514 		abort();
515 	}
516 	abort();
517 
518   get:
519 	switch (value->var.subs[sub - 1]) {
520 
521 	case LEAF_hrPartitionIndex:
522 		value->v.integer = entry->index.subs[1];
523 		return (SNMP_ERR_NOERROR);
524 
525 	case LEAF_hrPartitionLabel:
526 		return (string_get(value, entry->label, -1));
527 
528 	case LEAF_hrPartitionID:
529 		return(string_get(value, entry->id, -1));
530 
531 	case LEAF_hrPartitionSize:
532 		value->v.integer = entry->size;
533 		return (SNMP_ERR_NOERROR);
534 
535 	case LEAF_hrPartitionFSIndex:
536 		value->v.integer = entry->fs_Index;
537 		return (SNMP_ERR_NOERROR);
538 	}
539 	abort();
540 }
541