freebsd_getmntinfo.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "plugin_freebsd.h"
  3. #include <sys/mount.h>
  4. struct mount_point {
  5. char *name;
  6. uint32_t hash;
  7. size_t len;
  8. // flags
  9. int configured;
  10. int enabled;
  11. int updated;
  12. int do_space;
  13. int do_inodes;
  14. size_t collected; // the number of times this has been collected
  15. // charts and dimensions
  16. RRDSET *st_space;
  17. RRDDIM *rd_space_used;
  18. RRDDIM *rd_space_avail;
  19. RRDDIM *rd_space_reserved;
  20. RRDSET *st_inodes;
  21. RRDDIM *rd_inodes_used;
  22. RRDDIM *rd_inodes_avail;
  23. struct mount_point *next;
  24. };
  25. static struct mount_point *mount_points_root = NULL, *mount_points_last_used = NULL;
  26. static size_t mount_points_added = 0, mount_points_found = 0;
  27. static void mount_point_free(struct mount_point *m) {
  28. if (likely(m->st_space))
  29. rrdset_is_obsolete(m->st_space);
  30. if (likely(m->st_inodes))
  31. rrdset_is_obsolete(m->st_inodes);
  32. mount_points_added--;
  33. freez(m->name);
  34. freez(m);
  35. }
  36. static void mount_points_cleanup() {
  37. if (likely(mount_points_found == mount_points_added)) return;
  38. struct mount_point *m = mount_points_root, *last = NULL;
  39. while(m) {
  40. if (unlikely(!m->updated)) {
  41. // collector_info("Removing mount point '%s', linked after '%s'", m->name, last?last->name:"ROOT");
  42. if (mount_points_last_used == m)
  43. mount_points_last_used = last;
  44. struct mount_point *t = m;
  45. if (m == mount_points_root || !last)
  46. mount_points_root = m = m->next;
  47. else
  48. last->next = m = m->next;
  49. t->next = NULL;
  50. mount_point_free(t);
  51. }
  52. else {
  53. last = m;
  54. m->updated = 0;
  55. m = m->next;
  56. }
  57. }
  58. }
  59. static struct mount_point *get_mount_point(const char *name) {
  60. struct mount_point *m;
  61. uint32_t hash = simple_hash(name);
  62. // search it, from the last position to the end
  63. for(m = mount_points_last_used ; m ; m = m->next) {
  64. if (unlikely(hash == m->hash && !strcmp(name, m->name))) {
  65. mount_points_last_used = m->next;
  66. return m;
  67. }
  68. }
  69. // search it from the beginning to the last position we used
  70. for(m = mount_points_root ; m != mount_points_last_used ; m = m->next) {
  71. if (unlikely(hash == m->hash && !strcmp(name, m->name))) {
  72. mount_points_last_used = m->next;
  73. return m;
  74. }
  75. }
  76. // create a new one
  77. m = callocz(1, sizeof(struct mount_point));
  78. m->name = strdupz(name);
  79. m->hash = simple_hash(m->name);
  80. m->len = strlen(m->name);
  81. mount_points_added++;
  82. // link it to the end
  83. if (mount_points_root) {
  84. struct mount_point *e;
  85. for(e = mount_points_root; e->next ; e = e->next) ;
  86. e->next = m;
  87. }
  88. else
  89. mount_points_root = m;
  90. return m;
  91. }
  92. // --------------------------------------------------------------------------------------------------------------------
  93. // getmntinfo
  94. int do_getmntinfo(int update_every, usec_t dt) {
  95. (void)dt;
  96. #define DEFAULT_EXCLUDED_PATHS "/proc/*"
  97. // taken from gnulib/mountlist.c and shortened to FreeBSD related fstypes
  98. #define DEFAULT_EXCLUDED_FILESYSTEMS "autofs procfs subfs devfs none"
  99. #define CONFIG_SECTION_GETMNTINFO "plugin:freebsd:getmntinfo"
  100. static int enable_new_mount_points = -1;
  101. static int do_space = -1, do_inodes = -1;
  102. static SIMPLE_PATTERN *excluded_mountpoints = NULL;
  103. static SIMPLE_PATTERN *excluded_filesystems = NULL;
  104. if (unlikely(enable_new_mount_points == -1)) {
  105. enable_new_mount_points = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO,
  106. "enable new mount points detected at runtime",
  107. CONFIG_BOOLEAN_AUTO);
  108. do_space = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "space usage for all disks", CONFIG_BOOLEAN_AUTO);
  109. do_inodes = config_get_boolean_ondemand(CONFIG_SECTION_GETMNTINFO, "inodes usage for all disks", CONFIG_BOOLEAN_AUTO);
  110. excluded_mountpoints = simple_pattern_create(
  111. config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on paths", DEFAULT_EXCLUDED_PATHS),
  112. NULL,
  113. SIMPLE_PATTERN_EXACT,
  114. true);
  115. excluded_filesystems = simple_pattern_create(
  116. config_get(CONFIG_SECTION_GETMNTINFO, "exclude space metrics on filesystems", DEFAULT_EXCLUDED_FILESYSTEMS),
  117. NULL,
  118. SIMPLE_PATTERN_EXACT,
  119. true);
  120. }
  121. if (likely(do_space || do_inodes)) {
  122. struct statfs *mntbuf;
  123. int mntsize;
  124. // there is no mount info in sysctl MIBs
  125. if (unlikely(!(mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)))) {
  126. collector_error("FREEBSD: getmntinfo() failed");
  127. do_space = 0;
  128. collector_error("DISABLED: disk_space.* charts");
  129. do_inodes = 0;
  130. collector_error("DISABLED: disk_inodes.* charts");
  131. collector_error("DISABLED: getmntinfo module");
  132. return 1;
  133. } else {
  134. int i;
  135. mount_points_found = 0;
  136. for (i = 0; i < mntsize; i++) {
  137. char title[4096 + 1];
  138. struct mount_point *m = get_mount_point(mntbuf[i].f_mntonname);
  139. m->updated = 1;
  140. mount_points_found++;
  141. if (unlikely(!m->configured)) {
  142. char var_name[4096 + 1];
  143. // this is the first time we see this filesystem
  144. // remember we configured it
  145. m->configured = 1;
  146. m->enabled = enable_new_mount_points;
  147. if (likely(m->enabled))
  148. m->enabled = !(simple_pattern_matches(excluded_mountpoints, mntbuf[i].f_mntonname)
  149. || simple_pattern_matches(excluded_filesystems, mntbuf[i].f_fstypename));
  150. snprintfz(var_name, 4096, "%s:%s", CONFIG_SECTION_GETMNTINFO, mntbuf[i].f_mntonname);
  151. m->enabled = config_get_boolean_ondemand(var_name, "enabled", m->enabled);
  152. if (unlikely(m->enabled == CONFIG_BOOLEAN_NO))
  153. continue;
  154. m->do_space = config_get_boolean_ondemand(var_name, "space usage", do_space);
  155. m->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", do_inodes);
  156. }
  157. if (unlikely(!m->enabled))
  158. continue;
  159. if (unlikely(mntbuf[i].f_flags & MNT_RDONLY && !m->collected))
  160. continue;
  161. int rendered = 0;
  162. if (m->do_space == CONFIG_BOOLEAN_YES || (m->do_space == CONFIG_BOOLEAN_AUTO &&
  163. (mntbuf[i].f_blocks > 2 ||
  164. netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) {
  165. if (unlikely(!m->st_space)) {
  166. snprintfz(title, 4096, "Disk Space Usage for %s [%s]",
  167. mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
  168. m->st_space = rrdset_create_localhost("disk_space",
  169. mntbuf[i].f_mntonname,
  170. NULL,
  171. mntbuf[i].f_mntonname,
  172. "disk.space",
  173. title,
  174. "GiB",
  175. "freebsd.plugin",
  176. "getmntinfo",
  177. NETDATA_CHART_PRIO_DISKSPACE_SPACE,
  178. update_every,
  179. RRDSET_TYPE_STACKED
  180. );
  181. m->rd_space_avail = rrddim_add(m->st_space, "avail", NULL,
  182. mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
  183. m->rd_space_used = rrddim_add(m->st_space, "used", NULL,
  184. mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
  185. m->rd_space_reserved = rrddim_add(m->st_space, "reserved_for_root", "reserved for root",
  186. mntbuf[i].f_bsize, GIGA_FACTOR, RRD_ALGORITHM_ABSOLUTE);
  187. }
  188. rrddim_set_by_pointer(m->st_space, m->rd_space_avail, (collected_number) mntbuf[i].f_bavail);
  189. rrddim_set_by_pointer(m->st_space, m->rd_space_used, (collected_number) (mntbuf[i].f_blocks -
  190. mntbuf[i].f_bfree));
  191. rrddim_set_by_pointer(m->st_space, m->rd_space_reserved, (collected_number) (mntbuf[i].f_bfree -
  192. mntbuf[i].f_bavail));
  193. rrdset_done(m->st_space);
  194. rendered++;
  195. }
  196. if (m->do_inodes == CONFIG_BOOLEAN_YES || (m->do_inodes == CONFIG_BOOLEAN_AUTO &&
  197. (mntbuf[i].f_files > 1 ||
  198. netdata_zero_metrics_enabled == CONFIG_BOOLEAN_YES))) {
  199. if (unlikely(!m->st_inodes)) {
  200. snprintfz(title, 4096, "Disk Files (inodes) Usage for %s [%s]",
  201. mntbuf[i].f_mntonname, mntbuf[i].f_mntfromname);
  202. m->st_inodes = rrdset_create_localhost("disk_inodes",
  203. mntbuf[i].f_mntonname,
  204. NULL,
  205. mntbuf[i].f_mntonname,
  206. "disk.inodes",
  207. title,
  208. "inodes",
  209. "freebsd.plugin",
  210. "getmntinfo",
  211. NETDATA_CHART_PRIO_DISKSPACE_INODES,
  212. update_every,
  213. RRDSET_TYPE_STACKED
  214. );
  215. m->rd_inodes_avail = rrddim_add(m->st_inodes, "avail", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  216. m->rd_inodes_used = rrddim_add(m->st_inodes, "used", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
  217. }
  218. rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_avail, (collected_number) mntbuf[i].f_ffree);
  219. rrddim_set_by_pointer(m->st_inodes, m->rd_inodes_used, (collected_number) (mntbuf[i].f_files -
  220. mntbuf[i].f_ffree));
  221. rrdset_done(m->st_inodes);
  222. rendered++;
  223. }
  224. if (likely(rendered))
  225. m->collected++;
  226. }
  227. }
  228. } else {
  229. collector_error("DISABLED: getmntinfo module");
  230. return 1;
  231. }
  232. mount_points_cleanup();
  233. return 0;
  234. }