// SPDX-License-Identifier: GPL-3.0-or-later #include "plugin_freebsd.h" #include struct mount_point { char *name; uint32_t hash; size_t len; // flags int configured; int enabled; int updated; int do_space; int do_inodes; size_t collected; // the number of times this has been collected // charts and dimensions RRDSET *st_space; RRDDIM *rd_space_used; RRDDIM *rd_space_avail; RRDDIM *rd_space_reserved; RRDSET *st_inodes; RRDDIM *rd_inodes_used; RRDDIM *rd_inodes_avail; struct mount_point *next; }; static struct mount_point *mount_points_root = NULL, *mount_points_last_used = NULL; static size_t mount_points_added = 0, mount_points_found = 0; static void mount_point_free(struct mount_point *m) { if (likely(m->st_space)) rrdset_is_obsolete___safe_from_collector_thread(m->st_space); if (likely(m->st_inodes)) rrdset_is_obsolete___safe_from_collector_thread(m->st_inodes); mount_points_added--; freez(m->name); freez(m); } static void mount_points_cleanup() { if (likely(mount_points_found == mount_points_added)) return; struct mount_point *m = mount_points_root, *last = NULL; while(m) { if (unlikely(!m->updated)) { // collector_info("Removing mount point '%s', linked after '%s'", m->name, last?last->name:"ROOT"); if (mount_points_last_used == m) mount_points_last_used = last; struct mount_point *t = m; if (m == mount_points_root || !last) mount_points_root = m = m->next; else last->next = m = m->next; t->next = NULL; mount_point_free(t); } else { last = m; m->updated = 0; m = m->next; } } } static struct mount_point *get_mount_point(const char *name) { struct mount_point *m; uint32_t hash = simple_hash(name); // search it, from the last position to the end for(m = mount_points_last_used ; m ; m = m->next) { if (unlikely(hash == m->hash && !strcmp(name, m->name))) { mount_points_last_used = m->next; return m; } } // search it from the beginning to the last position we used for(m = mount_points_root ; m != mount_points_last_used ; m = m->next) { if (unlikely(hash == m->hash && !strcmp(name, m->name))) { mount_points_last_used = m->next; return m; } } // create a new one m = callocz(1, sizeof(struct mount_point)); m->name = strdupz(name); m->hash = simple_hash(m->name); m->len = strlen(m->name); mount_points_added++; // link it to the end if (mount_points_root) { struct mount_point *e; for(e = mount_points_root; e->next ; e = e->next) ; e->next = m; } else mount_points_root = m; return m; } // -------------------------------------------------------------------------------------------------------------------- // getmntinfo int do_getmntinfo(int update_every, usec_t dt) { (void)dt; #define DEFAULT_EXCLUDED_PATHS "/proc/*" // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes #define DEFAULT_EXCLUDED_FILESYSTEMS "autofs procfs subfs devfs none" #define CONFIG_SECTION_GETMNTINFO "plugin:freebsd:getmntinfo" static int enable_new_mount_points = -1; static int do_space = -1, do_inodes = -1; static SIMPLE_PATTERN *excluded_mountpoints = NULL; static SIMPLE_PATTERN *excluded_filesystems = NULL; if (unlikely(enable_new_mount_points == -1)) { enable_new_mount_points = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "enable new mount points detected at runtime", CONFIG_BOOLEAN_AUTO); do_space = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "space usage for all disks", CONFIG_BOOLEAN_AUTO); do_inodes = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "inodes usage for all disks", CONFIG_BOOLEAN_AUTO); excluded_mountpoints = simple_pattern_create( config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on paths", DEFAULT_EXCLUDED_PATHS), NULL, SIMPLE_PATTERN_EXACT, true); excluded_filesystems = simple_pattern_create( config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on filesystems", DEFAULT_EXCLUDED_FILESYSTEMS), NULL, SIMPLE_PATTERN_EXACT, true); } if (likely(do_space || do_inodes)) { struct statfs *mntbuf; int mntsize; // there is no mount info in sysctl MIBs if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) { collector_error("FREEBSD: getmntinfo() failed"); do_space = 0; collector_error("DISABLED: disk_space.* charts"); do_inodes = 0; collector_error("DISABLED: disk_inodes.* charts"); collector_error("DISABLED: getmntinfo module"); return 1; } else { int i; mount_points_found = 0; for (i = 0; i < mntsize; i++) { char title[4096 + 1]; struct mount_point *m = get_mount_point(mntbuf[i].f_mntonname); m->updated = 1; mount_points_found++; if (unlikely(!m->configured)) { char var_name[4096 + 1]; // this is the first time we see this filesystem // remember we configured it m->configured = 1; m->enabled = enable_new_mount_points; if (likely(m->enabled)) m->enabled = !(simple_pattern_matches(excluded_mountpoints, mntbuf[i].f_mntonname) || simple_pattern_matches(excluded_filesystems, mntbuf[i].f_fstypename)); snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETMNTINFO, mntbuf[i].f_mntonname); m->enabled = config_get_boolean_ondemand(var_name, "enabled", m->enabled); if (unlikely(m->enabled == CONFIG_BOOLEAN_NO)) continue; m->do_space = config_get_boolean_ondemand(var_name, "space usage", do_space); m->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", do_inodes); } if (unlikely(!m->enabled)) continue; if (unlikely(mntbuf[i].f_flags & MNT_RDONLY && !m->collected)) continue; int rendered = 0; if (m->do_space == CONFIG_BOOLEAN_YES || m->do_space == CONFIG_BOOLEAN_AUTO) { if (unlikely(!m->st_space)) { snprintfz(title, sizeof(title) - 1, "Disk Space Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); m->st_space = rrdset_create_localhost("disk_space", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.space", title, "GiB", "freebsd.plugin", "getmntinfo", NETDATA_CHART_PRIO_DISKSPACE_SPACE, update_every, RRDSET_TYPE_STACKED ); m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); m->rd_space_used = rrddim_add(m->st_space, "used", NULL, mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root", mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE); } rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number) mntbuf[i].f_bavail); rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number) (mntbuf[i].f_blocks - mntbuf[i].f_bfree)); rrddim_set_by_pointer(m->st_space, m->rd_space_reserved, (collected_number) (mntbuf[i].f_bfree - mntbuf[i].f_bavail)); rrdset_done(m->st_space); rendered++; } if (m->do_inodes == CONFIG_BOOLEAN_YES || m->do_inodes == CONFIG_BOOLEAN_AUTO) { if (unlikely(!m->st_inodes)) { snprintfz(title, sizeof(title) - 1, "Disk Files (inodes) Usage for %s [%s]", mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname); m->st_inodes = rrdset_create_localhost("disk_inodes", mntbuf[i].f_mntonname, NULL, mntbuf[i].f_mntonname, "disk.inodes", title, "inodes", "freebsd.plugin", "getmntinfo", NETDATA_CHART_PRIO_DISKSPACE_INODES, update_every, RRDSET_TYPE_STACKED ); m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); } rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number) mntbuf[i].f_ffree); rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number) (mntbuf[i].f_files - mntbuf[i].f_ffree)); rrdset_done(m->st_inodes); rendered++; } if (likely(rendered)) m->collected++; } } } else { collector_error("DISABLED: getmntinfo module"); return 1; } mount_points_cleanup(); return 0; }