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_storage_tbl.c
108351 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 for SNMPd. Implementation for hrStorageTable
32
*/
33
34
#include <sys/types.h>
35
#include <sys/param.h>
36
#include <sys/sysctl.h>
37
#include <sys/vmmeter.h>
38
#include <sys/mount.h>
39
40
#include <vm/vm_param.h>
41
42
#include <assert.h>
43
#include <err.h>
44
#include <limits.h>
45
#include <memstat.h>
46
#include <paths.h>
47
#include <stdlib.h>
48
#include <string.h>
49
#include <syslog.h>
50
#include <unistd.h> /* for getpagesize() */
51
#include <sysexits.h>
52
53
#include "hostres_snmp.h"
54
#include "hostres_oid.h"
55
#include "hostres_tree.h"
56
57
/* maximum length for description string according to MIB */
58
#define SE_DESC_MLEN (255 + 1)
59
60
/*
61
* This structure is used to hold a SNMP table entry
62
* for HOST-RESOURCES-MIB's hrStorageTable
63
*/
64
struct storage_entry {
65
int32_t index;
66
const struct asn_oid *type;
67
u_char *descr;
68
int32_t allocationUnits;
69
int32_t size;
70
int32_t used;
71
uint32_t allocationFailures;
72
#define HR_STORAGE_FOUND 0x001
73
uint32_t flags; /* to be used internally*/
74
TAILQ_ENTRY(storage_entry) link;
75
};
76
TAILQ_HEAD(storage_tbl, storage_entry);
77
78
/*
79
* Next structure is used to keep o list of mappings from a specific name
80
* (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the
81
* same index for a specific name at least for the duration of one SNMP agent
82
* run.
83
*/
84
struct storage_map_entry {
85
int32_t hrIndex; /* used for storage_entry::index */
86
87
/* map key, also used for storage_entry::descr */
88
u_char *a_name;
89
90
/*
91
* next may be NULL if the respective storage_entry
92
* is (temporally) gone
93
*/
94
struct storage_entry *entry;
95
STAILQ_ENTRY(storage_map_entry) link;
96
};
97
STAILQ_HEAD(storage_map, storage_map_entry);
98
99
/* the head of the list with table's entries */
100
static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl);
101
102
/*for consistent table indexing*/
103
static struct storage_map storage_map =
104
STAILQ_HEAD_INITIALIZER(storage_map);
105
106
/* last (agent) tick when hrStorageTable was updated */
107
static uint64_t storage_tick;
108
109
/* maximum number of ticks between two refreshs */
110
uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100;
111
112
/* for kvm_getswapinfo, malloc'd */
113
static struct kvm_swap *swap_devs;
114
static size_t swap_devs_len; /* item count for swap_devs */
115
116
/* for getfsstat, malloc'd */
117
static struct statfs *fs_buf;
118
static size_t fs_buf_count; /* item count for fs_buf */
119
120
static struct vmtotal mem_stats;
121
122
/* next int available for indexing the hrStorageTable */
123
static uint32_t next_storage_index = 1;
124
125
/* start of list for memory detailed stats */
126
static struct memory_type_list *mt_list;
127
128
/* Constants */
129
static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam;
130
static const struct asn_oid OIDX_hrStorageVirtualMemory_c =
131
OIDX_hrStorageVirtualMemory;
132
133
/**
134
* Create a new entry into the storage table and, if necessary, an
135
* entry into the storage map.
136
*/
137
static struct storage_entry *
138
storage_entry_create(const char *name)
139
{
140
struct storage_entry *entry;
141
struct storage_map_entry *map;
142
size_t name_len;
143
144
assert(name != NULL);
145
assert(strlen(name) > 0);
146
147
STAILQ_FOREACH(map, &storage_map, link)
148
if (strcmp(map->a_name, name) == 0)
149
break;
150
151
if (map == NULL) {
152
/* new object - get a new index */
153
if (next_storage_index > INT_MAX) {
154
syslog(LOG_ERR,
155
"%s: hrStorageTable index wrap", __func__);
156
errx(EX_SOFTWARE, "hrStorageTable index wrap");
157
}
158
159
if ((map = malloc(sizeof(*map))) == NULL) {
160
syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ );
161
return (NULL);
162
}
163
164
name_len = strlen(name) + 1;
165
if (name_len > SE_DESC_MLEN)
166
name_len = SE_DESC_MLEN;
167
168
if ((map->a_name = malloc(name_len)) == NULL) {
169
free(map);
170
return (NULL);
171
}
172
173
strlcpy(map->a_name, name, name_len);
174
map->hrIndex = next_storage_index++;
175
176
STAILQ_INSERT_TAIL(&storage_map, map, link);
177
178
HRDBG("%s added into hrStorageMap at index=%d",
179
name, map->hrIndex);
180
} else {
181
HRDBG("%s exists in hrStorageMap index=%d\n",
182
name, map->hrIndex);
183
}
184
185
if ((entry = malloc(sizeof(*entry))) == NULL) {
186
syslog(LOG_WARNING, "%s: %m", __func__);
187
return (NULL);
188
}
189
memset(entry, 0, sizeof(*entry));
190
191
entry->index = map->hrIndex;
192
193
if ((entry->descr = strdup(map->a_name)) == NULL) {
194
free(entry);
195
return (NULL);
196
}
197
198
map->entry = entry;
199
200
INSERT_OBJECT_INT(entry, &storage_tbl);
201
202
return (entry);
203
}
204
205
/**
206
* Delete an entry from the storage table.
207
*/
208
static void
209
storage_entry_delete(struct storage_entry *entry)
210
{
211
struct storage_map_entry *map;
212
213
assert(entry != NULL);
214
215
TAILQ_REMOVE(&storage_tbl, entry, link);
216
STAILQ_FOREACH(map, &storage_map, link)
217
if (map->entry == entry) {
218
map->entry = NULL;
219
break;
220
}
221
free(entry->descr);
222
free(entry);
223
}
224
225
/**
226
* Find a table entry by its name.
227
*/
228
static struct storage_entry *
229
storage_find_by_name(const char *name)
230
{
231
struct storage_entry *entry;
232
233
TAILQ_FOREACH(entry, &storage_tbl, link)
234
if (strcmp(entry->descr, name) == 0)
235
return (entry);
236
237
return (NULL);
238
}
239
240
/*
241
* VM info.
242
*/
243
static void
244
storage_OS_get_vm(void)
245
{
246
int mib[2] = { CTL_VM, VM_TOTAL };
247
size_t len = sizeof(mem_stats);
248
int page_size_bytes;
249
struct storage_entry *entry;
250
251
if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) {
252
syslog(LOG_ERR,
253
"hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) "
254
"failed: %m", __func__);
255
assert(0);
256
return;
257
}
258
259
page_size_bytes = getpagesize();
260
261
/* Real Memory Metrics */
262
if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL &&
263
(entry = storage_entry_create("Real Memory Metrics")) == NULL)
264
return; /* I'm out of luck now, maybe next time */
265
266
entry->flags |= HR_STORAGE_FOUND;
267
entry->type = &OIDX_hrStorageRam_c;
268
entry->allocationUnits = page_size_bytes;
269
entry->size = mem_stats.t_rm;
270
entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */
271
entry->allocationFailures = 0;
272
273
/* Shared Real Memory Metrics */
274
if ((entry = storage_find_by_name("Shared Real Memory Metrics")) ==
275
NULL &&
276
(entry = storage_entry_create("Shared Real Memory Metrics")) ==
277
NULL)
278
return;
279
280
entry->flags |= HR_STORAGE_FOUND;
281
entry->type = &OIDX_hrStorageRam_c;
282
entry->allocationUnits = page_size_bytes;
283
entry->size = mem_stats.t_rmshr;
284
/* ACTIVE is not USED - FIXME */
285
entry->used = mem_stats.t_armshr;
286
entry->allocationFailures = 0;
287
}
288
289
static void
290
storage_OS_get_memstat(void)
291
{
292
struct memory_type *mt_item;
293
struct storage_entry *entry;
294
295
if (mt_list == NULL) {
296
if ((mt_list = memstat_mtl_alloc()) == NULL)
297
/* again? we have a serious problem */
298
return;
299
}
300
301
if (memstat_sysctl_all(mt_list, 0) < 0) {
302
syslog(LOG_ERR, "memstat_sysctl_all failed: %s",
303
memstat_strerror(memstat_mtl_geterror(mt_list)) );
304
return;
305
}
306
307
if ((mt_item = memstat_mtl_first(mt_list)) == NULL) {
308
/* usually this is not an error, no errno for this failure*/
309
HRDBG("memstat_mtl_first failed");
310
return;
311
}
312
313
do {
314
const char *memstat_name;
315
uint64_t tmp_size;
316
int allocator;
317
char alloc_descr[SE_DESC_MLEN];
318
319
memstat_name = memstat_get_name(mt_item);
320
321
if (memstat_name == NULL || strlen(memstat_name) == 0)
322
continue;
323
324
switch (allocator = memstat_get_allocator(mt_item)) {
325
326
case ALLOCATOR_MALLOC:
327
snprintf(alloc_descr, sizeof(alloc_descr),
328
"MALLOC: %s", memstat_name);
329
break;
330
331
case ALLOCATOR_UMA:
332
snprintf(alloc_descr, sizeof(alloc_descr),
333
"UMA: %s", memstat_name);
334
break;
335
336
default:
337
snprintf(alloc_descr, sizeof(alloc_descr),
338
"UNKNOWN%d: %s", allocator, memstat_name);
339
break;
340
}
341
342
if ((entry = storage_find_by_name(alloc_descr)) == NULL &&
343
(entry = storage_entry_create(alloc_descr)) == NULL)
344
return;
345
346
entry->flags |= HR_STORAGE_FOUND;
347
entry->type = &OIDX_hrStorageRam_c;
348
349
if ((tmp_size = memstat_get_size(mt_item)) == 0)
350
tmp_size = memstat_get_sizemask(mt_item);
351
entry->allocationUnits =
352
(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
353
354
tmp_size = memstat_get_countlimit(mt_item);
355
entry->size =
356
(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
357
358
tmp_size = memstat_get_count(mt_item);
359
entry->used =
360
(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
361
362
tmp_size = memstat_get_failures(mt_item);
363
entry->allocationFailures =
364
(tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
365
366
} while((mt_item = memstat_mtl_next(mt_item)) != NULL);
367
}
368
369
/**
370
* Get swap info
371
*/
372
static void
373
storage_OS_get_swap(void)
374
{
375
struct storage_entry *entry;
376
char swap_w_prefix[SE_DESC_MLEN];
377
size_t len;
378
int nswapdev;
379
380
len = sizeof(nswapdev);
381
nswapdev = 0;
382
383
if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) {
384
syslog(LOG_ERR,
385
"hrStorageTable: sysctlbyname(\"vm.nswapdev\") "
386
"failed. %m");
387
assert(0);
388
return;
389
}
390
391
if (nswapdev <= 0) {
392
HRDBG("vm.nswapdev is %d", nswapdev);
393
return;
394
}
395
396
if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) {
397
swap_devs_len = nswapdev + 1;
398
swap_devs = reallocf(swap_devs,
399
swap_devs_len * sizeof(struct kvm_swap));
400
401
assert(swap_devs != NULL);
402
if (swap_devs == NULL) {
403
swap_devs_len = 0;
404
return;
405
}
406
}
407
408
nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0);
409
if (nswapdev < 0) {
410
syslog(LOG_ERR,
411
"hrStorageTable: kvm_getswapinfo failed. %m\n");
412
assert(0);
413
return;
414
}
415
416
for (len = 0; len < (size_t)nswapdev; len++) {
417
memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix));
418
snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1,
419
"Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname);
420
421
entry = storage_find_by_name(swap_w_prefix);
422
if (entry == NULL)
423
entry = storage_entry_create(swap_w_prefix);
424
425
assert (entry != NULL);
426
if (entry == NULL)
427
return; /* Out of luck */
428
429
entry->flags |= HR_STORAGE_FOUND;
430
entry->type = &OIDX_hrStorageVirtualMemory_c;
431
entry->allocationUnits = getpagesize();
432
entry->size = swap_devs[len].ksw_total;
433
entry->used = swap_devs[len].ksw_used;
434
entry->allocationFailures = 0;
435
}
436
}
437
438
/**
439
* Query the underlaying OS for the mounted file systems
440
* anf fill in the respective lists (for hrStorageTable and for hrFSTable)
441
*/
442
static void
443
storage_OS_get_fs(void)
444
{
445
struct storage_entry *entry;
446
uint64_t size, used;
447
int i, mounted_fs_count, units;
448
char fs_string[SE_DESC_MLEN];
449
450
if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) {
451
syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");
452
return; /* out of luck this time */
453
}
454
455
if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) {
456
fs_buf_count = mounted_fs_count;
457
fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs));
458
if (fs_buf == NULL) {
459
fs_buf_count = 0;
460
assert(0);
461
return;
462
}
463
}
464
465
if ((mounted_fs_count = getfsstat(fs_buf,
466
fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) {
467
syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");
468
return; /* out of luck this time */
469
}
470
471
HRDBG("got %d mounted FS", mounted_fs_count);
472
473
fs_tbl_pre_refresh();
474
475
for (i = 0; i < mounted_fs_count; i++) {
476
snprintf(fs_string, sizeof(fs_string),
477
"%s, type: %s, dev: %s", fs_buf[i].f_mntonname,
478
fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname);
479
480
entry = storage_find_by_name(fs_string);
481
if (entry == NULL)
482
entry = storage_entry_create(fs_string);
483
484
assert (entry != NULL);
485
if (entry == NULL)
486
return; /* Out of luck */
487
488
entry->flags |= HR_STORAGE_FOUND;
489
entry->type = fs_get_type(&fs_buf[i]); /*XXX - This is wrong*/
490
491
units = fs_buf[i].f_bsize;
492
size = fs_buf[i].f_blocks;
493
used = fs_buf[i].f_blocks - fs_buf[i].f_bfree;
494
while (size > INT_MAX) {
495
units <<= 1;
496
size >>= 1;
497
used >>= 1;
498
}
499
entry->allocationUnits = units;
500
entry->size = size;
501
entry->used = used;
502
503
entry->allocationFailures = 0;
504
505
/* take care of hrFSTable */
506
fs_tbl_process_statfs_entry(&fs_buf[i], entry->index);
507
}
508
509
fs_tbl_post_refresh();
510
}
511
512
/**
513
* Initialize storage table and populate it.
514
*/
515
void
516
init_storage_tbl(void)
517
{
518
if ((mt_list = memstat_mtl_alloc()) == NULL)
519
syslog(LOG_ERR,
520
"hrStorageTable: memstat_mtl_alloc() failed: %m");
521
522
refresh_storage_tbl(1);
523
}
524
525
void
526
fini_storage_tbl(void)
527
{
528
struct storage_map_entry *n1;
529
530
if (swap_devs != NULL) {
531
free(swap_devs);
532
swap_devs = NULL;
533
}
534
swap_devs_len = 0;
535
536
if (fs_buf != NULL) {
537
free(fs_buf);
538
fs_buf = NULL;
539
}
540
fs_buf_count = 0;
541
542
while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) {
543
STAILQ_REMOVE_HEAD(&storage_map, link);
544
if (n1->entry != NULL) {
545
TAILQ_REMOVE(&storage_tbl, n1->entry, link);
546
free(n1->entry->descr);
547
free(n1->entry);
548
}
549
free(n1->a_name);
550
free(n1);
551
}
552
assert(TAILQ_EMPTY(&storage_tbl));
553
}
554
555
void
556
refresh_storage_tbl(int force)
557
{
558
struct storage_entry *entry, *entry_tmp;
559
560
if (!force && storage_tick != 0 &&
561
this_tick - storage_tick < storage_tbl_refresh) {
562
HRDBG("no refresh needed");
563
return;
564
}
565
566
/* mark each entry as missing */
567
TAILQ_FOREACH(entry, &storage_tbl, link)
568
entry->flags &= ~HR_STORAGE_FOUND;
569
570
storage_OS_get_vm();
571
storage_OS_get_swap();
572
storage_OS_get_fs();
573
storage_OS_get_memstat();
574
575
/*
576
* Purge items that disappeared
577
*/
578
TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp)
579
if (!(entry->flags & HR_STORAGE_FOUND))
580
storage_entry_delete(entry);
581
582
storage_tick = this_tick;
583
584
HRDBG("refresh DONE");
585
}
586
587
/*
588
* This is the implementation for a generated (by our SNMP tool)
589
* function prototype, see hostres_tree.h
590
* It handles the SNMP operations for hrStorageTable
591
*/
592
int
593
op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value,
594
u_int sub, u_int iidx __unused, enum snmp_op curr_op)
595
{
596
struct storage_entry *entry;
597
598
refresh_storage_tbl(0);
599
600
switch (curr_op) {
601
602
case SNMP_OP_GETNEXT:
603
if ((entry = NEXT_OBJECT_INT(&storage_tbl,
604
&value->var, sub)) == NULL)
605
return (SNMP_ERR_NOSUCHNAME);
606
607
value->var.len = sub + 1;
608
value->var.subs[sub] = entry->index;
609
goto get;
610
611
case SNMP_OP_GET:
612
if ((entry = FIND_OBJECT_INT(&storage_tbl,
613
&value->var, sub)) == NULL)
614
return (SNMP_ERR_NOSUCHNAME);
615
goto get;
616
617
case SNMP_OP_SET:
618
if ((entry = FIND_OBJECT_INT(&storage_tbl,
619
&value->var, sub)) == NULL)
620
return (SNMP_ERR_NO_CREATION);
621
return (SNMP_ERR_NOT_WRITEABLE);
622
623
case SNMP_OP_ROLLBACK:
624
case SNMP_OP_COMMIT:
625
abort();
626
}
627
abort();
628
629
get:
630
switch (value->var.subs[sub - 1]) {
631
632
case LEAF_hrStorageIndex:
633
value->v.integer = entry->index;
634
return (SNMP_ERR_NOERROR);
635
636
case LEAF_hrStorageType:
637
assert(entry->type != NULL);
638
value->v.oid = *entry->type;
639
return (SNMP_ERR_NOERROR);
640
641
case LEAF_hrStorageDescr:
642
assert(entry->descr != NULL);
643
return (string_get(value, entry->descr, -1));
644
break;
645
646
case LEAF_hrStorageAllocationUnits:
647
value->v.integer = entry->allocationUnits;
648
return (SNMP_ERR_NOERROR);
649
650
case LEAF_hrStorageSize:
651
value->v.integer = entry->size;
652
return (SNMP_ERR_NOERROR);
653
654
case LEAF_hrStorageUsed:
655
value->v.integer = entry->used;
656
return (SNMP_ERR_NOERROR);
657
658
case LEAF_hrStorageAllocationFailures:
659
value->v.uint32 = entry->allocationFailures;
660
return (SNMP_ERR_NOERROR);
661
}
662
abort();
663
}
664
665