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
31 * Host Resources MIB implementation for SNMPd: instrumentation for
35 #include <sys/limits.h>
37 #include <sys/sysctl.h>
38 #include <sys/utsname.h>
49 #include "hostres_snmp.h"
50 #include "hostres_oid.h"
51 #include "hostres_tree.h"
53 #define CONTENTS_FNAME "+CONTENTS"
55 enum SWInstalledType {
57 SWI_OPERATING_SYSTEM = 2,
58 SWI_DEVICE_DRIVER = 3,
62 #define SW_NAME_MLEN (64 + 1)
65 * This structure is used to hold a SNMP table entry
66 * for HOST-RESOURCES-MIB's hrSWInstalledTable
70 u_char *name; /* max len for this is SW_NAME_MLEN */
71 const struct asn_oid *id;
72 int32_t type; /* from enum SWInstalledType */
76 #define HR_SWINSTALLED_FOUND 0x001
77 #define HR_SWINSTALLED_IMMUTABLE 0x002
80 TAILQ_ENTRY(swins_entry) link;
82 TAILQ_HEAD(swins_tbl, swins_entry);
85 * Table to keep a conistent mapping between software and indexes.
87 struct swins_map_entry {
88 int32_t index; /* swins_entry::index */
89 u_char *name; /* map key,a copy of swins_entry::name*/
92 * next may be NULL if the respective hrSWInstalledTblEntry
93 * is (temporally) gone
95 struct swins_entry *entry;
97 STAILQ_ENTRY(swins_map_entry) link;
99 STAILQ_HEAD(swins_map, swins_map_entry);
101 /* map for consistent indexing */
102 static struct swins_map swins_map = STAILQ_HEAD_INITIALIZER(swins_map);
104 /* the head of the list with hrSWInstalledTable's entries */
105 static struct swins_tbl swins_tbl = TAILQ_HEAD_INITIALIZER(swins_tbl);
107 /* next int available for indexing the hrSWInstalledTable */
108 static uint32_t next_swins_index = 1;
110 /* last (agent) tick when hrSWInstalledTable was updated */
111 static uint64_t swins_tick;
113 /* maximum number of ticks between updates of network table */
114 uint32_t swins_tbl_refresh = HR_SWINS_TBL_REFRESH * 100;
116 /* package directory */
119 /* last change of package list */
120 static time_t os_pkg_last_change;
123 * Create a new entry into the hrSWInstalledTable
125 static struct swins_entry *
126 swins_entry_create(const char *name)
128 struct swins_entry *entry;
129 struct swins_map_entry *map;
131 STAILQ_FOREACH(map, &swins_map, link)
132 if (strcmp((const char *)map->name, name) == 0)
137 /* new object - get a new index */
138 if (next_swins_index > INT_MAX) {
139 syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap",
141 /* There isn't much we can do here.
142 * If the next_swins_index is consumed
143 * then we can't add entries to this table
144 * So it is better to exit - if the table is sparsed
145 * at the next agent run we can fill it fully.
147 errx(EX_SOFTWARE, "hrSWInstalledTable index wrap");
150 if ((map = malloc(sizeof(*map))) == NULL) {
151 syslog(LOG_ERR, "%s: %m", __func__ );
155 name_len = strlen(name) + 1;
156 if (name_len > SW_NAME_MLEN)
157 name_len = SW_NAME_MLEN;
159 if ((map->name = malloc(name_len)) == NULL) {
160 syslog(LOG_WARNING, "%s: %m", __func__);
165 map->index = next_swins_index++;
166 strlcpy((char *)map->name, name, name_len);
168 STAILQ_INSERT_TAIL(&swins_map, map, link);
170 HRDBG("%s added into hrSWInstalled at %d", name, map->index);
173 if ((entry = malloc(sizeof(*entry))) == NULL) {
174 syslog(LOG_WARNING, "%s: %m", __func__);
177 memset(entry, 0, sizeof(*entry));
179 if ((entry->name = strdup(map->name)) == NULL) {
180 syslog(LOG_WARNING, "%s: %m", __func__);
185 entry->index = map->index;
188 INSERT_OBJECT_INT(entry, &swins_tbl);
194 * Delete an entry in the hrSWInstalledTable
197 swins_entry_delete(struct swins_entry *entry)
199 struct swins_map_entry *map;
201 assert(entry != NULL);
203 TAILQ_REMOVE(&swins_tbl, entry, link);
205 STAILQ_FOREACH(map, &swins_map, link)
206 if (map->entry == entry) {
216 * Find an entry given it's name
218 static struct swins_entry *
219 swins_find_by_name(const char *name)
221 struct swins_entry *entry;
223 TAILQ_FOREACH(entry, &swins_tbl, link)
224 if (strcmp((const char*)entry->name, name) == 0)
230 * Finalize this table
235 struct swins_map_entry *n1;
237 while ((n1 = STAILQ_FIRST(&swins_map)) != NULL) {
238 STAILQ_REMOVE_HEAD(&swins_map, link);
239 if (n1->entry != NULL) {
240 TAILQ_REMOVE(&swins_tbl, n1->entry, link);
241 free(n1->entry->name);
247 assert(TAILQ_EMPTY(&swins_tbl));
251 * Get the *running* O/S identification
254 swins_get_OS_ident(void)
256 struct utsname os_id;
257 char os_string[SW_NAME_MLEN] = "";
258 struct swins_entry *entry;
263 if (uname(&os_id) == -1) {
264 syslog(LOG_WARNING, "%s: %m", __func__);
268 snprintf(os_string, sizeof(os_string), "%s: %s",
269 os_id.sysname, os_id.version);
271 if ((entry = swins_find_by_name(os_string)) != NULL ||
272 (entry = swins_entry_create(os_string)) == NULL)
275 entry->flags |= (HR_SWINSTALLED_FOUND | HR_SWINSTALLED_IMMUTABLE);
276 entry->id = &oid_zeroDotZero;
277 entry->type = (int32_t)SWI_OPERATING_SYSTEM;
278 memset(entry->date, 0, sizeof(entry->date));
280 if (OS_getSystemInitialLoadParameters(&boot) == SNMP_ERR_NOERROR &&
281 strlen(boot) > 0 && stat(boot, &sb) == 0 &&
282 localtime_r(&sb.st_ctime, &k_ts) != NULL)
283 entry->date_len = make_date_time(entry->date, &k_ts, 0);
287 * Read the installed packages
290 swins_get_packages(void)
297 struct swins_entry *entry;
301 /* initialisation may have failed */
304 if (stat(pkg_dir, &sb) != 0) {
305 syslog(LOG_ERR, "hrSWInstalledTable: stat(\"%s\") failed: %m",
309 if (!S_ISDIR(sb.st_mode)) {
310 syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" is not a directory",
314 if (sb.st_ctime <= os_pkg_last_change) {
315 HRDBG("no need to rescan installed packages -- "
316 "directory time-stamp unmodified");
318 TAILQ_FOREACH(entry, &swins_tbl, link)
319 entry->flags |= HR_SWINSTALLED_FOUND;
324 if ((p_dir = opendir(pkg_dir)) == NULL) {
325 syslog(LOG_ERR, "hrSWInstalledTable: opendir(\"%s\") failed: "
330 while (errno = 0, (ent = readdir(p_dir)) != NULL) {
331 HRDBG(" pkg file: %s", ent->d_name);
333 /* check that the contents file is a regular file */
334 if (asprintf(&pkg_file, "%s/%s/%s", pkg_dir, ent->d_name,
335 CONTENTS_FNAME) == -1)
338 if (stat(pkg_file, &sb) != 0 ) {
343 if (!S_ISREG(sb.st_mode)) {
344 syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" not a "
345 "regular file -- skipped", pkg_file);
351 /* read directory timestamp on package */
352 if (asprintf(&pkg_file, "%s/%s", pkg_dir, ent->d_name) == -1)
355 if (stat(pkg_file, &sb) == -1 ||
356 localtime_r(&sb.st_ctime, &k_ts) == NULL) {
362 /* update or create entry */
363 if ((entry = swins_find_by_name(ent->d_name)) == NULL &&
364 (entry = swins_entry_create(ent->d_name)) == NULL) {
369 entry->flags |= HR_SWINSTALLED_FOUND;
370 entry->id = &oid_zeroDotZero;
371 entry->type = (int32_t)SWI_APPLICATION;
373 entry->date_len = make_date_time(entry->date, &k_ts, 0);
377 syslog(LOG_ERR, "hrSWInstalledTable: readdir_r(\"%s\") failed:"
382 * save the timestamp of directory
383 * to avoid any further scanning
385 os_pkg_last_change = sb.st_ctime;
388 (void)closedir(p_dir);
393 * Refresh the installed software table.
396 refresh_swins_tbl(void)
399 struct swins_entry *entry, *entry_tmp;
401 if (this_tick - swins_tick < swins_tbl_refresh) {
402 HRDBG("no refresh needed");
406 /* mark each entry as missing */
407 TAILQ_FOREACH(entry, &swins_tbl, link)
408 entry->flags &= ~HR_SWINSTALLED_FOUND;
410 ret = swins_get_packages();
412 TAILQ_FOREACH_SAFE(entry, &swins_tbl, link, entry_tmp)
413 if (!(entry->flags & HR_SWINSTALLED_FOUND) &&
414 !(entry->flags & HR_SWINSTALLED_IMMUTABLE))
415 swins_entry_delete(entry);
418 swins_tick = this_tick;
422 * Create and populate the package table
428 if ((pkg_dir = malloc(sizeof(PATH_PKGDIR))) == NULL)
429 syslog(LOG_ERR, "%s: %m", __func__);
431 strcpy(pkg_dir, PATH_PKGDIR);
433 swins_get_OS_ident();
443 op_hrSWInstalledTable(struct snmp_context *ctx __unused,
444 struct snmp_value *value, u_int sub, u_int iidx __unused,
445 enum snmp_op curr_op)
447 struct swins_entry *entry;
453 case SNMP_OP_GETNEXT:
454 if ((entry = NEXT_OBJECT_INT(&swins_tbl,
455 &value->var, sub)) == NULL)
456 return (SNMP_ERR_NOSUCHNAME);
457 value->var.len = sub + 1;
458 value->var.subs[sub] = entry->index;
462 if ((entry = FIND_OBJECT_INT(&swins_tbl,
463 &value->var, sub)) == NULL)
464 return (SNMP_ERR_NOSUCHNAME);
468 if ((entry = FIND_OBJECT_INT(&swins_tbl,
469 &value->var, sub)) == NULL)
470 return (SNMP_ERR_NO_CREATION);
471 return (SNMP_ERR_NOT_WRITEABLE);
473 case SNMP_OP_ROLLBACK:
480 switch (value->var.subs[sub - 1]) {
482 case LEAF_hrSWInstalledIndex:
483 value->v.integer = entry->index;
484 return (SNMP_ERR_NOERROR);
486 case LEAF_hrSWInstalledName:
487 return (string_get(value, entry->name, -1));
490 case LEAF_hrSWInstalledID:
491 assert(entry->id != NULL);
492 value->v.oid = *entry->id;
493 return (SNMP_ERR_NOERROR);
495 case LEAF_hrSWInstalledType:
496 value->v.integer = entry->type;
497 return (SNMP_ERR_NOERROR);
499 case LEAF_hrSWInstalledDate:
500 return (string_get(value, entry->date, entry->date_len));
509 op_hrSWInstalled(struct snmp_context *ctx __unused,
510 struct snmp_value *value __unused, u_int sub,
511 u_int iidx __unused, enum snmp_op curr_op)
514 /* only SNMP GET is possible */
521 return (SNMP_ERR_NOT_WRITEABLE);
523 case SNMP_OP_ROLLBACK:
525 case SNMP_OP_GETNEXT:
531 switch (value->var.subs[sub - 1]) {
533 case LEAF_hrSWInstalledLastChange:
534 case LEAF_hrSWInstalledLastUpdateTime:
536 * We always update the entire table so these two tick
537 * values should be equal.
540 if (swins_tick <= start_tick)
543 uint64_t lastChange = swins_tick - start_tick;
545 /* may overflow the SNMP type */
547 (lastChange > UINT_MAX ? UINT_MAX : lastChange);
550 return (SNMP_ERR_NOERROR);