]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c
MFC r362623:
[FreeBSD/stable/8.git] / usr.sbin / bsnmpd / modules / snmp_hostres / hostres_diskstorage_tbl.c
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 the hrDiskStorageTable
34  */
35
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/ata.h>
39 #include <sys/disk.h>
40 #include <sys/linker.h>
41 #include <sys/mdioctl.h>
42 #include <sys/module.h>
43 #include <sys/sysctl.h>
44
45 #include <assert.h>
46 #include <ctype.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <paths.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <syslog.h>
53 #include <unistd.h>
54
55 #include "hostres_snmp.h"
56 #include "hostres_oid.h"
57 #include "hostres_tree.h"
58
59 enum hrDiskStrorageAccess {
60         DS_READ_WRITE = 1,
61         DS_READ_ONLY  = 2
62 };
63
64 enum hrDiskStrorageMedia {
65         DSM_OTHER       =       1,
66         DSM_UNKNOWN     =       2,
67         DSM_HARDDISK    =       3,
68         DSM_FLOPPYDISK  =       4,
69         DSM_OPTICALDISKROM=     5,
70         DSM_OPTICALDISKWORM=    6,
71         DSM_OPTICALDISKRW=      7,
72         DSM_RAMDISK     =       8
73 };
74
75 /*
76  * This structure is used to hold a SNMP table entry for HOST-RESOURCES-MIB's
77  * hrDiskStorageTable. Note that index is external being allocated and
78  * maintained by the hrDeviceTable code.
79  *
80  * NOTE: according to MIB removable means removable media, not the
81  * device itself (like a USB card reader)
82  */
83 struct disk_entry {
84         int32_t         index;
85         int32_t         access;         /* enum hrDiskStrorageAccess */
86         int32_t         media;          /* enum hrDiskStrorageMedia*/
87         int32_t         removable;      /* enum snmpTCTruthValue*/
88         int32_t         capacity;
89         TAILQ_ENTRY(disk_entry) link;
90         /*
91          * next items are not from the SNMP mib table, only to be used
92          * internally
93          */
94 #define HR_DISKSTORAGE_FOUND    0x001
95 #define HR_DISKSTORAGE_ATA      0x002 /* belongs to the ATA subsystem */
96 #define HR_DISKSTORAGE_MD       0x004 /* it is a MD (memory disk) */
97         uint32_t        flags;
98         uint64_t        r_tick;
99         u_char          dev_name[32];   /* device name, i.e. "ad4" or "acd0" */
100 };
101 TAILQ_HEAD(disk_tbl, disk_entry);
102
103 /* the head of the list with hrDiskStorageTable's entries */
104 static struct disk_tbl disk_tbl =
105     TAILQ_HEAD_INITIALIZER(disk_tbl);
106
107 /* last tick when hrFSTable was updated */
108 static uint64_t disk_storage_tick;
109
110 /* minimum number of ticks between refreshs */
111 uint32_t disk_storage_tbl_refresh = HR_DISK_TBL_REFRESH * 100;
112
113 /* fd for "/dev/mdctl"*/
114 static int md_fd = -1;
115
116 /* buffer for sysctl("kern.disks") */
117 static char *disk_list;
118 static size_t disk_list_len;
119
120 /* some constants */
121 static const struct asn_oid OIDX_hrDeviceDiskStorage_c =
122     OIDX_hrDeviceDiskStorage;
123
124 /**
125  * Load the MD driver if it isn't loaded already.
126  */
127 static void
128 mdmaybeload(void)
129 {
130         char name1[64], name2[64];
131
132         snprintf(name1, sizeof(name1), "g_%s", MD_NAME);
133         snprintf(name2, sizeof(name2), "geom_%s", MD_NAME);
134         if (modfind(name1) == -1) {
135                 /* Not present in kernel, try loading it. */
136                 if (kldload(name2) == -1 || modfind(name1) == -1) {
137                         if (errno != EEXIST) {
138                                 errx(EXIT_FAILURE,
139                                     "%s module not available!", name2);
140                         }
141                 }
142         }
143 }
144
145 /**
146  * Create a new entry into the DiskStorageTable.
147  */
148 static struct disk_entry *
149 disk_entry_create(const struct device_entry *devEntry)
150 {
151         struct disk_entry *entry;
152
153         assert(devEntry != NULL);
154         if (devEntry == NULL)
155                 return NULL;
156
157         if ((entry = malloc(sizeof(*entry))) == NULL) {
158                 syslog(LOG_WARNING, "hrDiskStorageTable: %s: %m", __func__);
159                 return (NULL);
160         }
161
162         memset(entry, 0, sizeof(*entry));
163         entry->index = devEntry->index;
164         INSERT_OBJECT_INT(entry, &disk_tbl);
165
166         return (entry);
167 }
168
169 /**
170  * Delete a disk table entry.
171  */
172 static void
173 disk_entry_delete(struct disk_entry *entry)
174 {
175         struct device_entry *devEntry;
176
177         assert(entry != NULL);
178         TAILQ_REMOVE(&disk_tbl, entry, link);
179
180         devEntry = device_find_by_index(entry->index);
181
182         free(entry);
183
184         /*
185          * Also delete the respective device entry -
186          * this is needed for disk devices that are not
187          * detected by libdevinfo
188          */
189         if (devEntry != NULL &&
190             (devEntry->flags & HR_DEVICE_IMMUTABLE) == HR_DEVICE_IMMUTABLE)
191                 device_entry_delete(devEntry);
192 }
193
194 /**
195  * Find a disk storage entry given its index.
196  */
197 static struct disk_entry *
198 disk_find_by_index(int32_t idx)
199 {
200         struct disk_entry *entry;
201
202         TAILQ_FOREACH(entry, &disk_tbl, link)
203                 if (entry->index == idx)
204                         return (entry);
205
206         return (NULL);
207 }
208
209 /**
210  * Get the disk parameters
211  */
212 static void
213 disk_query_disk(struct disk_entry *entry)
214 {
215         char dev_path[128];
216         int fd;
217         off_t mediasize;
218
219         if (entry == NULL || entry->dev_name[0] == '\0')
220                 return;
221
222         snprintf(dev_path, sizeof(dev_path),
223             "%s%s", _PATH_DEV, entry->dev_name);
224         entry->capacity = 0;
225
226         HRDBG("OPENING device %s", dev_path);
227         if ((fd = open(dev_path, O_RDONLY|O_NONBLOCK)) == -1) {
228                 HRDBG("OPEN device %s failed: %s", dev_path, strerror(errno));
229                 return;
230         }
231
232         if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) {
233                 HRDBG("DIOCGMEDIASIZE for device %s failed: %s",
234                     dev_path, strerror(errno));
235                 (void)close(fd);
236                 return;
237         }
238
239         mediasize = mediasize / 1024;
240         entry->capacity = (mediasize > INT_MAX ? INT_MAX : mediasize);
241         partition_tbl_handle_disk(entry->index, entry->dev_name);
242
243         (void)close(fd);
244 }
245
246 /**
247  * Find all ATA disks in the device table.
248  */
249 static void
250 disk_OS_get_ATA_disks(void)
251 {
252         struct device_map_entry *map;
253         struct device_entry *entry;
254         struct disk_entry *disk_entry;
255         const struct disk_entry *found;
256
257         /* Things we know are ata disks */
258         static const struct disk_entry lookup[] = {
259                 {
260                     .dev_name = "ad",
261                     .media = DSM_HARDDISK,
262                     .removable = SNMP_FALSE
263                 },
264                 {
265                     .dev_name = "ar",
266                     .media = DSM_OTHER,
267                     .removable = SNMP_FALSE
268                 },
269                 {
270                     .dev_name = "acd",
271                     .media = DSM_OPTICALDISKROM,
272                     .removable = SNMP_TRUE
273                 },
274                 {
275                     .dev_name = "afd",
276                     .media = DSM_FLOPPYDISK,
277                     .removable = SNMP_TRUE
278                 },
279                 {
280                     .dev_name = "ast",
281                     .media = DSM_OTHER,
282                     .removable = SNMP_TRUE
283                 },
284
285                 { .media = DSM_UNKNOWN }
286         };
287
288         /* Walk over the device table looking for ata disks */
289         STAILQ_FOREACH(map, &device_map, link) {
290                 /* Skip deleted entries. */
291                 if (map->entry_p == NULL)
292                         continue;
293                 for (found = lookup; found->media != DSM_UNKNOWN; found++) {
294                         if (strncmp(map->name_key, found->dev_name,
295                             strlen(found->dev_name)) != 0)
296                                 continue;
297
298                         /*
299                          * Avoid false disk devices. For example adw(4) and
300                          * adv(4) - they are not disks!
301                          */
302                         if (strlen(map->name_key) > strlen(found->dev_name) &&
303                             !isdigit(map->name_key[strlen(found->dev_name)]))
304                                 continue;
305
306                         /* First get the entry from the hrDeviceTbl */
307                         entry = map->entry_p;
308                         entry->type = &OIDX_hrDeviceDiskStorage_c;
309
310                         /* Then check hrDiskStorage table for this device */
311                         disk_entry = disk_find_by_index(entry->index);
312                         if (disk_entry == NULL) {
313                                 disk_entry = disk_entry_create(entry);
314                                 if (disk_entry == NULL)
315                                         continue;
316
317                                 disk_entry->access = DS_READ_WRITE;
318                                 strlcpy(disk_entry->dev_name, entry->name,
319                                     sizeof(disk_entry->dev_name));
320
321                                 disk_entry->media = found->media;
322                                 disk_entry->removable = found->removable;
323                         }
324
325                         disk_entry->flags |= HR_DISKSTORAGE_FOUND;
326                         disk_entry->flags |= HR_DISKSTORAGE_ATA;
327
328                         disk_query_disk(disk_entry);
329                         disk_entry->r_tick = this_tick;
330                 }
331         }
332 }
333
334 /**
335  * Find MD disks in the device table.
336  */
337 static void
338 disk_OS_get_MD_disks(void)
339 {
340         struct device_map_entry *map;
341         struct device_entry *entry;
342         struct disk_entry *disk_entry;
343         struct md_ioctl mdio;
344         int unit;
345
346         /* Look for md devices */
347         STAILQ_FOREACH(map, &device_map, link) {
348                 /* Skip deleted entries. */
349                 if (map->entry_p == NULL)
350                         continue;
351                 if (sscanf(map->name_key, "md%d", &unit) != 1)
352                         continue;
353
354                 /* First get the entry from the hrDeviceTbl */
355                 entry = device_find_by_index(map->hrIndex);
356                 entry->type = &OIDX_hrDeviceDiskStorage_c;
357
358                 /* Then check hrDiskStorage table for this device */
359                 disk_entry = disk_find_by_index(entry->index);
360                 if (disk_entry == NULL) {
361                         disk_entry = disk_entry_create(entry);
362                         if (disk_entry == NULL)
363                                 continue;
364
365                         memset(&mdio, 0, sizeof(mdio));
366                         mdio.md_version = MDIOVERSION;
367                         mdio.md_unit = unit;
368
369                         if (ioctl(md_fd, MDIOCQUERY, &mdio) < 0) {
370                                 syslog(LOG_ERR,
371                                     "hrDiskStorageTable: Couldnt ioctl");
372                                 continue;
373                         }
374
375                         if ((mdio.md_options & MD_READONLY) == MD_READONLY)
376                                 disk_entry->access = DS_READ_ONLY;
377                         else
378                                 disk_entry->access = DS_READ_WRITE;
379
380                         strlcpy(disk_entry->dev_name, entry->name,
381                             sizeof(disk_entry->dev_name));
382
383                         disk_entry->media = DSM_RAMDISK;
384                         disk_entry->removable = SNMP_FALSE;
385                 }
386
387                 disk_entry->flags |= HR_DISKSTORAGE_FOUND;
388                 disk_entry->flags |= HR_DISKSTORAGE_MD;
389                 disk_entry->r_tick = this_tick;
390         }
391 }
392
393 /**
394  * Find rest of disks
395  */
396 static void
397 disk_OS_get_disks(void)
398 {
399         size_t disk_cnt = 0;
400         struct device_entry *entry;
401         struct disk_entry *disk_entry;
402
403         size_t need = 0;
404
405         if (sysctlbyname("kern.disks", NULL, &need, NULL, 0) == -1) {
406                 syslog(LOG_ERR, "%s: sysctl_1 kern.disks failed: %m", __func__);
407                 return;
408         }
409
410         if (need == 0)
411                 return;
412
413         if (disk_list_len != need + 1 || disk_list == NULL) {
414                 disk_list_len = need + 1;
415                 disk_list = reallocf(disk_list, disk_list_len);
416         }
417
418         if (disk_list == NULL) {
419                 syslog(LOG_ERR, "%s: reallocf failed", __func__);
420                 disk_list_len = 0;
421                 return;
422         }
423
424         memset(disk_list, 0, disk_list_len);
425
426         if (sysctlbyname("kern.disks", disk_list, &need, NULL, 0) == -1 ||
427             disk_list[0] == 0) {
428                 syslog(LOG_ERR, "%s: sysctl_2 kern.disks failed: %m", __func__);
429                 return;
430         }
431
432         for (disk_cnt = 0; disk_cnt < need; disk_cnt++) {
433                 char *disk = NULL;
434                 char disk_device[128] = "";
435
436                 disk = strsep(&disk_list, " ");
437                 if (disk == NULL)
438                         break;
439
440                 snprintf(disk_device, sizeof(disk_device),
441                     "%s%s", _PATH_DEV, disk);
442
443                 /* First check if the disk is in the hrDeviceTable. */
444                 if ((entry = device_find_by_name(disk)) == NULL) {
445                         /*
446                          * not found there - insert it as immutable
447                          */
448                         syslog(LOG_WARNING, "%s: device '%s' not in "
449                             "device list", __func__, disk);
450
451                         if ((entry = device_entry_create(disk, "", "")) == NULL)
452                                 continue;
453
454                         entry->flags |= HR_DEVICE_IMMUTABLE;
455                 }
456
457                 entry->type = &OIDX_hrDeviceDiskStorage_c;
458
459                 /* Then check hrDiskStorage table for this device */
460                 disk_entry = disk_find_by_index(entry->index);
461                 if (disk_entry == NULL) {
462                         disk_entry = disk_entry_create(entry);
463                         if (disk_entry == NULL)
464                                 continue;
465                 }
466
467                 disk_entry->flags |= HR_DISKSTORAGE_FOUND;
468
469                 if ((disk_entry->flags & HR_DISKSTORAGE_ATA) ||
470                     (disk_entry->flags & HR_DISKSTORAGE_MD)) {
471                         /*
472                          * ATA/MD detection is running before this one,
473                          * so don't waste the time here
474                          */
475                         continue;
476                 }
477
478                 disk_entry->access = DS_READ_WRITE;
479                 disk_entry->media = DSM_UNKNOWN;
480                 disk_entry->removable = SNMP_FALSE;
481
482                 if (strncmp(disk_entry->dev_name, "da", 2) == 0) {
483                         disk_entry->media = DSM_HARDDISK;
484                         disk_entry->removable = SNMP_FALSE;
485                 } else if (strncmp(disk_entry->dev_name, "cd", 2) == 0) {
486                         disk_entry->media = DSM_OPTICALDISKROM;
487                         disk_entry->removable = SNMP_TRUE;
488                 } else {
489                         disk_entry->media = DSM_UNKNOWN;
490                         disk_entry->removable = SNMP_FALSE;
491                 }
492
493                 strlcpy((char *)disk_entry->dev_name, disk,
494                     sizeof(disk_entry->dev_name));
495
496                 disk_query_disk(disk_entry);
497                 disk_entry->r_tick = this_tick;
498         }
499 }
500
501 /**
502  * Refresh routine for hrDiskStorageTable
503  * Usable for polling the system for any changes.
504  */
505 void
506 refresh_disk_storage_tbl(int force)
507 {
508         struct disk_entry *entry, *entry_tmp;
509
510         if (disk_storage_tick != 0 && !force &&
511             this_tick - disk_storage_tick < disk_storage_tbl_refresh) {
512                 HRDBG("no refresh needed");
513                 return;
514         }
515
516         partition_tbl_pre_refresh();
517
518         /* mark each entry as missing */
519         TAILQ_FOREACH(entry, &disk_tbl, link)
520                 entry->flags &= ~HR_DISKSTORAGE_FOUND;
521
522         disk_OS_get_ATA_disks();        /* this must be called first ! */
523         disk_OS_get_MD_disks();
524         disk_OS_get_disks();
525
526         /*
527          * Purge items that disappeared
528          */
529         TAILQ_FOREACH_SAFE(entry, &disk_tbl, link, entry_tmp)
530                 if (!(entry->flags & HR_DISKSTORAGE_FOUND))
531                         /* XXX remove IMMUTABLE entries that have disappeared */
532                         disk_entry_delete(entry);
533
534         disk_storage_tick = this_tick;
535
536         partition_tbl_post_refresh();
537
538         HRDBG("refresh DONE");
539 }
540
541 /*
542  * Init the things for both of hrDiskStorageTable
543  */
544 int
545 init_disk_storage_tbl(void)
546 {
547         char mddev[32] = "";
548
549         /* Try to load md.ko if not loaded already */
550         mdmaybeload();
551
552         md_fd = -1;
553         snprintf(mddev, sizeof(mddev) - 1, "%s%s", _PATH_DEV, MDCTL_NAME);
554         if ((md_fd = open(mddev, O_RDWR)) == -1) {
555                 syslog(LOG_ERR, "open %s failed: %m", mddev);
556                 return (-1);
557         }
558
559         refresh_disk_storage_tbl(1);
560
561         return (0);
562 }
563
564 /*
565  * Finalization routine for hrDiskStorageTable
566  * It destroys the lists and frees any allocated heap memory
567  */
568 void
569 fini_disk_storage_tbl(void)
570 {
571         struct disk_entry *n1;
572
573         while ((n1 = TAILQ_FIRST(&disk_tbl)) != NULL) {
574                 TAILQ_REMOVE(&disk_tbl, n1, link);
575                 free(n1);
576         }
577
578         free(disk_list);
579
580         if (md_fd > 0) {
581                 if (close(md_fd) == -1)
582                         syslog(LOG_ERR,"close (/dev/mdctl) failed: %m");
583                 md_fd = -1;
584         }
585 }
586
587 /*
588  * This is the implementation for a generated (by our SNMP "compiler" tool)
589  * function prototype, see hostres_tree.h
590  * It handles the SNMP operations for hrDiskStorageTable
591  */
592 int
593 op_hrDiskStorageTable(struct snmp_context *ctx __unused,
594     struct snmp_value *value, u_int sub, u_int iidx __unused,
595     enum snmp_op curr_op)
596 {
597         struct disk_entry *entry;
598
599         refresh_disk_storage_tbl(0);
600
601         switch (curr_op) {
602
603         case SNMP_OP_GETNEXT:
604                 if ((entry = NEXT_OBJECT_INT(&disk_tbl,
605                     &value->var, sub)) == NULL)
606                         return (SNMP_ERR_NOSUCHNAME);
607                 value->var.len = sub + 1;
608                 value->var.subs[sub] = entry->index;
609                 goto get;
610
611         case SNMP_OP_GET:
612                 if ((entry = FIND_OBJECT_INT(&disk_tbl,
613                     &value->var, sub)) == NULL)
614                         return (SNMP_ERR_NOSUCHNAME);
615                 goto get;
616
617         case SNMP_OP_SET:
618                 if ((entry = FIND_OBJECT_INT(&disk_tbl,
619                     &value->var, sub)) == NULL)
620                         return (SNMP_ERR_NO_CREATION);
621                 return (SNMP_ERR_NOT_WRITEABLE);
622
623         case SNMP_OP_ROLLBACK:
624         case SNMP_OP_COMMIT:
625                 abort();
626         }
627         abort();
628
629   get:
630         switch (value->var.subs[sub - 1]) {
631
632         case LEAF_hrDiskStorageAccess:
633                 value->v.integer = entry->access;
634                 return (SNMP_ERR_NOERROR);
635
636         case LEAF_hrDiskStorageMedia:
637                 value->v.integer = entry->media;
638                 return (SNMP_ERR_NOERROR);
639
640         case LEAF_hrDiskStorageRemoveble:
641                 value->v.integer = entry->removable;
642                 return (SNMP_ERR_NOERROR);
643
644         case LEAF_hrDiskStorageCapacity:
645                 value->v.integer = entry->capacity;
646                 return (SNMP_ERR_NOERROR);
647         }
648         abort();
649 }