1154133Sharti /*-
2154133Sharti * Copyright (c) 2005-2006 The FreeBSD Project
3154133Sharti * All rights reserved.
4154133Sharti *
5154133Sharti * Author: Victor Cruceru <soc-victor@freebsd.org>
6154133Sharti *
7154133Sharti * Redistribution of this software and documentation and use in source and
8154133Sharti * binary forms, with or without modification, are permitted provided that
9154133Sharti * the following conditions are met:
10154133Sharti *
11154133Sharti * 1. Redistributions of source code or documentation must retain the above
12154133Sharti *    copyright notice, this list of conditions and the following disclaimer.
13154133Sharti * 2. Redistributions in binary form must reproduce the above copyright
14154133Sharti *    notice, this list of conditions and the following disclaimer in the
15154133Sharti *    documentation and/or other materials provided with the distribution.
16154133Sharti *
17154133Sharti * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18154133Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19154133Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20154133Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21154133Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22154133Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23154133Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24154133Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25154133Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26154133Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27154133Sharti * SUCH DAMAGE.
28154133Sharti *
29154133Sharti * $FreeBSD: stable/10/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c 325396 2017-11-04 14:56:58Z pfg $
30154133Sharti */
31154133Sharti
32154133Sharti/*
33154133Sharti * Host Resources MIB: hrDeviceTable implementation for SNMPd.
34154133Sharti */
35154133Sharti
36154133Sharti#include <sys/un.h>
37154133Sharti#include <sys/limits.h>
38154133Sharti
39154133Sharti#include <assert.h>
40154133Sharti#include <err.h>
41154133Sharti#include <errno.h>
42154133Sharti#include <stdlib.h>
43154133Sharti#include <string.h>
44154133Sharti#include <syslog.h>
45154133Sharti#include <unistd.h>
46160341Sharti#include <sysexits.h>
47154133Sharti
48154133Sharti#include "hostres_snmp.h"
49154133Sharti#include "hostres_oid.h"
50154133Sharti#include "hostres_tree.h"
51154133Sharti
52160341Sharti#define FREE_DEV_STRUCT(entry_p) do {		\
53160341Sharti	free(entry_p->name);			\
54160341Sharti	free(entry_p->location);		\
55160341Sharti	free(entry_p->descr);			\
56160341Sharti	free(entry_p);				\
57160341Sharti} while (0)
58160341Sharti
59154133Sharti/*
60154133Sharti * Status of a device
61154133Sharti */
62154133Shartienum DeviceStatus {
63154133Sharti	DS_UNKNOWN	= 1,
64154133Sharti	DS_RUNNING	= 2,
65154133Sharti	DS_WARNING	= 3,
66154133Sharti	DS_TESTING	= 4,
67154133Sharti	DS_DOWN		= 5
68154133Sharti};
69154133Sharti
70154133ShartiTAILQ_HEAD(device_tbl, device_entry);
71154133Sharti
72154133Sharti/* the head of the list with hrDeviceTable's entries */
73154133Shartistatic struct device_tbl device_tbl = TAILQ_HEAD_INITIALIZER(device_tbl);
74154133Sharti
75154133Sharti/* Table used for consistent device table indexing. */
76154133Shartistruct device_map device_map = STAILQ_HEAD_INITIALIZER(device_map);
77154133Sharti
78154133Sharti/* next int available for indexing the hrDeviceTable */
79154133Shartistatic uint32_t next_device_index = 1;
80154133Sharti
81154133Sharti/* last (agent) tick when hrDeviceTable was updated */
82154133Shartistatic uint64_t device_tick = 0;
83154133Sharti
84154133Sharti/* maximum number of ticks between updates of device table */
85154133Shartiuint32_t device_tbl_refresh = 10 * 100;
86154133Sharti
87154133Sharti/* socket for /var/run/devd.pipe */
88154133Shartistatic int devd_sock = -1;
89154133Sharti
90154133Sharti/* used to wait notifications from /var/run/devd.pipe */
91154133Shartistatic void *devd_fd;
92154133Sharti
93154133Sharti/* some constants */
94154133Shartistatic const struct asn_oid OIDX_hrDeviceProcessor_c = OIDX_hrDeviceProcessor;
95154133Shartistatic const struct asn_oid OIDX_hrDeviceOther_c = OIDX_hrDeviceOther;
96154133Sharti
97154133Sharti/**
98154133Sharti * Create a new entry out of thin air.
99154133Sharti */
100154133Shartistruct device_entry *
101154133Shartidevice_entry_create(const char *name, const char *location, const char *descr)
102154133Sharti{
103160341Sharti	struct device_entry *entry = NULL;
104160341Sharti	struct device_map_entry *map = NULL;
105160341Sharti	size_t name_len;
106160341Sharti	size_t location_len;
107154133Sharti
108154133Sharti	assert((name[0] != 0) || (location[0] != 0));
109154133Sharti
110154133Sharti	if (name[0] == 0 && location[0] == 0)
111154133Sharti		return (NULL);
112154133Sharti
113160341Sharti	STAILQ_FOREACH(map, &device_map, link) {
114160341Sharti		assert(map->name_key != NULL);
115160341Sharti		assert(map->location_key != NULL);
116154133Sharti
117154133Sharti		if (strcmp(map->name_key, name) == 0 &&
118154133Sharti		    strcmp(map->location_key, location) == 0) {
119154133Sharti			break;
120154133Sharti		}
121160341Sharti	}
122154133Sharti
123154133Sharti	if (map == NULL) {
124154133Sharti		/* new object - get a new index */
125154133Sharti		if (next_device_index > INT_MAX) {
126310900Sngie			syslog(LOG_ERR,
127154133Sharti			    "%s: hrDeviceTable index wrap", __func__);
128160341Sharti			/* There isn't much we can do here.
129160341Sharti			 * If the next_swins_index is consumed
130160341Sharti			 * then we can't add entries to this table
131160341Sharti			 * So it is better to exit - if the table is sparsed
132160341Sharti			 * at the next agent run we can fill it fully.
133160341Sharti			 */
134160341Sharti			errx(EX_SOFTWARE, "hrDeviceTable index wrap");
135160341Sharti			/* not reachable */
136154133Sharti		}
137154133Sharti
138154133Sharti		if ((map = malloc(sizeof(*map))) == NULL) {
139154133Sharti			syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
140154133Sharti			return (NULL);
141154133Sharti		}
142154133Sharti
143160341Sharti		map->entry_p = NULL;
144160341Sharti
145160341Sharti		name_len = strlen(name) + 1;
146160341Sharti		if (name_len > DEV_NAME_MLEN)
147160341Sharti			name_len = DEV_NAME_MLEN;
148160341Sharti
149160341Sharti		if ((map->name_key = malloc(name_len)) == NULL) {
150160341Sharti			syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
151160341Sharti			free(map);
152160341Sharti			return (NULL);
153160341Sharti		}
154160341Sharti
155160341Sharti		location_len = strlen(location) + 1;
156160341Sharti		if (location_len > DEV_LOC_MLEN)
157160341Sharti			location_len = DEV_LOC_MLEN;
158160341Sharti
159160341Sharti		if ((map->location_key = malloc(location_len )) == NULL) {
160160341Sharti			syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
161160341Sharti			free(map->name_key);
162160341Sharti			free(map);
163160341Sharti			return (NULL);
164160341Sharti		}
165160341Sharti
166154133Sharti		map->hrIndex = next_device_index++;
167154133Sharti
168160341Sharti		strlcpy(map->name_key, name, name_len);
169160341Sharti		strlcpy(map->location_key, location, location_len);
170154133Sharti
171154133Sharti		STAILQ_INSERT_TAIL(&device_map, map, link);
172154133Sharti		HRDBG("%s at %s added into hrDeviceMap at index=%d",
173154133Sharti		    name, location, map->hrIndex);
174154133Sharti	} else {
175154133Sharti		HRDBG("%s at %s exists in hrDeviceMap index=%d",
176154133Sharti		    name, location, map->hrIndex);
177154133Sharti	}
178154133Sharti
179160341Sharti	if ((entry = malloc(sizeof(*entry))) == NULL) {
180160341Sharti		syslog(LOG_WARNING, "hrDeviceTable: %s: %m", __func__);
181160341Sharti		return (NULL);
182160341Sharti	}
183160341Sharti	memset(entry, 0, sizeof(*entry));
184160341Sharti
185154133Sharti	entry->index = map->hrIndex;
186160341Sharti	map->entry_p = entry;
187154133Sharti
188160341Sharti	if ((entry->name = strdup(map->name_key)) == NULL) {
189160341Sharti		syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
190160341Sharti		free(entry);
191160341Sharti		return (NULL);
192160341Sharti	}
193154133Sharti
194160341Sharti	if ((entry->location = strdup(map->location_key)) == NULL) {
195160341Sharti		syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
196160341Sharti		free(entry->name);
197160341Sharti		free(entry);
198160341Sharti		return (NULL);
199160341Sharti	}
200160341Sharti
201160341Sharti	/*
202160341Sharti	 * From here till the end of this function we reuse name_len
203228990Suqs	 * for a different purpose - for device_entry::descr
204160341Sharti	 */
205154133Sharti	if (name[0] != '\0')
206160341Sharti		name_len = strlen(name) + strlen(descr) +
207160341Sharti		    strlen(": ") + 1;
208154133Sharti	else
209160341Sharti		name_len = strlen(location) + strlen(descr) +
210160341Sharti		    strlen("unknown at : ") + 1;
211160341Sharti
212160341Sharti	if (name_len > DEV_DESCR_MLEN)
213160341Sharti		name_len = DEV_DESCR_MLEN;
214160341Sharti
215160341Sharti	if ((entry->descr = malloc(name_len )) == NULL) {
216160341Sharti		syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
217160341Sharti		free(entry->name);
218160341Sharti		free(entry->location);
219160341Sharti		free(entry);
220160341Sharti		return (NULL);
221160341Sharti	}
222160341Sharti
223160341Sharti	memset(&entry->descr[0], '\0', name_len);
224160341Sharti
225160341Sharti	if (name[0] != '\0')
226160341Sharti		snprintf(entry->descr, name_len,
227160341Sharti		    "%s: %s", name, descr);
228160341Sharti	else
229160341Sharti		snprintf(entry->descr, name_len,
230154133Sharti		    "unknown at %s: %s", location, descr);
231154133Sharti
232160341Sharti	entry->id = &oid_zeroDotZero;	/* unknown id - FIXME */
233160341Sharti	entry->status = (u_int)DS_UNKNOWN;
234154133Sharti	entry->errors = 0;
235160341Sharti	entry->type = &OIDX_hrDeviceOther_c;
236154133Sharti
237154133Sharti	INSERT_OBJECT_INT(entry, &device_tbl);
238154133Sharti
239154133Sharti	return (entry);
240154133Sharti}
241154133Sharti
242154133Sharti/**
243154133Sharti * Create a new entry into the device table.
244154133Sharti */
245154133Shartistatic struct device_entry *
246154133Shartidevice_entry_create_devinfo(const struct devinfo_dev *dev_p)
247154133Sharti{
248154133Sharti
249154133Sharti	assert(dev_p->dd_name != NULL);
250154133Sharti	assert(dev_p->dd_location != NULL);
251154133Sharti
252154133Sharti	return (device_entry_create(dev_p->dd_name, dev_p->dd_location,
253154133Sharti	    dev_p->dd_desc));
254154133Sharti}
255154133Sharti
256154133Sharti/**
257154133Sharti * Delete an entry from the device table.
258154133Sharti */
259160341Shartivoid
260154133Shartidevice_entry_delete(struct device_entry *entry)
261154133Sharti{
262154133Sharti	struct device_map_entry *map;
263154133Sharti
264154133Sharti	assert(entry != NULL);
265154133Sharti
266154133Sharti	TAILQ_REMOVE(&device_tbl, entry, link);
267154133Sharti
268154133Sharti	STAILQ_FOREACH(map, &device_map, link)
269154133Sharti		if (map->entry_p == entry) {
270154133Sharti			map->entry_p = NULL;
271154133Sharti			break;
272154133Sharti		}
273160341Sharti
274160341Sharti	FREE_DEV_STRUCT(entry);
275154133Sharti}
276154133Sharti
277154133Sharti/**
278154133Sharti * Find an entry given its name and location
279154133Sharti */
280154133Shartistatic struct device_entry *
281154133Shartidevice_find_by_dev(const struct devinfo_dev *dev_p)
282154133Sharti{
283154133Sharti	struct device_map_entry  *map;
284154133Sharti
285154133Sharti	assert(dev_p != NULL);
286154133Sharti
287154133Sharti	STAILQ_FOREACH(map, &device_map, link)
288154133Sharti		if (strcmp(map->name_key, dev_p->dd_name) == 0 &&
289154133Sharti		    strcmp(map->location_key, dev_p->dd_location) == 0)
290154133Sharti		    	return (map->entry_p);
291154133Sharti	return (NULL);
292154133Sharti}
293154133Sharti
294154133Sharti/**
295154133Sharti * Find an entry given its index.
296154133Sharti */
297154133Shartistruct device_entry *
298154133Shartidevice_find_by_index(int32_t idx)
299154133Sharti{
300154133Sharti	struct device_entry *entry;
301154133Sharti
302154133Sharti	TAILQ_FOREACH(entry, &device_tbl, link)
303154133Sharti		if (entry->index == idx)
304154133Sharti			return (entry);
305154133Sharti	return (NULL);
306154133Sharti}
307154133Sharti
308154133Sharti/**
309154133Sharti * Find an device entry given its name.
310154133Sharti */
311154133Shartistruct device_entry *
312154133Shartidevice_find_by_name(const char *dev_name)
313154133Sharti{
314154133Sharti	struct device_map_entry *map;
315154133Sharti
316154133Sharti	assert(dev_name != NULL);
317154133Sharti
318154133Sharti	STAILQ_FOREACH(map, &device_map, link)
319154133Sharti		if (strcmp(map->name_key, dev_name) == 0)
320154133Sharti			return (map->entry_p);
321154133Sharti
322154133Sharti	return (NULL);
323154133Sharti}
324154133Sharti
325154133Sharti/**
326154133Sharti * Find out the type of device. CPU only currently.
327154133Sharti */
328154133Shartistatic void
329160341Shartidevice_get_type(struct devinfo_dev *dev_p, const struct asn_oid **out_type_p)
330154133Sharti{
331154133Sharti
332154133Sharti	assert(dev_p != NULL);
333154133Sharti	assert(out_type_p != NULL);
334154133Sharti
335154133Sharti	if (dev_p == NULL)
336154133Sharti		return;
337154133Sharti
338154133Sharti	if (strncmp(dev_p->dd_name, "cpu", strlen("cpu")) == 0 &&
339154133Sharti	    strstr(dev_p->dd_location, ".CPU") != NULL) {
340160341Sharti		*out_type_p = &OIDX_hrDeviceProcessor_c;
341154133Sharti		return;
342154133Sharti	}
343154133Sharti}
344154133Sharti
345154133Sharti/**
346154133Sharti * Get the status of a device
347154133Sharti */
348154133Shartistatic enum DeviceStatus
349154133Shartidevice_get_status(struct devinfo_dev *dev)
350154133Sharti{
351154133Sharti
352154133Sharti	assert(dev != NULL);
353154133Sharti
354154133Sharti	switch (dev->dd_state) {
355199291Sattilio	case DS_ALIVE:			/* probe succeeded */
356199291Sattilio	case DS_NOTPRESENT:		/* not probed or probe failed */
357154133Sharti		return (DS_DOWN);
358199291Sattilio	case DS_ATTACHED:		/* attach method called */
359199291Sattilio	case DS_BUSY:			/* device is open */
360154133Sharti		return (DS_RUNNING);
361154133Sharti	default:
362154133Sharti		return (DS_UNKNOWN);
363154133Sharti	}
364154133Sharti}
365154133Sharti
366154133Sharti/**
367154133Sharti * Get the info for the given device and then recursively process all
368154133Sharti * child devices.
369154133Sharti */
370154133Shartistatic int
371154133Shartidevice_collector(struct devinfo_dev *dev, void *arg)
372154133Sharti{
373154133Sharti	struct device_entry *entry;
374154133Sharti
375154133Sharti	HRDBG("%llu/%llu name='%s' desc='%s' drivername='%s' location='%s'",
376154133Sharti	    (unsigned long long)dev->dd_handle,
377154133Sharti	    (unsigned long long)dev->dd_parent, dev->dd_name, dev->dd_desc,
378154133Sharti	    dev->dd_drivername, dev->dd_location);
379154133Sharti
380154133Sharti	if (dev->dd_name[0] != '\0' || dev->dd_location[0] != '\0') {
381154133Sharti		HRDBG("ANALYZING dev %s at %s",
382154133Sharti		    dev->dd_name, dev->dd_location);
383154133Sharti
384154133Sharti		if ((entry = device_find_by_dev(dev)) != NULL) {
385154133Sharti			entry->flags |= HR_DEVICE_FOUND;
386154133Sharti			entry->status = (u_int)device_get_status(dev);
387154133Sharti		} else if ((entry = device_entry_create_devinfo(dev)) != NULL) {
388154133Sharti			device_get_type(dev, &entry->type);
389154133Sharti
390154133Sharti			entry->flags |= HR_DEVICE_FOUND;
391154133Sharti			entry->status = (u_int)device_get_status(dev);
392154133Sharti		}
393154133Sharti	} else {
394154133Sharti		HRDBG("SKIPPED unknown device at location '%s'",
395154133Sharti		    dev->dd_location );
396154133Sharti	}
397154133Sharti
398154133Sharti	return (devinfo_foreach_device_child(dev, device_collector, arg));
399154133Sharti}
400154133Sharti
401154133Sharti/**
402154133Sharti * Create the socket to the device daemon.
403154133Sharti */
404154133Shartistatic int
405154133Sharticreate_devd_socket(void)
406154133Sharti{
407154133Sharti	int d_sock;
408154133Sharti 	struct sockaddr_un devd_addr;
409154133Sharti
410154133Sharti 	bzero(&devd_addr, sizeof(struct sockaddr_un));
411154133Sharti
412154133Sharti 	if ((d_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
413154133Sharti 		syslog(LOG_ERR, "Failed to create the socket for %s: %m",
414154133Sharti		    PATH_DEVD_PIPE);
415154133Sharti 		return (-1);
416154133Sharti 	}
417154133Sharti
418154133Sharti 	devd_addr.sun_family = PF_LOCAL;
419154133Sharti	devd_addr.sun_len = sizeof(devd_addr);
420154133Sharti 	strlcpy(devd_addr.sun_path, PATH_DEVD_PIPE,
421154133Sharti	    sizeof(devd_addr.sun_path) - 1);
422154133Sharti
423154133Sharti 	if (connect(d_sock, (struct sockaddr *)&devd_addr,
424154133Sharti	    sizeof(devd_addr)) == -1) {
425154133Sharti 		syslog(LOG_ERR,"Failed to connect socket for %s: %m",
426154133Sharti		    PATH_DEVD_PIPE);
427154133Sharti 		if (close(d_sock) < 0 )
428154133Sharti 			syslog(LOG_ERR,"Failed to close socket for %s: %m",
429154133Sharti			    PATH_DEVD_PIPE);
430154133Sharti		return (-1);
431154133Sharti 	}
432154133Sharti
433154133Sharti 	return (d_sock);
434154133Sharti}
435154133Sharti
436154133Sharti/*
437154133Sharti * Event on the devd socket.
438160341Sharti *
439154133Sharti * We should probably directly process entries here. For simplicity just
440154133Sharti * call the refresh routine with the force flag for now.
441154133Sharti */
442154133Shartistatic void
443154133Shartidevd_socket_callback(int fd, void *arg __unused)
444154133Sharti{
445154133Sharti	char buf[512];
446154133Sharti	int read_len = -1;
447154133Sharti
448154133Sharti	assert(fd == devd_sock);
449154133Sharti
450154133Sharti	HRDBG("called");
451154133Sharti
452223933Saeagain:
453223933Sae	read_len = read(fd, buf, sizeof(buf));
454154133Sharti	if (read_len < 0) {
455154133Sharti		if (errno == EBADF) {
456154133Sharti			devd_sock = -1;
457154133Sharti			if (devd_fd != NULL) {
458154133Sharti				fd_deselect(devd_fd);
459154133Sharti				devd_fd = NULL;
460154133Sharti			}
461154133Sharti			syslog(LOG_ERR, "Closing devd_fd, revert to "
462154133Sharti			    "devinfo polling");
463154133Sharti		}
464154133Sharti
465154133Sharti	} else if (read_len == 0) {
466154133Sharti		syslog(LOG_ERR, "zero bytes read from devd pipe... "
467154133Sharti		    "closing socket!");
468154133Sharti
469154133Sharti		if (close(devd_sock) < 0 )
470154133Sharti 			syslog(LOG_ERR, "Failed to close devd socket: %m");
471154133Sharti
472154133Sharti		devd_sock = -1;
473154133Sharti		if (devd_fd != NULL) {
474154133Sharti			fd_deselect(devd_fd);
475154133Sharti			devd_fd = NULL;
476154133Sharti		}
477154133Sharti		syslog(LOG_ERR, "Closing devd_fd, revert to devinfo polling");
478154133Sharti
479154133Sharti	} else {
480223933Sae		if (read_len == sizeof(buf))
481223933Sae			goto again;
482325396Spfg		/* Only refresh device table on a device add or remove event. */
483325396Spfg		if (buf[0] == '+' || buf[0] == '-')
484325396Spfg			refresh_device_tbl(1);
485154133Sharti	}
486154133Sharti}
487154133Sharti
488154133Sharti/**
489154133Sharti * Initialize and populate the device table.
490154133Sharti */
491154133Shartivoid
492154133Shartiinit_device_tbl(void)
493154133Sharti{
494154133Sharti
495154133Sharti	/* initially populate table for the other tables */
496154133Sharti	refresh_device_tbl(1);
497154133Sharti
498154133Sharti	/* no problem if that fails - just use polling mode */
499154133Sharti	devd_sock = create_devd_socket();
500154133Sharti}
501154133Sharti
502154133Sharti/**
503154133Sharti * Start devd(8) monitoring.
504154133Sharti */
505154133Shartivoid
506154133Shartistart_device_tbl(struct lmodule *mod)
507154133Sharti{
508154133Sharti
509154133Sharti	if (devd_sock > 0) {
510154133Sharti		devd_fd = fd_select(devd_sock, devd_socket_callback, NULL, mod);
511154133Sharti		if (devd_fd == NULL)
512154133Sharti			syslog(LOG_ERR, "fd_select failed on devd socket: %m");
513154133Sharti	}
514154133Sharti}
515154133Sharti
516154133Sharti/**
517154133Sharti * Finalization routine for hrDeviceTable
518154133Sharti * It destroys the lists and frees any allocated heap memory
519154133Sharti */
520154133Shartivoid
521154133Shartifini_device_tbl(void)
522154133Sharti{
523154133Sharti	struct device_map_entry *n1;
524154133Sharti
525154133Sharti	if (devd_fd != NULL)
526154133Sharti		fd_deselect(devd_fd);
527154133Sharti
528154133Sharti	if (devd_sock != -1)
529154133Sharti		(void)close(devd_sock);
530154133Sharti
531154133Sharti	devinfo_free();
532154133Sharti
533154133Sharti     	while ((n1 = STAILQ_FIRST(&device_map)) != NULL) {
534154133Sharti		STAILQ_REMOVE_HEAD(&device_map, link);
535154133Sharti		if (n1->entry_p != NULL) {
536154133Sharti			TAILQ_REMOVE(&device_tbl, n1->entry_p, link);
537160341Sharti			FREE_DEV_STRUCT(n1->entry_p);
538154133Sharti		}
539160341Sharti		free(n1->name_key);
540160341Sharti		free(n1->location_key);
541154133Sharti		free(n1);
542154133Sharti     	}
543154133Sharti	assert(TAILQ_EMPTY(&device_tbl));
544154133Sharti}
545154133Sharti
546154133Sharti/**
547154133Sharti * Refresh routine for hrDeviceTable. We don't refresh here if the devd socket
548154133Sharti * is open, because in this case we have the actual information always. We
549154133Sharti * also don't refresh when the table is new enough (if we don't have a devd
550154133Sharti * socket). In either case a refresh can be forced by passing a non-zero value.
551154133Sharti */
552154133Shartivoid
553154133Shartirefresh_device_tbl(int force)
554154133Sharti{
555154133Sharti	struct device_entry *entry, *entry_tmp;
556154133Sharti	struct devinfo_dev *dev_root;
557154133Sharti	static int act = 0;
558154133Sharti
559154133Sharti	if (!force && (devd_sock >= 0 ||
560154133Sharti	   (device_tick != 0 && this_tick - device_tick < device_tbl_refresh))){
561154133Sharti		HRDBG("no refresh needed");
562154133Sharti		return;
563154133Sharti	}
564154133Sharti
565154133Sharti	if (act) {
566154133Sharti		syslog(LOG_ERR, "%s: recursive call", __func__);
567154133Sharti		return;
568154133Sharti	}
569154133Sharti
570154133Sharti	if (devinfo_init() != 0) {
571154133Sharti		syslog(LOG_ERR,"%s: devinfo_init failed: %m", __func__);
572154133Sharti		return;
573154133Sharti	}
574154133Sharti
575154133Sharti	act = 1;
576154133Sharti	if ((dev_root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL){
577154133Sharti		syslog(LOG_ERR, "%s: can't get the root device: %m", __func__);
578154133Sharti		goto out;
579154133Sharti	}
580154133Sharti
581154133Sharti	/* mark each entry as missing */
582154133Sharti	TAILQ_FOREACH(entry, &device_tbl, link)
583154133Sharti		entry->flags &= ~HR_DEVICE_FOUND;
584154133Sharti
585154133Sharti	if (devinfo_foreach_device_child(dev_root, device_collector, NULL))
586154133Sharti		syslog(LOG_ERR, "%s: devinfo_foreach_device_child failed",
587154133Sharti		    __func__);
588154133Sharti
589154133Sharti	/*
590154133Sharti	 * Purge items that disappeared
591154133Sharti	 */
592154133Sharti	TAILQ_FOREACH_SAFE(entry, &device_tbl, link, entry_tmp) {
593154133Sharti		/*
594154133Sharti		 * If HR_DEVICE_IMMUTABLE bit is set then this means that
595154133Sharti		 * this entry was not detected by the above
596154133Sharti		 * devinfo_foreach_device() call. So we are not deleting
597154133Sharti		 * it there.
598154133Sharti		 */
599154133Sharti		if (!(entry->flags & HR_DEVICE_FOUND) &&
600154133Sharti		    !(entry->flags & HR_DEVICE_IMMUTABLE))
601154133Sharti			device_entry_delete(entry);
602154133Sharti	}
603154133Sharti
604154133Sharti	device_tick = this_tick;
605154133Sharti
606154133Sharti	/*
607154133Sharti	 * Force a refresh for the hrDiskStorageTable
608154133Sharti	 * XXX Why not the other dependen tables?
609154133Sharti	 */
610154133Sharti	refresh_disk_storage_tbl(1);
611154133Sharti
612154133Sharti  out:
613154133Sharti	devinfo_free();
614154133Sharti	act = 0;
615154133Sharti}
616154133Sharti
617154133Sharti/**
618154133Sharti * This is the implementation for a generated (by a SNMP tool)
619154133Sharti * function prototype, see hostres_tree.h
620154133Sharti * It handles the SNMP operations for hrDeviceTable
621154133Sharti */
622154133Shartiint
623154133Shartiop_hrDeviceTable(struct snmp_context *ctx __unused, struct snmp_value *value,
624154133Sharti    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
625154133Sharti{
626154133Sharti	struct device_entry *entry;
627154133Sharti
628154133Sharti	refresh_device_tbl(0);
629154133Sharti
630154133Sharti	switch (curr_op) {
631154133Sharti
632154133Sharti	case SNMP_OP_GETNEXT:
633154133Sharti		if ((entry = NEXT_OBJECT_INT(&device_tbl,
634154133Sharti		    &value->var, sub)) == NULL)
635154133Sharti			return (SNMP_ERR_NOSUCHNAME);
636154133Sharti		value->var.len = sub + 1;
637154133Sharti		value->var.subs[sub] = entry->index;
638154133Sharti		goto get;
639154133Sharti
640154133Sharti	case SNMP_OP_GET:
641154133Sharti		if ((entry = FIND_OBJECT_INT(&device_tbl,
642154133Sharti		    &value->var, sub)) == NULL)
643154133Sharti			return (SNMP_ERR_NOSUCHNAME);
644154133Sharti		goto get;
645154133Sharti
646154133Sharti	case SNMP_OP_SET:
647154133Sharti		if ((entry = FIND_OBJECT_INT(&device_tbl,
648154133Sharti		    &value->var, sub)) == NULL)
649154133Sharti			return (SNMP_ERR_NO_CREATION);
650154133Sharti		return (SNMP_ERR_NOT_WRITEABLE);
651154133Sharti
652154133Sharti	case SNMP_OP_ROLLBACK:
653154133Sharti	case SNMP_OP_COMMIT:
654154133Sharti		abort();
655154133Sharti	}
656154133Sharti	abort();
657154133Sharti
658154133Sharti  get:
659154133Sharti	switch (value->var.subs[sub - 1]) {
660154133Sharti
661154133Sharti	case LEAF_hrDeviceIndex:
662154133Sharti		value->v.integer = entry->index;
663154133Sharti		return (SNMP_ERR_NOERROR);
664154133Sharti
665154133Sharti	case LEAF_hrDeviceType:
666160341Sharti		assert(entry->type != NULL);
667160341Sharti	  	value->v.oid = *(entry->type);
668154133Sharti	  	return (SNMP_ERR_NOERROR);
669154133Sharti
670154133Sharti	case LEAF_hrDeviceDescr:
671154133Sharti	  	return (string_get(value, entry->descr, -1));
672154133Sharti
673154133Sharti	case LEAF_hrDeviceID:
674160341Sharti		value->v.oid = *(entry->id);
675154133Sharti	  	return (SNMP_ERR_NOERROR);
676154133Sharti
677154133Sharti	case LEAF_hrDeviceStatus:
678154133Sharti	  	value->v.integer = entry->status;
679154133Sharti	  	return (SNMP_ERR_NOERROR);
680154133Sharti
681154133Sharti	case LEAF_hrDeviceErrors:
682154133Sharti	  	value->v.uint32 = entry->errors;
683154133Sharti	  	return (SNMP_ERR_NOERROR);
684154133Sharti	}
685154133Sharti	abort();
686154133Sharti}
687