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_swinstalled_tbl.c
108262 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
* Host Resources MIB implementation for SNMPd: instrumentation for
30
* hrSWInstalledTable
31
*/
32
33
#include <sys/limits.h>
34
#include <sys/stat.h>
35
#include <sys/sysctl.h>
36
#include <sys/utsname.h>
37
38
#include <assert.h>
39
#include <dirent.h>
40
#include <err.h>
41
#include <errno.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 CONTENTS_FNAME "+CONTENTS"
52
53
enum SWInstalledType {
54
SWI_UNKNOWN = 1,
55
SWI_OPERATING_SYSTEM = 2,
56
SWI_DEVICE_DRIVER = 3,
57
SWI_APPLICATION = 4
58
};
59
60
#define SW_NAME_MLEN (64 + 1)
61
62
/*
63
* This structure is used to hold a SNMP table entry
64
* for HOST-RESOURCES-MIB's hrSWInstalledTable
65
*/
66
struct swins_entry {
67
int32_t index;
68
u_char *name; /* max len for this is SW_NAME_MLEN */
69
const struct asn_oid *id;
70
int32_t type; /* from enum SWInstalledType */
71
u_char date[11];
72
u_int date_len;
73
74
#define HR_SWINSTALLED_FOUND 0x001
75
#define HR_SWINSTALLED_IMMUTABLE 0x002
76
uint32_t flags;
77
78
TAILQ_ENTRY(swins_entry) link;
79
};
80
TAILQ_HEAD(swins_tbl, swins_entry);
81
82
/*
83
* Table to keep a conistent mapping between software and indexes.
84
*/
85
struct swins_map_entry {
86
int32_t index; /* swins_entry::index */
87
u_char *name; /* map key,a copy of swins_entry::name*/
88
89
/*
90
* next may be NULL if the respective hrSWInstalledTblEntry
91
* is (temporally) gone
92
*/
93
struct swins_entry *entry;
94
95
STAILQ_ENTRY(swins_map_entry) link;
96
};
97
STAILQ_HEAD(swins_map, swins_map_entry);
98
99
/* map for consistent indexing */
100
static struct swins_map swins_map = STAILQ_HEAD_INITIALIZER(swins_map);
101
102
/* the head of the list with hrSWInstalledTable's entries */
103
static struct swins_tbl swins_tbl = TAILQ_HEAD_INITIALIZER(swins_tbl);
104
105
/* next int available for indexing the hrSWInstalledTable */
106
static uint32_t next_swins_index = 1;
107
108
/* last (agent) tick when hrSWInstalledTable was updated */
109
static uint64_t swins_tick;
110
111
/* maximum number of ticks between updates of network table */
112
uint32_t swins_tbl_refresh = HR_SWINS_TBL_REFRESH * 100;
113
114
/* package directory */
115
u_char *pkg_dir;
116
117
/* last change of package list */
118
static time_t os_pkg_last_change;
119
120
/**
121
* Create a new entry into the hrSWInstalledTable
122
*/
123
static struct swins_entry *
124
swins_entry_create(const char *name)
125
{
126
struct swins_entry *entry;
127
struct swins_map_entry *map;
128
129
STAILQ_FOREACH(map, &swins_map, link)
130
if (strcmp((const char *)map->name, name) == 0)
131
break;
132
133
if (map == NULL) {
134
size_t name_len;
135
/* new object - get a new index */
136
if (next_swins_index > INT_MAX) {
137
syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap",
138
__func__ );
139
/* There isn't much we can do here.
140
* If the next_swins_index is consumed
141
* then we can't add entries to this table
142
* So it is better to exit - if the table is sparsed
143
* at the next agent run we can fill it fully.
144
*/
145
errx(EX_SOFTWARE, "hrSWInstalledTable index wrap");
146
}
147
148
if ((map = malloc(sizeof(*map))) == NULL) {
149
syslog(LOG_ERR, "%s: %m", __func__ );
150
return (NULL);
151
}
152
153
name_len = strlen(name) + 1;
154
if (name_len > SW_NAME_MLEN)
155
name_len = SW_NAME_MLEN;
156
157
if ((map->name = malloc(name_len)) == NULL) {
158
syslog(LOG_WARNING, "%s: %m", __func__);
159
free(map);
160
return (NULL);
161
}
162
163
map->index = next_swins_index++;
164
strlcpy((char *)map->name, name, name_len);
165
166
STAILQ_INSERT_TAIL(&swins_map, map, link);
167
168
HRDBG("%s added into hrSWInstalled at %d", name, map->index);
169
}
170
171
if ((entry = malloc(sizeof(*entry))) == NULL) {
172
syslog(LOG_WARNING, "%s: %m", __func__);
173
return (NULL);
174
}
175
memset(entry, 0, sizeof(*entry));
176
177
if ((entry->name = strdup(map->name)) == NULL) {
178
syslog(LOG_WARNING, "%s: %m", __func__);
179
free(entry);
180
return (NULL);
181
}
182
183
entry->index = map->index;
184
map->entry = entry;
185
186
INSERT_OBJECT_INT(entry, &swins_tbl);
187
188
return (entry);
189
}
190
191
/**
192
* Delete an entry in the hrSWInstalledTable
193
*/
194
static void
195
swins_entry_delete(struct swins_entry *entry)
196
{
197
struct swins_map_entry *map;
198
199
assert(entry != NULL);
200
201
TAILQ_REMOVE(&swins_tbl, entry, link);
202
203
STAILQ_FOREACH(map, &swins_map, link)
204
if (map->entry == entry) {
205
map->entry = NULL;
206
break;
207
}
208
209
free(entry->name);
210
free(entry);
211
}
212
213
/**
214
* Find an entry given it's name
215
*/
216
static struct swins_entry *
217
swins_find_by_name(const char *name)
218
{
219
struct swins_entry *entry;
220
221
TAILQ_FOREACH(entry, &swins_tbl, link)
222
if (strcmp((const char*)entry->name, name) == 0)
223
return (entry);
224
return (NULL);
225
}
226
227
/**
228
* Finalize this table
229
*/
230
void
231
fini_swins_tbl(void)
232
{
233
struct swins_map_entry *n1;
234
235
while ((n1 = STAILQ_FIRST(&swins_map)) != NULL) {
236
STAILQ_REMOVE_HEAD(&swins_map, link);
237
if (n1->entry != NULL) {
238
TAILQ_REMOVE(&swins_tbl, n1->entry, link);
239
free(n1->entry->name);
240
free(n1->entry);
241
}
242
free(n1->name);
243
free(n1);
244
}
245
assert(TAILQ_EMPTY(&swins_tbl));
246
}
247
248
/**
249
* Get the *running* O/S identification
250
*/
251
static void
252
swins_get_OS_ident(void)
253
{
254
struct utsname os_id;
255
char os_string[SW_NAME_MLEN] = "";
256
struct swins_entry *entry;
257
u_char *boot;
258
struct stat sb;
259
struct tm k_ts;
260
261
if (uname(&os_id) == -1) {
262
syslog(LOG_WARNING, "%s: %m", __func__);
263
return;
264
}
265
266
snprintf(os_string, sizeof(os_string), "%s: %s",
267
os_id.sysname, os_id.version);
268
269
if ((entry = swins_find_by_name(os_string)) != NULL ||
270
(entry = swins_entry_create(os_string)) == NULL)
271
return;
272
273
entry->flags |= (HR_SWINSTALLED_FOUND | HR_SWINSTALLED_IMMUTABLE);
274
entry->id = &oid_zeroDotZero;
275
entry->type = (int32_t)SWI_OPERATING_SYSTEM;
276
memset(entry->date, 0, sizeof(entry->date));
277
278
if (OS_getSystemInitialLoadParameters(&boot) == SNMP_ERR_NOERROR &&
279
strlen(boot) > 0 && stat(boot, &sb) == 0 &&
280
localtime_r(&sb.st_ctime, &k_ts) != NULL)
281
entry->date_len = make_date_time(entry->date, &k_ts, 0);
282
}
283
284
/**
285
* Read the installed packages
286
*/
287
static int
288
swins_get_packages(void)
289
{
290
struct stat sb;
291
DIR *p_dir;
292
struct dirent *ent;
293
struct tm k_ts;
294
char *pkg_file;
295
struct swins_entry *entry;
296
int ret = 0;
297
298
if (pkg_dir == NULL)
299
/* initialisation may have failed */
300
return (-1);
301
302
if (stat(pkg_dir, &sb) != 0) {
303
syslog(LOG_ERR, "hrSWInstalledTable: stat(\"%s\") failed: %m",
304
pkg_dir);
305
return (-1);
306
}
307
if (!S_ISDIR(sb.st_mode)) {
308
syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" is not a directory",
309
pkg_dir);
310
return (-1);
311
}
312
if (sb.st_ctime <= os_pkg_last_change) {
313
HRDBG("no need to rescan installed packages -- "
314
"directory time-stamp unmodified");
315
316
TAILQ_FOREACH(entry, &swins_tbl, link)
317
entry->flags |= HR_SWINSTALLED_FOUND;
318
319
return (0);
320
}
321
322
if ((p_dir = opendir(pkg_dir)) == NULL) {
323
syslog(LOG_ERR, "hrSWInstalledTable: opendir(\"%s\") failed: "
324
"%m", pkg_dir);
325
return (-1);
326
}
327
328
while (errno = 0, (ent = readdir(p_dir)) != NULL) {
329
HRDBG(" pkg file: %s", ent->d_name);
330
331
/* check that the contents file is a regular file */
332
if (asprintf(&pkg_file, "%s/%s/%s", pkg_dir, ent->d_name,
333
CONTENTS_FNAME) == -1)
334
continue;
335
336
if (stat(pkg_file, &sb) != 0 ) {
337
free(pkg_file);
338
continue;
339
}
340
341
if (!S_ISREG(sb.st_mode)) {
342
syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" not a "
343
"regular file -- skipped", pkg_file);
344
free(pkg_file);
345
continue;
346
}
347
free(pkg_file);
348
349
/* read directory timestamp on package */
350
if (asprintf(&pkg_file, "%s/%s", pkg_dir, ent->d_name) == -1)
351
continue;
352
353
if (stat(pkg_file, &sb) == -1 ||
354
localtime_r(&sb.st_ctime, &k_ts) == NULL) {
355
free(pkg_file);
356
continue;
357
}
358
free(pkg_file);
359
360
/* update or create entry */
361
if ((entry = swins_find_by_name(ent->d_name)) == NULL &&
362
(entry = swins_entry_create(ent->d_name)) == NULL) {
363
ret = -1;
364
goto PKG_LOOP_END;
365
}
366
367
entry->flags |= HR_SWINSTALLED_FOUND;
368
entry->id = &oid_zeroDotZero;
369
entry->type = (int32_t)SWI_APPLICATION;
370
371
entry->date_len = make_date_time(entry->date, &k_ts, 0);
372
}
373
374
if (errno != 0) {
375
syslog(LOG_ERR, "hrSWInstalledTable: readdir(\"%s\") failed:"
376
" %m", pkg_dir);
377
ret = -1;
378
} else {
379
/*
380
* save the timestamp of directory
381
* to avoid any further scanning
382
*/
383
os_pkg_last_change = sb.st_ctime;
384
}
385
PKG_LOOP_END:
386
(void)closedir(p_dir);
387
return (ret);
388
}
389
390
/**
391
* Refresh the installed software table.
392
*/
393
void
394
refresh_swins_tbl(void)
395
{
396
int ret;
397
struct swins_entry *entry, *entry_tmp;
398
399
if (this_tick - swins_tick < swins_tbl_refresh) {
400
HRDBG("no refresh needed");
401
return;
402
}
403
404
/* mark each entry as missing */
405
TAILQ_FOREACH(entry, &swins_tbl, link)
406
entry->flags &= ~HR_SWINSTALLED_FOUND;
407
408
ret = swins_get_packages();
409
410
TAILQ_FOREACH_SAFE(entry, &swins_tbl, link, entry_tmp)
411
if (!(entry->flags & HR_SWINSTALLED_FOUND) &&
412
!(entry->flags & HR_SWINSTALLED_IMMUTABLE))
413
swins_entry_delete(entry);
414
415
if (ret == 0)
416
swins_tick = this_tick;
417
}
418
419
/**
420
* Create and populate the package table
421
*/
422
void
423
init_swins_tbl(void)
424
{
425
426
if ((pkg_dir = malloc(sizeof(PATH_PKGDIR))) == NULL)
427
syslog(LOG_ERR, "%s: %m", __func__);
428
else
429
strcpy(pkg_dir, PATH_PKGDIR);
430
431
swins_get_OS_ident();
432
refresh_swins_tbl();
433
434
HRDBG("init done");
435
}
436
437
/**
438
* SNMP handler
439
*/
440
int
441
op_hrSWInstalledTable(struct snmp_context *ctx __unused,
442
struct snmp_value *value, u_int sub, u_int iidx __unused,
443
enum snmp_op curr_op)
444
{
445
struct swins_entry *entry;
446
447
refresh_swins_tbl();
448
449
switch (curr_op) {
450
451
case SNMP_OP_GETNEXT:
452
if ((entry = NEXT_OBJECT_INT(&swins_tbl,
453
&value->var, sub)) == NULL)
454
return (SNMP_ERR_NOSUCHNAME);
455
value->var.len = sub + 1;
456
value->var.subs[sub] = entry->index;
457
goto get;
458
459
case SNMP_OP_GET:
460
if ((entry = FIND_OBJECT_INT(&swins_tbl,
461
&value->var, sub)) == NULL)
462
return (SNMP_ERR_NOSUCHNAME);
463
goto get;
464
465
case SNMP_OP_SET:
466
if ((entry = FIND_OBJECT_INT(&swins_tbl,
467
&value->var, sub)) == NULL)
468
return (SNMP_ERR_NO_CREATION);
469
return (SNMP_ERR_NOT_WRITEABLE);
470
471
case SNMP_OP_ROLLBACK:
472
case SNMP_OP_COMMIT:
473
abort();
474
}
475
abort();
476
477
get:
478
switch (value->var.subs[sub - 1]) {
479
480
case LEAF_hrSWInstalledIndex:
481
value->v.integer = entry->index;
482
return (SNMP_ERR_NOERROR);
483
484
case LEAF_hrSWInstalledName:
485
return (string_get(value, entry->name, -1));
486
break;
487
488
case LEAF_hrSWInstalledID:
489
assert(entry->id != NULL);
490
value->v.oid = *entry->id;
491
return (SNMP_ERR_NOERROR);
492
493
case LEAF_hrSWInstalledType:
494
value->v.integer = entry->type;
495
return (SNMP_ERR_NOERROR);
496
497
case LEAF_hrSWInstalledDate:
498
return (string_get(value, entry->date, entry->date_len));
499
}
500
abort();
501
}
502
503
/**
504
* Scalars
505
*/
506
int
507
op_hrSWInstalled(struct snmp_context *ctx __unused,
508
struct snmp_value *value __unused, u_int sub,
509
u_int iidx __unused, enum snmp_op curr_op)
510
{
511
512
/* only SNMP GET is possible */
513
switch (curr_op) {
514
515
case SNMP_OP_GET:
516
goto get;
517
518
case SNMP_OP_SET:
519
return (SNMP_ERR_NOT_WRITEABLE);
520
521
case SNMP_OP_ROLLBACK:
522
case SNMP_OP_COMMIT:
523
case SNMP_OP_GETNEXT:
524
abort();
525
}
526
abort();
527
528
get:
529
switch (value->var.subs[sub - 1]) {
530
531
case LEAF_hrSWInstalledLastChange:
532
case LEAF_hrSWInstalledLastUpdateTime:
533
/*
534
* We always update the entire table so these two tick
535
* values should be equal.
536
*/
537
refresh_swins_tbl();
538
if (swins_tick <= start_tick)
539
value->v.uint32 = 0;
540
else {
541
uint64_t lastChange = swins_tick - start_tick;
542
543
/* may overflow the SNMP type */
544
value->v.uint32 =
545
(lastChange > UINT_MAX ? UINT_MAX : lastChange);
546
}
547
548
return (SNMP_ERR_NOERROR);
549
550
default:
551
abort();
552
}
553
}
554
555