master
c 907 lines 32 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "apps_plugin.h"
4 #include "libnetdata/parsers/duration.h"
5
6 #define APPS_PLUGIN_FUNCTIONS() do { \
7 fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
8 PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
9 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
10 RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
11 } while(0)
12
13 #define APPS_PLUGIN_GLOBAL_FUNCTIONS() do { \
14 fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"processes\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \
15 PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, APPS_PLUGIN_PROCESSES_FUNCTION_DESCRIPTION, \
16 (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID|HTTP_ACCESS_SAME_SPACE|HTTP_ACCESS_SENSITIVE_DATA), \
17 RRDFUNCTIONS_PRIORITY_DEFAULT / 10); \
18 } while(0)
19
20 // ----------------------------------------------------------------------------
21 // options
22
23 bool debug_enabled = false;
24
25 bool enable_detailed_uptime_charts = false;
26 bool enable_users_charts = true;
27 bool enable_groups_charts = true;
28 #if (PROCESSES_HAVE_SERVICE == 1)
29 bool enable_services_charts = true;
30 #endif
31 bool include_exited_childs = true;
32 bool proc_pid_cmdline_is_needed = true; // true when we need to read /proc/cmdline
33
34 #if defined(OS_FREEBSD) || defined(OS_MACOS)
35 int enable_file_charts = CONFIG_BOOLEAN_NO;
36 #elif defined(OS_LINUX)
37 int enable_file_charts = CONFIG_BOOLEAN_AUTO;
38 #elif defined(OS_WINDOWS)
39 int enable_file_charts = CONFIG_BOOLEAN_YES;
40 #endif
41 bool obsolete_file_charts = false;
42
43 // ----------------------------------------------------------------------------
44 // internal counters
45
46 size_t
47 global_iterations_counter = 1,
48 calls_counter = 0,
49 file_counter = 0,
50 filenames_allocated_counter = 0,
51 inodes_changed_counter = 0,
52 links_changed_counter = 0,
53 targets_assignment_counter = 0,
54 apps_groups_targets_count = 0; // # of apps_groups.conf targets
55
56 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
57 bool enable_guest_charts = false;
58 bool show_guest_time = false; // set when guest values are collected
59 #endif
60
61 uint32_t
62 all_files_len = 0,
63 all_files_size = 0;
64
65 // --------------------------------------------------------------------------------------------------------------------
66 // Normalization
67 //
68 // With normalization we lower the collected metrics by a factor to make them
69 // match the total utilization of the system.
70 // The discrepancy exists because apps.plugin needs some time to collect all
71 // the metrics. This results in utilization that exceeds the total utilization
72 // of the system.
73 //
74 // During normalization, we align the per-process utilization to the global
75 // utilization of the system. We first consume the exited children utilization
76 // and it the collected values is above the total, we proportionally scale each
77 // reported metric.
78
79 // the total system time, as reported by /proc/stat
80 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
81 kernel_uint_t
82 global_utime = 0,
83 global_stime = 0,
84 global_gtime = 0;
85 #endif
86
87 // the normalization ratios, as calculated by normalize_utilization()
88 NETDATA_DOUBLE
89 utime_fix_ratio = 1.0,
90 stime_fix_ratio = 1.0,
91 gtime_fix_ratio = 1.0,
92 minflt_fix_ratio = 1.0,
93 majflt_fix_ratio = 1.0,
94 cutime_fix_ratio = 1.0,
95 cstime_fix_ratio = 1.0,
96 cgtime_fix_ratio = 1.0,
97 cminflt_fix_ratio = 1.0,
98 cmajflt_fix_ratio = 1.0;
99
100 // --------------------------------------------------------------------------------------------------------------------
101
102 int update_every = 1;
103
104 #if defined(OS_LINUX)
105 proc_state proc_state_count[PROC_STATUS_END];
106 const char *proc_states[] = {
107 [PROC_STATUS_RUNNING] = "running",
108 [PROC_STATUS_SLEEPING] = "sleeping_interruptible",
109 [PROC_STATUS_SLEEPING_D] = "sleeping_uninterruptible",
110 [PROC_STATUS_ZOMBIE] = "zombie",
111 [PROC_STATUS_STOPPED] = "stopped",
112 };
113 #endif
114
115 // will be changed to getenv(NETDATA_USER_CONFIG_DIR) if it exists
116 static char *user_config_dir = CONFIG_DIR;
117 static char *stock_config_dir = LIBCONFIG_DIR;
118
119 size_t pagesize;
120
121 void sanitize_apps_plugin_chart_meta(char *buf) {
122 external_plugins_sanitize(buf, buf, strlen(buf) + 1);
123 }
124
125 // ----------------------------------------------------------------------------
126 // update chart dimensions
127
128 // Helper function to count the number of processes in the linked list
129 int count_processes(struct pid_stat *root) {
130 int count = 0;
131
132 for(struct pid_stat *p = root; p ; p = p->next)
133 if(p->updated) count++;
134
135 return count;
136 }
137
138 // Comparator function to sort by pid
139 int compare_by_pid(const void *a, const void *b) {
140 struct pid_stat *pa = *(struct pid_stat **)a;
141 struct pid_stat *pb = *(struct pid_stat **)b;
142 return ((int)pa->pid - (int)pb->pid);
143 }
144
145 // Function to print a process and its children recursively
146 void print_process_tree(struct pid_stat *root, struct pid_stat *parent, int depth, int total_processes) {
147 // Allocate an array of pointers for processes with the given parent
148 struct pid_stat **children = (struct pid_stat **)malloc(total_processes * sizeof(struct pid_stat *));
149 int children_count = 0;
150
151 // Populate the array with processes that have the given parent
152 struct pid_stat *p = root;
153 while (p != NULL) {
154 if (p->updated && p->parent == parent) {
155 children[children_count++] = p;
156 }
157 p = p->next;
158 }
159
160 // Sort the children array by pid
161 qsort(children, children_count, sizeof(struct pid_stat *), compare_by_pid);
162
163 // Print each child and recurse
164 for (int i = 0; i < children_count; i++) {
165 // Print the current process with indentation based on depth
166 if (depth > 0) {
167 for (int j = 0; j < (depth - 1) * 4; j++) {
168 printf(" ");
169 }
170 printf(" \\_ ");
171 }
172
173 #if (PROCESSES_HAVE_COMM_AND_NAME == 1)
174 printf("[%d] %s (name: %s) [%s]: %s\n", children[i]->pid,
175 string2str(children[i]->comm),
176 string2str(children[i]->name),
177 string2str(children[i]->target->name),
178 string2str(children[i]->cmdline));
179 #else
180 printf("[%d] orig: '%s' new: '%s' [target: %s]: cmdline: %s\n", children[i]->pid,
181 string2str(children[i]->comm_orig),
182 string2str(children[i]->comm),
183 string2str(children[i]->target->name),
184 string2str(children[i]->cmdline));
185 #endif
186
187 // Recurse to print this child's children
188 print_process_tree(root, children[i], depth + 1, total_processes);
189 }
190
191 // Free the allocated array
192 free(children);
193 }
194
195 // Function to print the full hierarchy
196 void print_hierarchy(struct pid_stat *root) {
197 // Count the total number of processes
198 int total_processes = count_processes(root);
199
200 // Start printing from processes with parent = NULL (i.e., root processes)
201 print_process_tree(root, NULL, 0, total_processes);
202 }
203
204 // ----------------------------------------------------------------------------
205 // update chart dimensions
206
207 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
208 static void normalize_utilization(struct target *root) {
209 struct target *w;
210
211 // children processing introduces spikes,
212 // here we try to eliminate them by disabling children processing either
213 // for specific dimensions or entirely.
214 // of course, either way, we disable it just for a single iteration.
215
216 kernel_uint_t max_time = os_get_system_cpus() * NSEC_PER_SEC;
217 kernel_uint_t utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
218
219 if(global_utime > max_time) global_utime = max_time;
220 if(global_stime > max_time) global_stime = max_time;
221 if(global_gtime > max_time) global_gtime = max_time;
222
223 for(w = root; w ; w = w->next) {
224 if(w->target || (!w->values[PDF_PROCESSES] && !w->exposed)) continue;
225
226 utime += w->values[PDF_UTIME];
227 stime += w->values[PDF_STIME];
228 gtime += w->values[PDF_GTIME];
229 cutime += w->values[PDF_CUTIME];
230 cstime += w->values[PDF_CSTIME];
231 cgtime += w->values[PDF_CGTIME];
232
233 minflt += w->values[PDF_MINFLT];
234 majflt += w->values[PDF_MAJFLT];
235 cminflt += w->values[PDF_CMINFLT];
236 cmajflt += w->values[PDF_CMAJFLT];
237 }
238
239 if(global_utime || global_stime || global_gtime) {
240 if(global_utime + global_stime + global_gtime > utime + cutime + stime + cstime + gtime + cgtime) {
241 // everything we collected fits
242 utime_fix_ratio =
243 stime_fix_ratio =
244 gtime_fix_ratio =
245 cutime_fix_ratio =
246 cstime_fix_ratio =
247 cgtime_fix_ratio = 1.0; //(NETDATA_DOUBLE)(global_utime + global_stime) / (NETDATA_DOUBLE)(utime + cutime + stime + cstime);
248 }
249 else if((global_utime + global_stime > utime + stime) && (cutime || cstime)) {
250 // children resources are too high,
251 // lower only the children resources
252 utime_fix_ratio =
253 stime_fix_ratio =
254 gtime_fix_ratio = 1.0;
255 cutime_fix_ratio =
256 cstime_fix_ratio =
257 cgtime_fix_ratio = (NETDATA_DOUBLE)((global_utime + global_stime) - (utime + stime)) / (NETDATA_DOUBLE)(cutime + cstime);
258 }
259 else if(utime || stime) {
260 // even running processes are unrealistic
261 // zero the children resources
262 // lower the running processes resources
263 utime_fix_ratio =
264 stime_fix_ratio =
265 gtime_fix_ratio = (NETDATA_DOUBLE)(global_utime + global_stime) / (NETDATA_DOUBLE)(utime + stime);
266 cutime_fix_ratio =
267 cstime_fix_ratio =
268 cgtime_fix_ratio = 0.0;
269 }
270 else {
271 utime_fix_ratio =
272 stime_fix_ratio =
273 gtime_fix_ratio =
274 cutime_fix_ratio =
275 cstime_fix_ratio =
276 cgtime_fix_ratio = 0.0;
277 }
278 }
279 else {
280 utime_fix_ratio =
281 stime_fix_ratio =
282 gtime_fix_ratio =
283 cutime_fix_ratio =
284 cstime_fix_ratio =
285 cgtime_fix_ratio = 0.0;
286 }
287
288 if(utime_fix_ratio > 1.0) utime_fix_ratio = 1.0;
289 if(cutime_fix_ratio > 1.0) cutime_fix_ratio = 1.0;
290 if(stime_fix_ratio > 1.0) stime_fix_ratio = 1.0;
291 if(cstime_fix_ratio > 1.0) cstime_fix_ratio = 1.0;
292 if(gtime_fix_ratio > 1.0) gtime_fix_ratio = 1.0;
293 if(cgtime_fix_ratio > 1.0) cgtime_fix_ratio = 1.0;
294
295 // if(utime_fix_ratio < 0.0) utime_fix_ratio = 0.0;
296 // if(cutime_fix_ratio < 0.0) cutime_fix_ratio = 0.0;
297 // if(stime_fix_ratio < 0.0) stime_fix_ratio = 0.0;
298 // if(cstime_fix_ratio < 0.0) cstime_fix_ratio = 0.0;
299 // if(gtime_fix_ratio < 0.0) gtime_fix_ratio = 0.0;
300 // if(cgtime_fix_ratio < 0.0) cgtime_fix_ratio = 0.0;
301
302 // TODO
303 // we use cpu time to normalize page faults
304 // the problem is that to find the proper max values
305 // for page faults we have to parse /proc/vmstat
306 // which is quite big to do it again (netdata does it already)
307 //
308 // a better solution could be to somehow have netdata
309 // do this normalization for us
310
311 if(utime || stime || gtime)
312 majflt_fix_ratio =
313 minflt_fix_ratio = (NETDATA_DOUBLE)(utime * utime_fix_ratio + stime * stime_fix_ratio + gtime * gtime_fix_ratio) / (NETDATA_DOUBLE)(utime + stime + gtime);
314 else
315 minflt_fix_ratio =
316 majflt_fix_ratio = 1.0;
317
318 if(cutime || cstime || cgtime)
319 cmajflt_fix_ratio =
320 cminflt_fix_ratio = (NETDATA_DOUBLE)(cutime * cutime_fix_ratio + cstime * cstime_fix_ratio + cgtime * cgtime_fix_ratio) / (NETDATA_DOUBLE)(cutime + cstime + cgtime);
321 else
322 cminflt_fix_ratio =
323 cmajflt_fix_ratio = 1.0;
324
325 // the report
326
327 debug_log(
328 "SYSTEM: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " "
329 "COLLECTED: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " cu=" KERNEL_UINT_FORMAT " cs=" KERNEL_UINT_FORMAT " cg=" KERNEL_UINT_FORMAT " "
330 "DELTA: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " "
331 "FIX: u=%0.2f s=%0.2f g=%0.2f cu=%0.2f cs=%0.2f cg=%0.2f "
332 "FINALLY: u=" KERNEL_UINT_FORMAT " s=" KERNEL_UINT_FORMAT " g=" KERNEL_UINT_FORMAT " cu=" KERNEL_UINT_FORMAT " cs=" KERNEL_UINT_FORMAT " cg=" KERNEL_UINT_FORMAT " "
333 , global_utime
334 , global_stime
335 , global_gtime
336 , utime
337 , stime
338 , gtime
339 , cutime
340 , cstime
341 , cgtime
342 , utime + cutime - global_utime
343 , stime + cstime - global_stime
344 , gtime + cgtime - global_gtime
345 , utime_fix_ratio
346 , stime_fix_ratio
347 , gtime_fix_ratio
348 , cutime_fix_ratio
349 , cstime_fix_ratio
350 , cgtime_fix_ratio
351 , (kernel_uint_t)(utime * utime_fix_ratio)
352 , (kernel_uint_t)(stime * stime_fix_ratio)
353 , (kernel_uint_t)(gtime * gtime_fix_ratio)
354 , (kernel_uint_t)(cutime * cutime_fix_ratio)
355 , (kernel_uint_t)(cstime * cstime_fix_ratio)
356 , (kernel_uint_t)(cgtime * cgtime_fix_ratio)
357 );
358 }
359 #endif
360
361 // ----------------------------------------------------------------------------
362 // parse command line arguments
363
364 int check_proc_1_io() {
365 int ret = 0;
366
367 #if defined(OS_LINUX)
368 procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
369 if(!ff) goto cleanup;
370
371 ff = procfile_readall(ff);
372 if(!ff) goto cleanup;
373
374 ret = 1;
375
376 cleanup:
377 procfile_close(ff);
378 #endif
379
380 return ret;
381 }
382
383 static bool profile_speed = false;
384 static bool print_tree_and_exit = false;
385 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
386 int pss_refresh_period = 0; // disabled by default
387 #endif
388
389 static void parse_args(int argc, char **argv)
390 {
391 int i, freq = 0;
392
393 for(i = 1; i < argc; i++) {
394 if(!freq) {
395 int n = (int)str2l(argv[i]);
396 if(n > 0) {
397 freq = n;
398 continue;
399 }
400 }
401
402 if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) {
403 printf("apps.plugin %s\n", NETDATA_VERSION);
404 exit(0);
405 }
406
407 if(strcmp("print", argv[i]) == 0 || strcmp("-print", argv[i]) == 0 || strcmp("--print", argv[i]) == 0) {
408 print_tree_and_exit = true;
409 continue;
410 }
411
412 #if defined(OS_LINUX)
413 if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) {
414 if(!check_proc_1_io()) {
415 perror("Tried to read /proc/1/io and it failed");
416 exit(1);
417 }
418 printf("OK\n");
419 exit(0);
420 }
421 #endif
422
423 if(strcmp("debug", argv[i]) == 0) {
424 debug_enabled = true;
425 #ifndef NETDATA_INTERNAL_CHECKS
426 fprintf(stderr, "apps.plugin has been compiled without debugging\n");
427 #endif
428 continue;
429 }
430
431 if(strcmp("profile-speed", argv[i]) == 0) {
432 profile_speed = true;
433 continue;
434 }
435
436 #if defined(OS_LINUX)
437 if(strcmp("fds-cache-secs", argv[i]) == 0) {
438 if(argc <= i + 1) {
439 fprintf(stderr, "Parameter 'fds-cache-secs' requires a number as argument.\n");
440 exit(1);
441 }
442 i++;
443 max_fds_cache_seconds = str2i(argv[i]);
444 if(max_fds_cache_seconds < 0) max_fds_cache_seconds = 0;
445 continue;
446 }
447
448 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
449 if(strcmp("--pss", argv[i]) == 0) {
450 if(argc <= i + 1) {
451 fprintf(stderr, "Parameter '--pss' requires a duration (e.g. 5m, 300s) or 'off'.\n");
452 exit(1);
453 }
454 i++;
455 int64_t seconds = 0;
456 if(!duration_parse(argv[i], &seconds, "s", "s")) {
457 fprintf(stderr, "Cannot parse '--pss' value '%s'.\n", argv[i]);
458 exit(1);
459 }
460 if(seconds <= 0) {
461 pss_refresh_period = 0; // disabled
462 }
463 else {
464 pss_refresh_period = (int)seconds;
465 if(pss_refresh_period < 1)
466 pss_refresh_period = 1;
467 }
468 continue;
469 }
470 #endif
471 #endif
472
473 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
474 if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
475 include_exited_childs = 0;
476 continue;
477 }
478
479 if(strcmp("with-childs", argv[i]) == 0) {
480 include_exited_childs = 1;
481 continue;
482 }
483 #endif
484
485 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
486 if(strcmp("with-guest", argv[i]) == 0) {
487 enable_guest_charts = true;
488 continue;
489 }
490
491 if(strcmp("no-guest", argv[i]) == 0 || strcmp("without-guest", argv[i]) == 0) {
492 enable_guest_charts = false;
493 continue;
494 }
495 #endif
496
497 #if (PROCESSES_HAVE_FDS == 1)
498 if(strcmp("with-files", argv[i]) == 0) {
499 enable_file_charts = CONFIG_BOOLEAN_YES;
500 continue;
501 }
502
503 if(strcmp("no-files", argv[i]) == 0 || strcmp("without-files", argv[i]) == 0) {
504 enable_file_charts = CONFIG_BOOLEAN_NO;
505 continue;
506 }
507 #endif
508
509 #if (PROCESSES_HAVE_UID == 1) || (PROCESSES_HAVE_SID == 1)
510 if(strcmp("no-users", argv[i]) == 0 || strcmp("without-users", argv[i]) == 0) {
511 enable_users_charts = 0;
512 continue;
513 }
514 #endif
515
516 #if (PROCESSES_HAVE_GID == 1)
517 if(strcmp("no-groups", argv[i]) == 0 || strcmp("without-groups", argv[i]) == 0) {
518 enable_groups_charts = 0;
519 continue;
520 }
521 #endif
522
523 #if (PROCESSES_HAVE_SERVICE == 1)
524 if(strcmp("no-services", argv[i]) == 0 || strcmp("without-services", argv[i]) == 0) {
525 enable_services_charts = false;
526 continue;
527 }
528 #endif
529
530 if(strcmp("with-detailed-uptime", argv[i]) == 0) {
531 enable_detailed_uptime_charts = 1;
532 continue;
533 }
534 if(strcmp("with-function-cmdline", argv[i]) == 0) {
535 enable_function_cmdline = true;
536 continue;
537 }
538
539 if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
540 fprintf(stderr,
541 "\n"
542 " netdata apps.plugin %s\n"
543 " Copyright 2018-2025 Netdata Inc.\n"
544 " Released under GNU General Public License v3 or later.\n"
545 " All rights reserved.\n"
546 "\n"
547 " This program is a data collector plugin for netdata.\n"
548 "\n"
549 " Available command line options:\n"
550 "\n"
551 " SECONDS set the data collection frequency\n"
552 "\n"
553 " debug enable debugging (lot of output)\n"
554 "\n"
555 " with-function-cmdline enable reporting the complete command line for processes\n"
556 " it includes the command and passed arguments\n"
557 " it may include sensitive data such as passwords and tokens\n"
558 " enabling this could be a security risk\n"
559 "\n"
560 #if (PROCESSES_HAVE_CPU_CHILDREN_TIME == 1) || (PROCESSES_HAVE_CHILDREN_FLTS == 1)
561 " with-childs\n"
562 " without-childs enable / disable aggregating exited\n"
563 " children resources into parents\n"
564 " (default is enabled)\n"
565 "\n"
566 #endif
567 #if (PROCESSES_HAVE_CPU_GUEST_TIME == 1)
568 " with-guest\n"
569 " without-guest enable / disable reporting guest charts\n"
570 " (default is disabled)\n"
571 "\n"
572 #endif
573 #if (PROCESSES_HAVE_FDS == 1)
574 " with-files\n"
575 " without-files enable / disable reporting files, sockets, pipes\n"
576 " (default is enabled)\n"
577 "\n"
578 #endif
579 #if (PROCESSES_HAVE_UID == 1) || (PROCESSES_HAVE_SID == 1)
580 " without-users disable reporting per user charts\n"
581 "\n"
582 #endif
583 #if (PROCESSES_HAVE_GID == 1)
584 " without-groups disable reporting per user group charts\n"
585 "\n"
586 #endif
587 #if (PROCESSES_HAVE_SERVICE == 1)
588 " without-services disable reporting per Windows service charts\n"
589 "\n"
590 #endif
591 " with-detailed-uptime enable reporting min/avg/max uptime charts\n"
592 "\n"
593 #if defined(OS_LINUX)
594 " fds-cache-secs N cache the files of processed for N seconds\n"
595 " caching is adaptive per file (when a file\n"
596 " is found, it starts at 0 and while the file\n"
597 " remains open, it is incremented up to the\n"
598 " max given)\n"
599 " (default is %d seconds)\n"
600 "\n"
601 #if (PROCESSES_HAVE_SMAPS_ROLLUP == 1)
602 " --pss TIME enable estimated memory using PSS sampling at the given interval\n"
603 " (e.g. 5m, 300s). Use 'off' or '0' to disable.\n"
604 " (default is off)\n"
605 "\n"
606 #endif
607 #endif
608 " version or -v or -V print program version and exit\n"
609 "\n"
610 , NETDATA_VERSION
611 #if defined(OS_LINUX)
612 , max_fds_cache_seconds
613 #endif
614 );
615 exit(0);
616 }
617
618 #if !defined(OS_WINDOWS) || !defined(RUN_UNDER_CLION)
619 netdata_log_error("Cannot understand option %s", argv[i]);
620 exit(1);
621 #endif
622 }
623
624 if(freq > 0) update_every = freq;
625
626 if(read_apps_groups_conf(user_config_dir, "groups")) {
627 netdata_log_info("Cannot read process groups configuration file '%s/apps_groups.conf'. Will try '%s/apps_groups.conf'", user_config_dir, stock_config_dir);
628
629 if(read_apps_groups_conf(stock_config_dir, "groups")) {
630 netdata_log_error("Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.", stock_config_dir);
631 exit(1);
632 }
633 else
634 netdata_log_info("Loaded config file '%s/apps_groups.conf'", stock_config_dir);
635 }
636 else
637 netdata_log_info("Loaded config file '%s/apps_groups.conf'", user_config_dir);
638 }
639
640 #if !defined(OS_WINDOWS)
641 static inline int am_i_running_as_root() {
642 uid_t uid = getuid(), euid = geteuid();
643
644 if(uid == 0 || euid == 0) {
645 if(debug_enabled) netdata_log_info("I am running with escalated privileges, uid = %u, euid = %u.", uid, euid);
646 return 1;
647 }
648
649 if(debug_enabled) netdata_log_info("I am not running with escalated privileges, uid = %u, euid = %u.", uid, euid);
650 return 0;
651 }
652
653 #ifdef HAVE_CAPABILITY
654 static inline int check_capabilities() {
655 cap_t caps = cap_get_proc();
656 if(!caps) {
657 netdata_log_error("Cannot get current capabilities.");
658 return 0;
659 }
660 else if(debug_enabled)
661 netdata_log_info("Received my capabilities from the system.");
662
663 int ret = 1;
664
665 cap_flag_value_t cfv = CAP_CLEAR;
666 if(cap_get_flag(caps, CAP_DAC_READ_SEARCH, CAP_EFFECTIVE, &cfv) == -1) {
667 netdata_log_error("Cannot find if CAP_DAC_READ_SEARCH is effective.");
668 ret = 0;
669 }
670 else {
671 if(cfv != CAP_SET) {
672 netdata_log_error("apps.plugin should run with CAP_DAC_READ_SEARCH.");
673 ret = 0;
674 }
675 else if(debug_enabled)
676 netdata_log_info("apps.plugin runs with CAP_DAC_READ_SEARCH.");
677 }
678
679 cfv = CAP_CLEAR;
680 if(cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &cfv) == -1) {
681 netdata_log_error("Cannot find if CAP_SYS_PTRACE is effective.");
682 ret = 0;
683 }
684 else {
685 if(cfv != CAP_SET) {
686 netdata_log_error("apps.plugin should run with CAP_SYS_PTRACE.");
687 ret = 0;
688 }
689 else if(debug_enabled)
690 netdata_log_info("apps.plugin runs with CAP_SYS_PTRACE.");
691 }
692
693 cap_free(caps);
694
695 return ret;
696 }
697 #else
698 static inline int check_capabilities() {
699 return 0;
700 }
701 #endif
702 #endif
703
704 netdata_mutex_t apps_and_stdout_mutex;
705
706 static void __attribute__((constructor)) init_mutex(void) {
707 netdata_mutex_init(&apps_and_stdout_mutex);
708 }
709
710 static void __attribute__((destructor)) destroy_mutex(void) {
711 netdata_mutex_destroy(&apps_and_stdout_mutex);
712 }
713
714 static bool apps_plugin_exit = false;
715
716 int main(int argc, char **argv) {
717 nd_log_initialize_for_external_plugins("apps.plugin");
718 netdata_threads_init_for_external_plugins(0);
719
720 pagesize = (size_t)sysconf(_SC_PAGESIZE);
721
722 bool send_resource_usage = true;
723 {
724 const char *s = getenv("NETDATA_INTERNALS_MONITORING");
725 if(s && *s && strcmp(s, "NO") == 0)
726 send_resource_usage = false;
727 }
728
729 // since apps.plugin runs as root, prevent it from opening symbolic links
730 procfile_open_flags = O_RDONLY|O_NOFOLLOW;
731
732 netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
733 if(verify_netdata_host_prefix(true) == -1) exit(1);
734
735 user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
736 if(user_config_dir == NULL) {
737 // netdata_log_info("NETDATA_CONFIG_DIR is not passed from netdata");
738 user_config_dir = CONFIG_DIR;
739 }
740 // else netdata_log_info("Found NETDATA_USER_CONFIG_DIR='%s'", user_config_dir);
741
742 stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
743 if(stock_config_dir == NULL) {
744 // netdata_log_info("NETDATA_CONFIG_DIR is not passed from netdata");
745 stock_config_dir = LIBCONFIG_DIR;
746 }
747 // else netdata_log_info("Found NETDATA_USER_CONFIG_DIR='%s'", user_config_dir);
748
749 #ifdef NETDATA_INTERNAL_CHECKS
750 if(debug_flags != 0) {
751 struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
752 if(setrlimit(RLIMIT_CORE, &rl) != 0)
753 netdata_log_info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
754 #ifdef HAVE_SYS_PRCTL_H
755 prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
756 #endif
757 }
758 #endif /* NETDATA_INTERNAL_CHECKS */
759
760 procfile_set_adaptive_allocation(true, 0, 0, 0);
761 os_get_system_cpus_uncached();
762 apps_managers_and_aggregators_init(); // before parsing args!
763 parse_args(argc, argv);
764
765 #if !defined(OS_WINDOWS)
766 if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) {
767 uid_t uid = getuid(), euid = geteuid();
768 #ifdef HAVE_CAPABILITY
769 netdata_log_error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
770 "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
771 "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
772 "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
773 , uid, euid, argv[0], argv[0], argv[0]);
774 #else
775 netdata_log_error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
776 "Without these, apps.plugin cannot report disk I/O utilization of other processes. "
777 "Your system does not support capabilities. "
778 "To enable setuid to root run: sudo chown root:netdata %s; sudo chmod 4750 %s; "
779 , uid, euid, argv[0], argv[0]);
780 #endif
781 }
782 #endif
783
784 netdata_log_info("started on pid %d", getpid());
785
786 #if (PROCESSES_HAVE_UID == 1)
787 cached_usernames_init();
788 #endif
789
790 #if (PROCESSES_HAVE_GID == 1)
791 cached_groupnames_init();
792 #endif
793
794 #if (PROCESSES_HAVE_SID == 1)
795 cached_sid_username_init();
796 #endif
797
798 apps_pids_init();
799 OS_FUNCTION(apps_os_init)();
800 int exit_status = 0;
801
802 // ------------------------------------------------------------------------
803 // the event loop for functions
804
805 struct functions_evloop_globals *wg =
806 functions_evloop_init(1, "APPS", &apps_and_stdout_mutex, &apps_plugin_exit, &exit_status);
807
808 functions_evloop_add_function(wg, "processes", function_processes, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, NULL);
809
810 // ------------------------------------------------------------------------
811
812 netdata_mutex_lock(&apps_and_stdout_mutex);
813 APPS_PLUGIN_GLOBAL_FUNCTIONS();
814
815 global_iterations_counter = 1;
816 heartbeat_t hb;
817 heartbeat_init(&hb, update_every * USEC_PER_SEC);
818 for (; !__atomic_load_n(&apps_plugin_exit, __ATOMIC_ACQUIRE); global_iterations_counter++) {
819 netdata_mutex_unlock(&apps_and_stdout_mutex);
820
821 usec_t dt;
822 if(profile_speed) {
823 static int profiling_count=0;
824 profiling_count++;
825 if(unlikely(profiling_count > 500)) exit(0);
826 dt = update_every * USEC_PER_SEC;
827 }
828 else
829 dt = heartbeat_next(&hb);
830
831 netdata_mutex_lock(&apps_and_stdout_mutex);
832
833 struct pollfd pollfd = { .fd = fileno(stdout), .events = POLLERR };
834 if (unlikely(poll(&pollfd, 1, 0) < 0)) {
835 netdata_mutex_unlock(&apps_and_stdout_mutex);
836 fatal("Cannot check if a pipe is available");
837 }
838 if (unlikely(pollfd.revents & POLLERR)) {
839 netdata_mutex_unlock(&apps_and_stdout_mutex);
840 fatal("Received error on read pipe.");
841 }
842
843 if(!collect_data_for_all_pids()) {
844 netdata_log_error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
845 printf("DISABLE\n");
846 netdata_mutex_unlock(&apps_and_stdout_mutex);
847 exit(1);
848 }
849
850 aggregate_processes_to_targets();
851
852 #if (ALL_PIDS_ARE_READ_INSTANTLY == 0)
853 OS_FUNCTION(apps_os_read_global_cpu_utilization)();
854 normalize_utilization(apps_groups_root_target);
855 #endif
856
857 if(unlikely(print_tree_and_exit)) {
858 print_hierarchy(root_of_pids());
859 netdata_mutex_unlock(&apps_and_stdout_mutex);
860 exit(0);
861 }
862
863 if(send_resource_usage)
864 send_resource_usage_to_netdata(dt);
865
866 #if (PROCESSES_HAVE_STATE == 1)
867 send_proc_states_count(dt);
868 #endif
869
870 send_charts_updates_to_netdata(apps_groups_root_target, "app", "app_group", "Applications Groups");
871 send_collected_data_to_netdata(apps_groups_root_target, "app", dt);
872
873 #if (PROCESSES_HAVE_UID == 1)
874 if (enable_users_charts) {
875 send_charts_updates_to_netdata(users_root_target, "user", "user", "User");
876 send_collected_data_to_netdata(users_root_target, "user", dt);
877 }
878 #endif
879
880 #if (PROCESSES_HAVE_GID == 1)
881 if (enable_groups_charts) {
882 send_charts_updates_to_netdata(groups_root_target, "usergroup", "user_group", "User Group");
883 send_collected_data_to_netdata(groups_root_target, "usergroup", dt);
884 }
885 #endif
886
887 #if (PROCESSES_HAVE_SID == 1)
888 if (enable_users_charts) {
889 send_charts_updates_to_netdata(sids_root_target, "user", "user", "User Processes");
890 send_collected_data_to_netdata(sids_root_target, "user", dt);
891 }
892 #endif
893
894 #if (PROCESSES_HAVE_SERVICE == 1)
895 if (enable_services_charts) {
896 send_charts_updates_to_netdata(services_root_target, "service", "service", "Windows Service");
897 send_collected_data_to_netdata(services_root_target, "service", dt);
898 }
899 #endif
900
901 fflush(stdout);
902
903 debug_log("done Loop No %zu", global_iterations_counter);
904 }
905 netdata_mutex_unlock(&apps_and_stdout_mutex);
906 exit(exit_status);
907 }