summaryrefslogtreecommitdiffstats
path: root/collectors/debugfs.plugin/debugfs_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/debugfs.plugin/debugfs_plugin.c')
-rw-r--r--collectors/debugfs.plugin/debugfs_plugin.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/collectors/debugfs.plugin/debugfs_plugin.c b/collectors/debugfs.plugin/debugfs_plugin.c
new file mode 100644
index 000000000..9713be320
--- /dev/null
+++ b/collectors/debugfs.plugin/debugfs_plugin.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "debugfs_plugin.h"
+#include "libnetdata/required_dummies.h"
+
+static char *user_config_dir = CONFIG_DIR;
+static char *stock_config_dir = LIBCONFIG_DIR;
+
+static int update_every = 1;
+
+static struct debugfs_module {
+ const char *name;
+
+ int enabled;
+
+ int (*func)(int update_every, const char *name);
+} debugfs_modules[] = {
+ // Memory Fragmentation
+ { .name = "/sys/kernel/debug/extfrag", .enabled = CONFIG_BOOLEAN_YES,
+ .func = do_debugfs_extfrag},
+ { .name = "/sys/kernel/debug/zswap", .enabled = CONFIG_BOOLEAN_YES,
+ .func = do_debugfs_zswap},
+
+ // The terminator
+ { .name = NULL, .enabled = CONFIG_BOOLEAN_NO, .func = NULL}
+};
+
+#ifdef HAVE_CAPABILITY
+static int debugfs_check_capabilities()
+{
+ cap_t caps = cap_get_proc();
+ if (!caps) {
+ error("Cannot get current capabilities.");
+ return 0;
+ }
+
+ int ret = 1;
+ cap_flag_value_t cfv = CAP_CLEAR;
+ if (cap_get_flag(caps, CAP_DAC_READ_SEARCH, CAP_EFFECTIVE, &cfv) == -1) {
+ error("Cannot find if CAP_DAC_READ_SEARCH is effective.");
+ ret = 0;
+ } else {
+ if (cfv != CAP_SET) {
+ error("debugfs.plugin should run with CAP_DAC_READ_SEARCH.");
+ ret = 0;
+ }
+ }
+ cap_free(caps);
+
+ return ret;
+}
+#else
+static int debugfs_check_capabilities()
+{
+ return 0;
+}
+#endif
+
+// TODO: This is a function used by 3 different collector, we should do it global (next PR)
+static int debugfs_am_i_running_as_root()
+{
+ uid_t uid = getuid(), euid = geteuid();
+
+ if (uid == 0 || euid == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void debugfs2lower(char *name)
+{
+ while (*name) {
+ *name = tolower(*name);
+ name++;
+ }
+}
+
+// Consiidering our goal to redce binaries, I preferred to copy function, instead to force link with unecessary libs
+const char *debugfs_rrdset_type_name(RRDSET_TYPE chart_type) {
+ switch(chart_type) {
+ case RRDSET_TYPE_LINE:
+ default:
+ return RRDSET_TYPE_LINE_NAME;
+
+ case RRDSET_TYPE_AREA:
+ return RRDSET_TYPE_AREA_NAME;
+
+ case RRDSET_TYPE_STACKED:
+ return RRDSET_TYPE_STACKED_NAME;
+ }
+}
+
+const char *debugfs_rrd_algorithm_name(RRD_ALGORITHM algorithm) {
+ switch(algorithm) {
+ case RRD_ALGORITHM_ABSOLUTE:
+ default:
+ return RRD_ALGORITHM_ABSOLUTE_NAME;
+
+ case RRD_ALGORITHM_INCREMENTAL:
+ return RRD_ALGORITHM_INCREMENTAL_NAME;
+
+ case RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL:
+ return RRD_ALGORITHM_PCENT_OVER_ROW_TOTAL_NAME;
+
+ case RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL:
+ return RRD_ALGORITHM_PCENT_OVER_DIFF_TOTAL_NAME;
+ }
+}
+
+int debugfs_check_sys_permission() {
+ int ret = 0;
+
+ char filename[FILENAME_MAX + 1];
+
+ snprintfz(filename, FILENAME_MAX, "%s/sys/kernel/debug/extfrag/extfrag_index", netdata_configured_host_prefix);
+
+ procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+ if(!ff) goto dcsp_cleanup;
+
+ ff = procfile_readall(ff);
+ if(!ff) goto dcsp_cleanup;
+
+ ret = 1;
+
+dcsp_cleanup:
+ if (!ret)
+ perror("Cannot open /sys/kernel/debug/extfrag/extfrag_index file");
+ procfile_close(ff);
+ return ret;
+}
+
+static void debugfs_parse_args(int argc, char **argv)
+{
+ int i, freq = 0;
+ for(i = 1; i < argc; i++) {
+ if(!freq) {
+ int n = (int)str2l(argv[i]);
+ if(n > 0) {
+ freq = n;
+ continue;
+ }
+ }
+
+ if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
+ if(!debugfs_check_sys_permission()) {
+ exit(2);
+ }
+ printf("OK\n");
+ exit(0);
+ }
+ }
+
+ if(freq > 0) update_every = freq;
+}
+
+int main(int argc, char **argv)
+{
+ // debug_flags = D_PROCFILE;
+ stderror = stderr;
+
+ // set the name for logging
+ program_name = "debugfs.plugin";
+
+ // disable syslog for debugfs.plugin
+ error_log_syslog = 0;
+
+ netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
+ if (verify_netdata_host_prefix() == -1)
+ exit(1);
+
+ user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
+ if (user_config_dir == NULL) {
+ user_config_dir = CONFIG_DIR;
+ }
+
+ stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
+ if (stock_config_dir == NULL) {
+ // info("NETDATA_CONFIG_DIR is not passed from netdata");
+ stock_config_dir = LIBCONFIG_DIR;
+ }
+
+ // FIXME: should first check if /sys/kernel/debug is mounted
+
+ // FIXME: remove debugfs_check_sys_permission() after https://github.com/netdata/netdata/issues/15048 is fixed
+ if (!debugfs_check_capabilities() && !debugfs_am_i_running_as_root() && !debugfs_check_sys_permission()) {
+ uid_t uid = getuid(), euid = geteuid();
+#ifdef HAVE_CAPABILITY
+ error(
+ "debugfs.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+ "Without these, debugfs.plugin cannot access /sys/kernel/debug. "
+ "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
+ "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; ",
+ uid,
+ euid,
+ argv[0],
+ argv[0],
+ argv[0]);
+#else
+ error(
+ "debugfs.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
+ "Without these, debugfs.plugin cannot access /sys/kernel/debug."
+ "Your system does not support capabilities. "
+ "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; ",
+ uid,
+ euid,
+ argv[0],
+ argv[0]);
+#endif
+ exit(1);
+ }
+
+ // if (!debugfs_check_sys_permission()) {
+ // exit(2);
+ // }
+
+ debugfs_parse_args(argc, argv);
+
+ size_t iteration;
+ usec_t step = update_every * USEC_PER_SEC;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+
+ for (iteration = 0; iteration < 86400; iteration++) {
+ heartbeat_next(&hb, step);
+ int enabled = 0;
+
+ for (int i = 0; debugfs_modules[i].name; i++) {
+ struct debugfs_module *pm = &debugfs_modules[i];
+ if (unlikely(!pm->enabled))
+ continue;
+
+ pm->enabled = !pm->func(update_every, pm->name);
+ if (likely(pm->enabled))
+ enabled++;
+ }
+ if (!enabled) {
+ info("all modules are disabled, exiting...");
+ return 1;
+ }
+ }
+
+ fprintf(stdout, "EXIT\n");
+ fflush(stdout);
+ return 0;
+}