master
c 450 lines 12.9 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "apps_plugin.h"
4
5 static uint32_t
6 all_files_len = 0,
7 all_files_size = 0;
8
9 uint32_t all_files_len_get(void) {
10 (void)all_files_size;
11 return all_files_len;
12 }
13
14 #if (PROCESSES_HAVE_FDS == 1)
15 // ----------------------------------------------------------------------------
16 // file descriptor
17 //
18 // this is used to keep a global list of all open files of the system.
19 // it is needed in order to calculate the unique files processes have open.
20
21 #define FILE_DESCRIPTORS_INCREASE_STEP 100
22
23 // types for struct file_descriptor->type
24 typedef enum __attribute__((packed)) fd_filetype {
25 FILETYPE_OTHER,
26 FILETYPE_FILE,
27 FILETYPE_PIPE,
28 FILETYPE_SOCKET,
29 FILETYPE_INOTIFY,
30 FILETYPE_EVENTFD,
31 FILETYPE_EVENTPOLL,
32 FILETYPE_TIMERFD,
33 FILETYPE_SIGNALFD
34 } FD_FILETYPE;
35
36 struct file_descriptor {
37 avl_t avl;
38
39 #ifdef NETDATA_INTERNAL_CHECKS
40 uint32_t magic;
41 #endif /* NETDATA_INTERNAL_CHECKS */
42
43 const char *name;
44 uint32_t hash;
45 uint32_t count;
46 uint32_t pos;
47 FD_FILETYPE type;
48 } *all_files = NULL;
49
50 // ----------------------------------------------------------------------------
51
52 static inline void reallocate_target_fds(struct target *w) {
53 if(unlikely(!w))
54 return;
55
56 if(unlikely(!w->target_fds || w->target_fds_size < all_files_size)) {
57 w->target_fds = reallocz(w->target_fds, sizeof(int) * all_files_size);
58 memset(&w->target_fds[w->target_fds_size], 0, sizeof(int) * (all_files_size - w->target_fds_size));
59 w->target_fds_size = all_files_size;
60 }
61 }
62
63 static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
64 switch(type) {
65 case FILETYPE_SOCKET:
66 openfds->sockets++;
67 break;
68
69 case FILETYPE_FILE:
70 openfds->files++;
71 break;
72
73 case FILETYPE_PIPE:
74 openfds->pipes++;
75 break;
76
77 case FILETYPE_INOTIFY:
78 openfds->inotifies++;
79 break;
80
81 case FILETYPE_EVENTFD:
82 openfds->eventfds++;
83 break;
84
85 case FILETYPE_TIMERFD:
86 openfds->timerfds++;
87 break;
88
89 case FILETYPE_SIGNALFD:
90 openfds->signalfds++;
91 break;
92
93 case FILETYPE_EVENTPOLL:
94 openfds->eventpolls++;
95 break;
96
97 case FILETYPE_OTHER:
98 openfds->other++;
99 break;
100 }
101 }
102
103 static inline void aggregate_fd_on_target(int fd, struct target *w) {
104 if(unlikely(!w))
105 return;
106
107 if(unlikely(w->target_fds[fd])) {
108 // it is already aggregated
109 // just increase its usage counter
110 w->target_fds[fd]++;
111 return;
112 }
113
114 // increase its usage counter
115 // so that we will not add it again
116 w->target_fds[fd]++;
117
118 aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
119 }
120
121 void aggregate_pid_fds_on_targets(struct pid_stat *p) {
122 if(enable_file_charts == CONFIG_BOOLEAN_AUTO && all_files_len > MAX_SYSTEM_FD_TO_ALLOW_FILES_PROCESSING) {
123 nd_log(NDLS_COLLECTORS, NDLP_NOTICE, "apps.plugin: the number of system file descriptors are too many (%u), "
124 "disabling file charts. If you want this enabled, set the 'with-files' "
125 "parameter to [plugin:apps] section of netdata.conf", all_files_size);
126
127 enable_file_charts = CONFIG_BOOLEAN_NO;
128 obsolete_file_charts = true;
129 return;
130 }
131
132 if(unlikely(!p->updated)) {
133 // the process is not running
134 return;
135 }
136
137 struct target
138 #if (PROCESSES_HAVE_UID == 1)
139 *u = p->uid_target,
140 #endif
141 #if (PROCESSES_HAVE_GID == 1)
142 *g = p->gid_target,
143 #endif
144 *w = p->target;
145
146 reallocate_target_fds(w);
147 #if (PROCESSES_HAVE_UID == 1)
148 reallocate_target_fds(u);
149 #endif
150 #if (PROCESSES_HAVE_GID == 1)
151 reallocate_target_fds(g);
152 #endif
153
154 #if (PROCESSES_HAVE_FDS == 1)
155 p->openfds.files = 0;
156 p->openfds.pipes = 0;
157 p->openfds.sockets = 0;
158 p->openfds.inotifies = 0;
159 p->openfds.eventfds = 0;
160 p->openfds.timerfds = 0;
161 p->openfds.signalfds = 0;
162 p->openfds.eventpolls = 0;
163 p->openfds.other = 0;
164
165 uint32_t c, size = p->fds_size;
166 struct pid_fd *fds = p->fds;
167 for(c = 0; c < size ;c++) {
168 int fd = fds[c].fd;
169
170 if(likely(fd <= 0 || (uint32_t)fd >= all_files_size))
171 continue;
172
173 aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
174
175 aggregate_fd_on_target(fd, w);
176 #if (PROCESSES_HAVE_UID == 1)
177 aggregate_fd_on_target(fd, u);
178 #endif
179 #if (PROCESSES_HAVE_GID == 1)
180 aggregate_fd_on_target(fd, g);
181 #endif
182 }
183 #endif
184 }
185
186 // ----------------------------------------------------------------------------
187
188 int file_descriptor_compare(void* a, void* b) {
189 #ifdef NETDATA_INTERNAL_CHECKS
190 if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
191 netdata_log_error("Corrupted index data detected. Please report this.");
192 #endif /* NETDATA_INTERNAL_CHECKS */
193
194 if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
195 return -1;
196
197 else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
198 return 1;
199
200 else
201 return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
202 }
203
204 // int file_descriptor_iterator(avl_t *a) { if(a) {}; return 0; }
205
206 avl_tree_type all_files_index = {
207 NULL,
208 file_descriptor_compare
209 };
210
211 static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
212 struct file_descriptor tmp;
213 tmp.hash = (hash)?hash:simple_hash(name);
214 tmp.name = name;
215 tmp.count = 0;
216 tmp.pos = 0;
217 #ifdef NETDATA_INTERNAL_CHECKS
218 tmp.magic = 0x0BADCAFE;
219 #endif /* NETDATA_INTERNAL_CHECKS */
220
221 return (struct file_descriptor *)avl_search(&all_files_index, (avl_t *) &tmp);
222 }
223
224 #define file_descriptor_add(fd) avl_insert(&all_files_index, (avl_t *)(fd))
225 #define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl_t *)(fd))
226
227 // ----------------------------------------------------------------------------
228
229 void file_descriptor_not_used(int id) {
230 if(id > 0 && (uint32_t)id < all_files_size) {
231
232 #ifdef NETDATA_INTERNAL_CHECKS
233 if(all_files[id].magic != 0x0BADCAFE) {
234 netdata_log_error("Ignoring request to remove empty file id %d.", id);
235 return;
236 }
237 #endif /* NETDATA_INTERNAL_CHECKS */
238
239 debug_log("decreasing slot %d (count = %d).", id, all_files[id].count);
240
241 if(all_files[id].count > 0) {
242 all_files[id].count--;
243
244 if(!all_files[id].count) {
245 debug_log(" >> slot %d is empty.", id);
246
247 if(unlikely(file_descriptor_remove(&all_files[id]) != (void *)&all_files[id]))
248 netdata_log_error("INTERNAL ERROR: removal of unused fd from index, removed a different fd");
249
250 #ifdef NETDATA_INTERNAL_CHECKS
251 all_files[id].magic = 0x00000000;
252 #endif /* NETDATA_INTERNAL_CHECKS */
253 all_files_len--;
254 }
255 }
256 else
257 netdata_log_error("Request to decrease counter of fd %d (%s), while the use counter is 0",
258 id, all_files[id].name);
259 }
260 else
261 netdata_log_error("Request to decrease counter of fd %d, which is outside the array size (1 to %"PRIu32")",
262 id, all_files_size);
263 }
264
265 static inline void all_files_grow() {
266 void *old = all_files;
267
268 uint32_t new_size = (all_files_size > 0) ? all_files_size * 2 : 2048;
269
270 // there is no empty slot
271 all_files = reallocz(all_files, new_size * sizeof(struct file_descriptor));
272
273 // if the address changed, we have to rebuild the index
274 // since all pointers are now invalid
275
276 if(unlikely(old && old != (void *)all_files)) {
277 all_files_index.root = NULL;
278 for(uint32_t i = 0; i < all_files_size; i++) {
279 if(!all_files[i].count) continue;
280 if(unlikely(file_descriptor_add(&all_files[i]) != (void *)&all_files[i]))
281 netdata_log_error("INTERNAL ERROR: duplicate indexing of fd during realloc.");
282 }
283 }
284
285 // initialize the newly added entries
286
287 for(uint32_t i = all_files_size; i < new_size; i++) {
288 all_files[i].count = 0;
289 all_files[i].name = NULL;
290 #ifdef NETDATA_INTERNAL_CHECKS
291 all_files[i].magic = 0x00000000;
292 #endif /* NETDATA_INTERNAL_CHECKS */
293 all_files[i].pos = i;
294 }
295
296 if(unlikely(!all_files_size)) all_files_len = 1;
297 all_files_size = new_size;
298 }
299
300 static inline uint32_t file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) {
301 // check we have enough memory to add it
302 if(!all_files || all_files_len == all_files_size)
303 all_files_grow();
304
305 debug_log(" >> searching for empty slot.");
306
307 // search for an empty slot
308
309 static int last_pos = 0;
310 uint32_t i, c;
311 for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
312 if(c >= all_files_size) c = 0;
313 if(c == 0) continue;
314
315 if(!all_files[c].count) {
316 debug_log(" >> Examining slot %d.", c);
317
318 #ifdef NETDATA_INTERNAL_CHECKS
319 if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
320 netdata_log_error("fd on position %"PRIu32" is not cleared properly. It still has %s in it.", c, all_files[c].name);
321 #endif /* NETDATA_INTERNAL_CHECKS */
322
323 debug_log(" >> %s fd position %d for %s (last name: %s)", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
324
325 freez((void *)all_files[c].name);
326 all_files[c].name = NULL;
327 last_pos = c;
328 break;
329 }
330 }
331
332 all_files_len++;
333
334 if(i == all_files_size) {
335 fatal("We should find an empty slot, but there isn't any");
336 exit(1);
337 }
338 // else we have an empty slot in 'c'
339
340 debug_log(" >> updating slot %d.", c);
341
342 all_files[c].name = strdupz(name);
343 all_files[c].hash = hash;
344 all_files[c].type = type;
345 all_files[c].pos = c;
346 all_files[c].count = 1;
347 #ifdef NETDATA_INTERNAL_CHECKS
348 all_files[c].magic = 0x0BADCAFE;
349 #endif /* NETDATA_INTERNAL_CHECKS */
350 if(unlikely(file_descriptor_add(&all_files[c]) != (void *)&all_files[c]))
351 netdata_log_error("INTERNAL ERROR: duplicate indexing of fd.");
352
353 return c;
354 }
355
356 uint32_t file_descriptor_find_or_add(const char *name, uint32_t hash) {
357 if(unlikely(!hash))
358 hash = simple_hash(name);
359
360 debug_log("adding or finding name '%s' with hash %u", name, hash);
361
362 struct file_descriptor *fd = file_descriptor_find(name, hash);
363 if(fd) {
364 // found
365 debug_log(" >> found on slot %d", fd->pos);
366
367 fd->count++;
368 return fd->pos;
369 }
370 // not found
371
372 FD_FILETYPE type;
373 if(likely(name[0] == '/')) type = FILETYPE_FILE;
374 else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
375 else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
376 else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
377 const char *t = &name[11];
378
379 if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
380 else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
381 else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
382 else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
383 else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
384 else {
385 debug_log("UNKNOWN anonymous inode: %s", name);
386 type = FILETYPE_OTHER;
387 }
388 }
389 else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
390 else {
391 debug_log("UNKNOWN linkname: %s", name);
392 type = FILETYPE_OTHER;
393 }
394
395 return file_descriptor_set_on_empty_slot(name, hash, type);
396 }
397
398 void clear_pid_fd(struct pid_fd *pfd) {
399 pfd->fd = 0;
400
401 #if defined(OS_LINUX)
402 pfd->link_hash = 0;
403 pfd->inode = 0;
404 pfd->cache_iterations_counter = 0;
405 pfd->cache_iterations_reset = 0;
406 #endif
407 }
408
409 void make_all_pid_fds_negative(struct pid_stat *p) {
410 struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
411 while(pfd < pfdend) {
412 pfd->fd = -(pfd->fd);
413 pfd++;
414 }
415 }
416
417 static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
418 struct pid_fd *pfd = p->fds, *pfdend = &p->fds[p->fds_size];
419
420 while(pfd < pfdend) {
421 int fd = pfd->fd;
422
423 if(unlikely(fd < 0)) {
424 file_descriptor_not_used(-(fd));
425 clear_pid_fd(pfd);
426 }
427
428 pfd++;
429 }
430 }
431
432 void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
433 struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
434
435 while(pfd < pfdend) {
436 #if defined(OS_LINUX)
437 pfd->filename = NULL;
438 #endif
439 clear_pid_fd(pfd);
440 pfd++;
441 }
442 }
443
444 int read_pid_file_descriptors(struct pid_stat *p, void *ptr) {
445 bool ret = OS_FUNCTION(apps_os_read_pid_fds)(p, ptr);
446 cleanup_negative_pid_fds(p);
447
448 return ret ? 1 : 0;
449 }
450 #endif