summaryrefslogtreecommitdiffstats
path: root/src/libnetdata/paths
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:33:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:34:10 +0000
commit83ba6762cc43d9db581b979bb5e3445669e46cc2 (patch)
tree2e69833b43f791ed253a7a20318b767ebe56cdb8 /src/libnetdata/paths
parentReleasing debian version 1.47.5-1. (diff)
downloadnetdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.tar.xz
netdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.zip
Merging upstream version 2.0.3+dfsg (Closes: #923993, #1042533, #1045145).
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libnetdata/paths')
-rw-r--r--src/libnetdata/paths/paths.c327
-rw-r--r--src/libnetdata/paths/paths.h26
2 files changed, 353 insertions, 0 deletions
diff --git a/src/libnetdata/paths/paths.c b/src/libnetdata/paths/paths.c
new file mode 100644
index 000000000..c68ee805f
--- /dev/null
+++ b/src/libnetdata/paths/paths.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "paths.h"
+
+static int is_procfs(const char *path, char **reason) {
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ (void)path;
+ (void)reason;
+#else
+ struct statfs stat;
+
+ if (statfs(path, &stat) == -1) {
+ if (reason)
+ *reason = "failed to statfs()";
+ return -1;
+ }
+
+#if defined PROC_SUPER_MAGIC
+ if (stat.f_type != PROC_SUPER_MAGIC) {
+ if (reason)
+ *reason = "type is not procfs";
+ return -1;
+ }
+#endif
+
+#endif
+
+ return 0;
+}
+
+static int is_sysfs(const char *path, char **reason) {
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ (void)path;
+ (void)reason;
+#else
+ struct statfs stat;
+
+ if (statfs(path, &stat) == -1) {
+ if (reason)
+ *reason = "failed to statfs()";
+ return -1;
+ }
+
+#if defined SYSFS_MAGIC
+ if (stat.f_type != SYSFS_MAGIC) {
+ if (reason)
+ *reason = "type is not sysfs";
+ return -1;
+ }
+#endif
+
+#endif
+
+ return 0;
+}
+
+int verify_netdata_host_prefix(bool log_msg) {
+ if(!netdata_configured_host_prefix)
+ netdata_configured_host_prefix = "";
+
+ if(!*netdata_configured_host_prefix)
+ return 0;
+
+ char path[FILENAME_MAX];
+ char *reason = "unknown reason";
+ errno_clear();
+
+ strncpyz(path, netdata_configured_host_prefix, sizeof(path) - 1);
+
+ struct stat sb;
+ if (stat(path, &sb) == -1) {
+ reason = "failed to stat()";
+ goto failed;
+ }
+
+ if((sb.st_mode & S_IFMT) != S_IFDIR) {
+ errno = EINVAL;
+ reason = "is not a directory";
+ goto failed;
+ }
+
+ snprintfz(path, sizeof(path), "%s/proc", netdata_configured_host_prefix);
+ if(is_procfs(path, &reason) == -1)
+ goto failed;
+
+ snprintfz(path, sizeof(path), "%s/sys", netdata_configured_host_prefix);
+ if(is_sysfs(path, &reason) == -1)
+ goto failed;
+
+ if (netdata_configured_host_prefix && *netdata_configured_host_prefix) {
+ if (log_msg)
+ netdata_log_info("Using host prefix directory '%s'", netdata_configured_host_prefix);
+ }
+
+ return 0;
+
+failed:
+ if (log_msg)
+ netdata_log_error("Ignoring host prefix '%s': path '%s' %s", netdata_configured_host_prefix, path, reason);
+
+ netdata_configured_host_prefix = "";
+ return -1;
+}
+
+size_t filename_from_path_entry(char out[FILENAME_MAX], const char *path, const char *entry, const char *extension) {
+ if(unlikely(!path || !*path)) path = ".";
+ if(unlikely(!entry)) entry = "";
+
+ // skip trailing slashes in path
+ size_t len = strlen(path);
+ while(len > 0 && path[len - 1] == '/') len--;
+
+ // skip leading slashes in subpath
+ while(entry[0] == '/') entry++;
+
+ // if the last character in path is / and (there is a subpath or path is now empty)
+ // keep the trailing slash in path and remove the additional slash
+ char *slash = "/";
+ if(path[len] == '/' && (*entry || len == 0)) {
+ slash = "";
+ len++;
+ }
+ else if(!*entry) {
+ // there is no entry
+ // no need for trailing slash
+ slash = "";
+ }
+
+ return snprintfz(out, FILENAME_MAX, "%.*s%s%s%s%s", (int)len, path, slash, entry,
+ extension && *extension ? "." : "",
+ extension && *extension ? extension : "");
+}
+
+char *filename_from_path_entry_strdupz(const char *path, const char *entry) {
+ char filename[FILENAME_MAX];
+ filename_from_path_entry(filename, path, entry, NULL);
+ return strdupz(filename);
+}
+
+bool filename_is_dir(const char *filename, bool create_it) {
+ CLEAN_CHAR_P *buffer = NULL;
+
+ size_t max_links = 100;
+
+ bool is_dir = false;
+ struct stat st;
+ while(max_links && stat(filename, &st) == 0) {
+ if ((st.st_mode & S_IFMT) == S_IFDIR)
+ is_dir = true;
+ else if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ max_links--;
+
+ if(!buffer)
+ buffer = mallocz(FILENAME_MAX);
+
+ char link_dst[FILENAME_MAX];
+ ssize_t l = readlink(filename, link_dst, FILENAME_MAX - 1);
+ if (l > 0) {
+ link_dst[l] = '\0';
+ strncpyz(buffer, link_dst, FILENAME_MAX - 1);
+ filename = buffer;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if(!is_dir && create_it && max_links == 100 && mkdir(filename, 0750) == 0)
+ is_dir = true;
+
+ return is_dir;
+}
+
+bool path_entry_is_dir(const char *path, const char *entry, bool create_it) {
+ char filename[FILENAME_MAX];
+ filename_from_path_entry(filename, path, entry, NULL);
+ return filename_is_dir(filename, create_it);
+}
+
+bool filename_is_file(const char *filename) {
+ CLEAN_CHAR_P *buffer = NULL;
+
+ size_t max_links = 100;
+
+ bool is_file = false;
+ struct stat st;
+ while(max_links && stat(filename, &st) == 0) {
+ if((st.st_mode & S_IFMT) == S_IFREG)
+ is_file = true;
+ else if((st.st_mode & S_IFMT) == S_IFLNK) {
+ max_links--;
+
+ if(!buffer)
+ buffer = mallocz(FILENAME_MAX);
+
+ char link_dst[FILENAME_MAX];
+ ssize_t l = readlink(filename, link_dst, FILENAME_MAX - 1);
+ if(l > 0) {
+ link_dst[l] = '\0';
+ strncpyz(buffer, link_dst, FILENAME_MAX - 1);
+ filename = buffer;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ return is_file;
+}
+
+bool path_entry_is_file(const char *path, const char *entry) {
+ char filename[FILENAME_MAX];
+ filename_from_path_entry(filename, path, entry, NULL);
+ return filename_is_file(filename);
+}
+
+void recursive_config_double_dir_load(const char *user_path, const char *stock_path, const char *entry, int (*callback)(const char *filename, void *data, bool stock_config), void *data, size_t depth) {
+ if(depth > 3) {
+ netdata_log_error("CONFIG: Max directory depth reached while reading user path '%s', stock path '%s', subpath '%s'", user_path, stock_path,
+ entry);
+ return;
+ }
+
+ if(!stock_path)
+ stock_path = user_path;
+
+ char *udir = filename_from_path_entry_strdupz(user_path, entry);
+ char *sdir = filename_from_path_entry_strdupz(stock_path, entry);
+
+ netdata_log_debug(D_HEALTH, "CONFIG traversing user-config directory '%s', stock config directory '%s'", udir, sdir);
+
+ DIR *dir = opendir(udir);
+ if (!dir) {
+ netdata_log_error("CONFIG cannot open user-config directory '%s'.", udir);
+ }
+ else {
+ struct dirent *de = NULL;
+ while((de = readdir(dir))) {
+ if(de->d_type == DT_DIR || de->d_type == DT_LNK) {
+ if( !de->d_name[0] ||
+ (de->d_name[0] == '.' && de->d_name[1] == '\0') ||
+ (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+ ) {
+ netdata_log_debug(D_HEALTH, "CONFIG ignoring user-config directory '%s/%s'", udir, de->d_name);
+ continue;
+ }
+
+ if(path_entry_is_dir(udir, de->d_name, false)) {
+ recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data, depth + 1);
+ continue;
+ }
+ }
+
+ if(de->d_type == DT_UNKNOWN || de->d_type == DT_REG || de->d_type == DT_LNK) {
+ size_t len = strlen(de->d_name);
+ if(path_entry_is_file(udir, de->d_name) &&
+ len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
+ char *filename = filename_from_path_entry_strdupz(udir, de->d_name);
+ netdata_log_debug(D_HEALTH, "CONFIG calling callback for user file '%s'", filename);
+ callback(filename, data, false);
+ freez(filename);
+ continue;
+ }
+ }
+
+ netdata_log_debug(D_HEALTH, "CONFIG ignoring user-config file '%s/%s' of type %d", udir, de->d_name, (int)de->d_type);
+ }
+
+ closedir(dir);
+ }
+
+ netdata_log_debug(D_HEALTH, "CONFIG traversing stock config directory '%s', user config directory '%s'", sdir, udir);
+
+ dir = opendir(sdir);
+ if (!dir) {
+ netdata_log_error("CONFIG cannot open stock config directory '%s'.", sdir);
+ }
+ else {
+ if (strcmp(udir, sdir)) {
+ struct dirent *de = NULL;
+ while((de = readdir(dir))) {
+ if(de->d_type == DT_DIR || de->d_type == DT_LNK) {
+ if( !de->d_name[0] ||
+ (de->d_name[0] == '.' && de->d_name[1] == '\0') ||
+ (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+ ) {
+ netdata_log_debug(D_HEALTH, "CONFIG ignoring stock config directory '%s/%s'", sdir, de->d_name);
+ continue;
+ }
+
+ if(path_entry_is_dir(sdir, de->d_name, false)) {
+ // we recurse in stock subdirectory, only when there is no corresponding
+ // user subdirectory - to avoid reading the files twice
+
+ if(!path_entry_is_dir(udir, de->d_name, false))
+ recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data, depth + 1);
+
+ continue;
+ }
+ }
+
+ if(de->d_type == DT_UNKNOWN || de->d_type == DT_REG || de->d_type == DT_LNK) {
+ size_t len = strlen(de->d_name);
+ if(path_entry_is_file(sdir, de->d_name) && !path_entry_is_file(udir, de->d_name) &&
+ len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
+ char *filename = filename_from_path_entry_strdupz(sdir, de->d_name);
+ netdata_log_debug(D_HEALTH, "CONFIG calling callback for stock file '%s'", filename);
+ callback(filename, data, true);
+ freez(filename);
+ continue;
+ }
+
+ }
+
+ netdata_log_debug(D_HEALTH, "CONFIG ignoring stock-config file '%s/%s' of type %d", udir, de->d_name, (int)de->d_type);
+ }
+ }
+ closedir(dir);
+ }
+
+ netdata_log_debug(D_HEALTH, "CONFIG done traversing user-config directory '%s', stock config directory '%s'", udir, sdir);
+
+ freez(udir);
+ freez(sdir);
+}
diff --git a/src/libnetdata/paths/paths.h b/src/libnetdata/paths/paths.h
new file mode 100644
index 000000000..9c5a8a748
--- /dev/null
+++ b/src/libnetdata/paths/paths.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_PATHS_H
+#define NETDATA_PATHS_H
+
+#include "../libnetdata.h"
+
+size_t filename_from_path_entry(char out[FILENAME_MAX], const char *path, const char *entry, const char *extension);
+char *filename_from_path_entry_strdupz(const char *path, const char *entry);
+
+bool filename_is_file(const char *filename);
+bool filename_is_dir(const char *filename, bool create_it);
+
+bool path_entry_is_file(const char *path, const char *entry);
+bool path_entry_is_dir(const char *path, const char *entry, bool create_it);
+
+void recursive_config_double_dir_load(
+ const char *user_path
+ , const char *stock_path
+ , const char *entry
+ , int (*callback)(const char *filename, void *data, bool stock_config)
+ , void *data
+ , size_t depth
+);
+
+#endif //NETDATA_PATHS_H