master
c 327 lines 10.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "paths.h"
4
5 static int is_procfs(const char *path, char **reason) {
6 #if defined(__APPLE__) || defined(__FreeBSD__)
7 (void)path;
8 (void)reason;
9 #else
10 struct statfs stat;
11
12 if (statfs(path, &stat) == -1) {
13 if (reason)
14 *reason = "failed to statfs()";
15 return -1;
16 }
17
18 #if defined PROC_SUPER_MAGIC
19 if (stat.f_type != PROC_SUPER_MAGIC) {
20 if (reason)
21 *reason = "type is not procfs";
22 return -1;
23 }
24 #endif
25
26 #endif
27
28 return 0;
29 }
30
31 static int is_sysfs(const char *path, char **reason) {
32 #if defined(__APPLE__) || defined(__FreeBSD__)
33 (void)path;
34 (void)reason;
35 #else
36 struct statfs stat;
37
38 if (statfs(path, &stat) == -1) {
39 if (reason)
40 *reason = "failed to statfs()";
41 return -1;
42 }
43
44 #if defined SYSFS_MAGIC
45 if (stat.f_type != SYSFS_MAGIC) {
46 if (reason)
47 *reason = "type is not sysfs";
48 return -1;
49 }
50 #endif
51
52 #endif
53
54 return 0;
55 }
56
57 int verify_netdata_host_prefix(bool log_msg) {
58 if(!netdata_configured_host_prefix)
59 netdata_configured_host_prefix = "";
60
61 if(!*netdata_configured_host_prefix)
62 return 0;
63
64 char path[FILENAME_MAX];
65 char *reason = "unknown reason";
66 errno_clear();
67
68 strncpyz(path, netdata_configured_host_prefix, sizeof(path) - 1);
69
70 struct stat sb;
71 if (stat(path, &sb) == -1) {
72 reason = "failed to stat()";
73 goto failed;
74 }
75
76 if((sb.st_mode & S_IFMT) != S_IFDIR) {
77 errno = EINVAL;
78 reason = "is not a directory";
79 goto failed;
80 }
81
82 snprintfz(path, sizeof(path), "%s/proc", netdata_configured_host_prefix);
83 if(is_procfs(path, &reason) == -1)
84 goto failed;
85
86 snprintfz(path, sizeof(path), "%s/sys", netdata_configured_host_prefix);
87 if(is_sysfs(path, &reason) == -1)
88 goto failed;
89
90 if (netdata_configured_host_prefix && *netdata_configured_host_prefix) {
91 if (log_msg)
92 netdata_log_info("Using host prefix directory '%s'", netdata_configured_host_prefix);
93 }
94
95 return 0;
96
97 failed:
98 if (log_msg)
99 netdata_log_error("Ignoring host prefix '%s': path '%s' %s", netdata_configured_host_prefix, path, reason);
100
101 netdata_configured_host_prefix = "";
102 return -1;
103 }
104
105 size_t filename_from_path_entry(char out[FILENAME_MAX], const char *path, const char *entry, const char *extension) {
106 if(unlikely(!path || !*path)) path = ".";
107 if(unlikely(!entry)) entry = "";
108
109 // skip trailing slashes in path
110 size_t len = strlen(path);
111 while(len > 0 && path[len - 1] == '/') len--;
112
113 // skip leading slashes in subpath
114 while(entry[0] == '/') entry++;
115
116 // if the last character in path is / and (there is a subpath or path is now empty)
117 // keep the trailing slash in path and remove the additional slash
118 char *slash = "/";
119 if(path[len] == '/' && (*entry || len == 0)) {
120 slash = "";
121 len++;
122 }
123 else if(!*entry) {
124 // there is no entry
125 // no need for trailing slash
126 slash = "";
127 }
128
129 return snprintfz(out, FILENAME_MAX, "%.*s%s%s%s%s", (int)len, path, slash, entry,
130 extension && *extension ? "." : "",
131 extension && *extension ? extension : "");
132 }
133
134 char *filename_from_path_entry_strdupz(const char *path, const char *entry) {
135 char filename[FILENAME_MAX];
136 filename_from_path_entry(filename, path, entry, NULL);
137 return strdupz(filename);
138 }
139
140 bool filename_is_dir(const char *filename, bool create_it) {
141 CLEAN_CHAR_P *buffer = NULL;
142
143 size_t max_links = 100;
144
145 bool is_dir = false;
146 struct stat st;
147 while(max_links && stat(filename, &st) == 0) {
148 if ((st.st_mode & S_IFMT) == S_IFDIR)
149 is_dir = true;
150 else if ((st.st_mode & S_IFMT) == S_IFLNK) {
151 max_links--;
152
153 if(!buffer)
154 buffer = mallocz(FILENAME_MAX);
155
156 char link_dst[FILENAME_MAX];
157 ssize_t l = readlink(filename, link_dst, FILENAME_MAX - 1);
158 if (l > 0) {
159 link_dst[l] = '\0';
160 strncpyz(buffer, link_dst, FILENAME_MAX - 1);
161 filename = buffer;
162 continue;
163 }
164 }
165
166 break;
167 }
168
169 if(!is_dir && create_it && max_links == 100 && mkdir(filename, 0750) == 0)
170 is_dir = true;
171
172 return is_dir;
173 }
174
175 bool path_entry_is_dir(const char *path, const char *entry, bool create_it) {
176 char filename[FILENAME_MAX];
177 filename_from_path_entry(filename, path, entry, NULL);
178 return filename_is_dir(filename, create_it);
179 }
180
181 bool filename_is_file(const char *filename) {
182 CLEAN_CHAR_P *buffer = NULL;
183
184 size_t max_links = 100;
185
186 bool is_file = false;
187 struct stat st;
188 while(max_links && stat(filename, &st) == 0) {
189 if((st.st_mode & S_IFMT) == S_IFREG)
190 is_file = true;
191 else if((st.st_mode & S_IFMT) == S_IFLNK) {
192 max_links--;
193
194 if(!buffer)
195 buffer = mallocz(FILENAME_MAX);
196
197 char link_dst[FILENAME_MAX];
198 ssize_t l = readlink(filename, link_dst, FILENAME_MAX - 1);
199 if(l > 0) {
200 link_dst[l] = '\0';
201 strncpyz(buffer, link_dst, FILENAME_MAX - 1);
202 filename = buffer;
203 continue;
204 }
205 }
206
207 break;
208 }
209
210 return is_file;
211 }
212
213 bool path_entry_is_file(const char *path, const char *entry) {
214 char filename[FILENAME_MAX];
215 filename_from_path_entry(filename, path, entry, NULL);
216 return filename_is_file(filename);
217 }
218
219 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) {
220 if(depth > 3) {
221 netdata_log_error("CONFIG: Max directory depth reached while reading user path '%s', stock path '%s', subpath '%s'", user_path, stock_path,
222 entry);
223 return;
224 }
225
226 if(!stock_path)
227 stock_path = user_path;
228
229 char *udir = filename_from_path_entry_strdupz(user_path, entry);
230 char *sdir = filename_from_path_entry_strdupz(stock_path, entry);
231
232 netdata_log_debug(D_HEALTH, "CONFIG traversing user-config directory '%s', stock config directory '%s'", udir, sdir);
233
234 DIR *dir = opendir(udir);
235 if (!dir) {
236 netdata_log_error("CONFIG cannot open user-config directory '%s'.", udir);
237 }
238 else {
239 struct dirent *de = NULL;
240 while((de = readdir(dir))) {
241 if(de->d_type == DT_DIR || de->d_type == DT_LNK) {
242 if( !de->d_name[0] ||
243 (de->d_name[0] == '.' && de->d_name[1] == '\0') ||
244 (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
245 ) {
246 netdata_log_debug(D_HEALTH, "CONFIG ignoring user-config directory '%s/%s'", udir, de->d_name);
247 continue;
248 }
249
250 if(path_entry_is_dir(udir, de->d_name, false)) {
251 recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data, depth + 1);
252 continue;
253 }
254 }
255
256 if(de->d_type == DT_UNKNOWN || de->d_type == DT_REG || de->d_type == DT_LNK) {
257 size_t len = strlen(de->d_name);
258 if(path_entry_is_file(udir, de->d_name) &&
259 len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
260 char *filename = filename_from_path_entry_strdupz(udir, de->d_name);
261 netdata_log_debug(D_HEALTH, "CONFIG calling callback for user file '%s'", filename);
262 callback(filename, data, false);
263 freez(filename);
264 continue;
265 }
266 }
267
268 netdata_log_debug(D_HEALTH, "CONFIG ignoring user-config file '%s/%s' of type %d", udir, de->d_name, (int)de->d_type);
269 }
270
271 closedir(dir);
272 }
273
274 netdata_log_debug(D_HEALTH, "CONFIG traversing stock config directory '%s', user config directory '%s'", sdir, udir);
275
276 dir = opendir(sdir);
277 if (!dir) {
278 netdata_log_error("CONFIG cannot open stock config directory '%s'.", sdir);
279 }
280 else {
281 if (strcmp(udir, sdir)) {
282 struct dirent *de = NULL;
283 while((de = readdir(dir))) {
284 if(de->d_type == DT_DIR || de->d_type == DT_LNK) {
285 if( !de->d_name[0] ||
286 (de->d_name[0] == '.' && de->d_name[1] == '\0') ||
287 (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
288 ) {
289 netdata_log_debug(D_HEALTH, "CONFIG ignoring stock config directory '%s/%s'", sdir, de->d_name);
290 continue;
291 }
292
293 if(path_entry_is_dir(sdir, de->d_name, false)) {
294 // we recurse in stock subdirectory, only when there is no corresponding
295 // user subdirectory - to avoid reading the files twice
296
297 if(!path_entry_is_dir(udir, de->d_name, false))
298 recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data, depth + 1);
299
300 continue;
301 }
302 }
303
304 if(de->d_type == DT_UNKNOWN || de->d_type == DT_REG || de->d_type == DT_LNK) {
305 size_t len = strlen(de->d_name);
306 if(path_entry_is_file(sdir, de->d_name) && !path_entry_is_file(udir, de->d_name) &&
307 len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
308 char *filename = filename_from_path_entry_strdupz(sdir, de->d_name);
309 netdata_log_debug(D_HEALTH, "CONFIG calling callback for stock file '%s'", filename);
310 callback(filename, data, true);
311 freez(filename);
312 continue;
313 }
314
315 }
316
317 netdata_log_debug(D_HEALTH, "CONFIG ignoring stock-config file '%s/%s' of type %d", udir, de->d_name, (int)de->d_type);
318 }
319 }
320 closedir(dir);
321 }
322
323 netdata_log_debug(D_HEALTH, "CONFIG done traversing user-config directory '%s', stock config directory '%s'", udir, sdir);
324
325 freez(udir);
326 freez(sdir);
327 }