hostres_fs_tbl.c revision 154137
1/*-
2 * Copyright (c) 2005-2006 The FreeBSD Project
3 * All rights reserved.
4 *
5 * Author: Victor Cruceru <soc-victor@freebsd.org>
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 * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c 154133 2006-01-09 12:33:45Z harti $
30 */
31
32/*
33 * Host Resources MIB for SNMPd. Implementation for hrFSTable
34 */
35
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/sysctl.h>
39#include <sys/mount.h>
40
41#include <assert.h>
42#include <err.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46
47#include "hostres_snmp.h"
48#include "hostres_oid.h"
49#include "hostres_tree.h"
50
51/*
52 * File system access enum
53 */
54enum hrFSAccess {
55	FS_READ_WRITE = 1,
56	FS_READ_ONLY  = 2
57};
58
59/*
60 * This structure is used to hold a SNMP table entry
61 * for HOST-RESOURCES-MIB's hrFSTable
62 */
63struct fs_entry {
64	int32_t		index;
65	u_char		mountPoint[128 + 1];
66	u_char		remoteMountPoint[128 + 1];
67	const struct asn_oid *type;
68	int32_t		access;		/* enum hrFSAccess, see above */
69	int32_t		bootable;	/* TruthValue */
70	int32_t		storageIndex;	/* hrStorageTblEntry::index */
71	u_char		lastFullBackupDate[11];
72	u_char		lastPartialBackupDate[11];
73#define HR_FS_FOUND 0x001
74	uint32_t	flags;		/* not in mib table, for internal use */
75	TAILQ_ENTRY(fs_entry) link;
76};
77TAILQ_HEAD(fs_tbl, fs_entry);
78
79/*
80 * Next structure is used to keep o list of mappings from a specific name
81 * (a_name) to an entry in the hrFSTblEntry. We are trying to keep the same
82 * index for a specific name at least for the duration of one SNMP agent run.
83 */
84struct fs_map_entry {
85	int32_t		hrIndex;	/* used for hrFSTblEntry::index */
86	u_char		a_name[128 + 1];/* map key */
87
88	/* may be NULL if the respective hrFSTblEntry is (temporally) gone */
89	struct fs_entry *entry;
90	STAILQ_ENTRY(fs_map_entry) 	link;
91};
92STAILQ_HEAD(fs_map, fs_map_entry);
93
94/* head of the list with hrFSTable's entries */
95static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl);
96
97/* for consistent table indexing */
98static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map);
99
100/* next index available for hrFSTable */
101static uint32_t	next_fs_index = 1;
102
103/* last tick when hrFSTable was updated */
104static uint64_t fs_tick;
105
106/* maximum number of ticks between refreshs */
107uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100;
108
109/* some constants */
110static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS;
111static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660;
112static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS;
113static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2;
114static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther;
115static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32;
116static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS;
117static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware;
118static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS;
119static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown;
120
121/* file system type map */
122static const struct {
123	const char		*str;	/* the type string */
124	const struct asn_oid	*oid;	/* the OID to return */
125} fs_type_map[] = {
126	{ "ufs",	&OIDX_hrFSBerkeleyFFS_c },
127	{ "cd9660",	&OIDX_hrFSiso9660_c },
128	{ "nfs",	&OIDX_hrFSNFS_c },
129	{ "ext2fs",	&OIDX_hrFSLinuxExt2_c },
130	{ "procfs",	&OIDX_hrFSOther_c },
131	{ "devfs",	&OIDX_hrFSOther_c },
132	{ "msdosfs",	&OIDX_hrFSFAT32_c },
133	{ "ntfs",	&OIDX_hrFSNTFS_c },
134	{ "nwfs",	&OIDX_hrFSNetware_c },
135	{ "hpfs",	&OIDX_hrFSHPFS_c },
136	{ "smbfs",	&OIDX_hrFSOther_c },
137};
138#define	N_FS_TYPE_MAP	(sizeof(fs_type_map) / sizeof(fs_type_map[0]))
139
140/**
141 * Create an entry into the FS table and an entry in the map (if needed).
142 */
143static struct fs_entry *
144fs_entry_create(const char *name)
145{
146	struct fs_entry	*entry;
147	struct fs_map_entry *map;
148
149	if ((entry = malloc(sizeof(*entry))) == NULL) {
150		syslog(LOG_WARNING, "%s: %m", __func__);
151		return (NULL);
152	}
153
154	strlcpy(entry->mountPoint, name, sizeof(entry->mountPoint));
155
156	STAILQ_FOREACH(map, &fs_map, link)
157		if (strncmp(map->a_name, entry->mountPoint,
158		    sizeof(map->a_name) - 1) == 0) {
159			entry->index = map->hrIndex;
160			map->entry = entry;
161			break;
162		}
163
164	if (map == NULL) {
165		/* new object - get a new index */
166		if (next_fs_index > INT_MAX) {
167			/* XXX no other sensible reaction? */
168		        syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__);
169			return (NULL);
170		}
171
172		if ((map = malloc(sizeof(*map))) == NULL) {
173			syslog(LOG_ERR, "%s: %m", __func__);
174			free(entry);
175			return (NULL);
176		}
177
178		map->hrIndex = next_fs_index++;
179		strlcpy(map->a_name, entry->mountPoint, sizeof(map->a_name));
180		map->entry = entry;
181
182		STAILQ_INSERT_TAIL(&fs_map, map, link);
183
184		HRDBG("%s added into hrFSMap at index=%d", name, map->hrIndex);
185	} else {
186		HRDBG("%s exists in hrFSMap index=%d", name, map->hrIndex);
187	}
188
189	entry->index = map->hrIndex;
190
191	INSERT_OBJECT_INT(entry, &fs_tbl);
192
193	return (entry);
194}
195
196/**
197 * Delete an entry in the FS table.
198 */
199static void
200fs_entry_delete(struct fs_entry* entry)
201{
202	struct fs_map_entry *map;
203
204	TAILQ_REMOVE(&fs_tbl, entry, link);
205	STAILQ_FOREACH(map, &fs_map, link)
206		if (map->entry == entry) {
207			map->entry = NULL;
208			break;
209		}
210
211	free(entry);
212}
213
214/**
215 * Find a table entry by its name
216 */
217static struct fs_entry *
218fs_find_by_name(const char *name)
219{
220	struct fs_entry *entry;
221
222	TAILQ_FOREACH(entry, &fs_tbl, link)
223		if (strncmp(entry->mountPoint, name,
224		    sizeof(entry->mountPoint) - 1) == 0)
225			return (entry);
226
227	return (NULL);
228}
229
230/**
231 * Get rid of all data
232 */
233void
234fini_fs_tbl(void)
235{
236	struct fs_map_entry *n1;
237
238     	while ((n1 = STAILQ_FIRST(&fs_map)) != NULL) {
239		STAILQ_REMOVE_HEAD(&fs_map, link);
240		if (n1->entry != NULL) {
241			TAILQ_REMOVE(&fs_tbl, n1->entry, link);
242			free(n1->entry);
243		}
244		free(n1);
245     	}
246	assert(TAILQ_EMPTY(&fs_tbl));
247}
248
249/**
250 * Called before the refreshing is started from the storage table.
251 */
252void
253fs_tbl_pre_refresh(void)
254{
255	struct fs_entry *entry;
256
257	/* mark each entry as missisng */
258	TAILQ_FOREACH(entry, &fs_tbl, link)
259		entry->flags &= ~HR_FS_FOUND;
260}
261
262/**
263 * Called after refreshing from the storage table.
264 */
265void
266fs_tbl_post_refresh(void)
267{
268	struct fs_entry *entry, *entry_tmp;
269
270	/*
271	 * Purge items that disappeared
272	 */
273	TAILQ_FOREACH_SAFE(entry, &fs_tbl, link, entry_tmp)
274		if (!(entry->flags & HR_FS_FOUND))
275			fs_entry_delete(entry);
276
277	fs_tick = this_tick;
278}
279
280/*
281 * Refresh the FS table. This is done by forcing a refresh of the storage table.
282 */
283void
284refresh_fs_tbl(void)
285{
286
287	if (fs_tick == 0 || this_tick - fs_tick >= fs_tbl_refresh) {
288		refresh_storage_tbl(1);
289		HRDBG("refresh DONE");
290	}
291}
292
293/**
294 * Get the type OID for a given file system
295 */
296const struct asn_oid *
297fs_get_type(const struct statfs *fs_p)
298{
299	u_int t;
300
301	assert(fs_p != NULL);
302
303	for (t = 0; t < N_FS_TYPE_MAP; t++)
304		if (strcmp(fs_type_map[t].str, fs_p->f_fstypename) == 0)
305			return (fs_type_map[t].oid);
306
307	return (&OIDX_hrFSUnknown_c);
308}
309
310/*
311 * Given information returned from statfs(2) either create a new entry into
312 * the fs_tbl or refresh the entry if it is already there.
313 */
314void
315fs_tbl_process_statfs_entry(const struct statfs *fs_p, int32_t storage_idx)
316{
317	struct fs_entry *entry;
318
319	assert(fs_p != 0);
320
321	HRDBG("for hrStorageEntry::index %d", storage_idx);
322
323	if (fs_p == NULL)
324		return;
325
326	if ((entry = fs_find_by_name(fs_p->f_mntonname)) != NULL ||
327	    (entry = fs_entry_create(fs_p->f_mntonname)) != NULL) {
328		entry->flags |= HR_FS_FOUND;
329
330		strcpy(entry->mountPoint, fs_p->f_mntonname);
331
332		if (!(fs_p->f_flags & MNT_LOCAL))
333			/* this is a remote mount */
334			strcpy(entry->remoteMountPoint, fs_p->f_mntfromname);
335		else
336			entry->remoteMountPoint[0] = '\0';
337
338		entry->type = fs_get_type(fs_p);
339
340		if ((fs_p->f_flags & MNT_RDONLY) == MNT_RDONLY)
341			entry->access = FS_READ_ONLY;
342		else
343			entry->access = FS_READ_WRITE;
344
345		/* FIXME - bootable fs ?! */
346		entry->bootable = TRUTH_MK((fs_p->f_flags & MNT_ROOTFS)
347		    == MNT_ROOTFS);
348
349		entry->storageIndex = storage_idx;
350
351		/* Info not available */
352		memset(entry->lastFullBackupDate, 0,
353		    sizeof(entry->lastFullBackupDate));
354
355		/* Info not available */
356		memset(entry->lastPartialBackupDate, 0,
357		    sizeof(entry->lastPartialBackupDate));
358
359		handle_partition_fs_index(fs_p->f_mntfromname, entry->index);
360	}
361}
362
363/*
364 * This is the implementation for a generated (by our SNMP "compiler" tool)
365 * function prototype, see hostres_tree.h
366 * It handles the SNMP operations for hrFSTable
367 */
368int
369op_hrFSTable(struct snmp_context *ctx __unused, struct snmp_value *value,
370    u_int sub, u_int iidx __unused, enum snmp_op curr_op)
371{
372	struct fs_entry *entry;
373
374	refresh_fs_tbl();
375
376	switch (curr_op) {
377
378	case SNMP_OP_GETNEXT:
379		if ((entry = NEXT_OBJECT_INT(&fs_tbl,
380		    &value->var, sub)) == NULL)
381			return (SNMP_ERR_NOSUCHNAME);
382		value->var.len = sub + 1;
383		value->var.subs[sub] = entry->index;
384		goto get;
385
386	case SNMP_OP_GET:
387		if ((entry = FIND_OBJECT_INT(&fs_tbl,
388		    &value->var, sub)) == NULL)
389			return (SNMP_ERR_NOSUCHNAME);
390		goto get;
391
392	case SNMP_OP_SET:
393		if ((entry = FIND_OBJECT_INT(&fs_tbl,
394		    &value->var, sub)) == NULL)
395			return (SNMP_ERR_NO_CREATION);
396		return (SNMP_ERR_NOT_WRITEABLE);
397
398	case SNMP_OP_ROLLBACK:
399	case SNMP_OP_COMMIT:
400		abort();
401	}
402	abort();
403  get:
404	switch (value->var.subs[sub - 1]) {
405
406	case LEAF_hrFSIndex:
407		value->v.integer = entry->index;
408		return (SNMP_ERR_NOERROR);
409
410	case LEAF_hrFSMountPoint:
411		return (string_get(value, entry->mountPoint, -1));
412
413	case LEAF_hrFSRemoteMountPoint:
414		return (string_get(value, entry->remoteMountPoint, -1));
415		break;
416
417	case LEAF_hrFSType:
418		value->v.oid = *entry->type;
419		return (SNMP_ERR_NOERROR);
420
421	case LEAF_hrFSAccess:
422		value->v.integer = entry->access;
423		return (SNMP_ERR_NOERROR);
424
425	case LEAF_hrFSBootable:
426		value->v.integer = entry->bootable;
427		return (SNMP_ERR_NOERROR);
428
429	case LEAF_hrFSStorageIndex:
430		value->v.integer = entry->storageIndex;
431		return (SNMP_ERR_NOERROR);
432
433	case LEAF_hrFSLastFullBackupDate:
434		return (string_get(value, entry->lastFullBackupDate, 8));
435
436	case LEAF_hrFSLastPartialBackupDate:
437		return (string_get(value, entry->lastPartialBackupDate, 8));
438	}
439	abort();
440}
441