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