xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c (revision 3d11b6c8f01e1fca5936a11d6996448467851a94)
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$
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  */
54 enum 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  */
63 struct 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 };
77 TAILQ_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  */
84 struct 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 };
92 STAILQ_HEAD(fs_map, fs_map_entry);
93 
94 /* head of the list with hrFSTable's entries */
95 static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl);
96 
97 /* for consistent table indexing */
98 static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map);
99 
100 /* next index available for hrFSTable */
101 static uint32_t	next_fs_index = 1;
102 
103 /* last tick when hrFSTable was updated */
104 static uint64_t fs_tick;
105 
106 /* maximum number of ticks between refreshs */
107 uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100;
108 
109 /* some constants */
110 static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS;
111 static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660;
112 static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS;
113 static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2;
114 static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther;
115 static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32;
116 static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS;
117 static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware;
118 static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS;
119 static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown;
120 
121 /* file system type map */
122 static 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  */
143 static struct fs_entry *
144 fs_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  */
199 static void
200 fs_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  */
217 static struct fs_entry *
218 fs_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  */
233 void
234 fini_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  */
252 void
253 fs_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  */
265 void
266 fs_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  */
283 void
284 refresh_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  */
296 const struct asn_oid *
297 fs_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  */
314 void
315 fs_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  */
368 int
369 op_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