int keeploops; // increases by 1 every time keep is 1 and updated 0
char updated:1; // 1 when the process is currently running
char merged:1; // 1 when it has been merged to its parent
- char new_entry:1; // 1 when this is a new process, just saw for the first time
char read:1; // 1 when we have already read this process for this iteration
int sortlist; // higher numbers = top on the process tree
// struct pid_stat management
static inline struct pid_stat *get_pid_entry(pid_t pid) {
- if(unlikely(all_pids[pid])) {
- all_pids[pid]->new_entry = 0;
+ if(unlikely(all_pids[pid]))
return all_pids[pid];
- }
- all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
- all_pids[pid]->fds = callocz(sizeof(int), MAX_SPARE_FDS);
- all_pids[pid]->fds_size = MAX_SPARE_FDS;
+ struct pid_stat *p = callocz(sizeof(struct pid_stat), 1);
+ p->fds = callocz(sizeof(int), MAX_SPARE_FDS);
+ p->fds_size = MAX_SPARE_FDS;
if(likely(root_of_pids))
- root_of_pids->prev = all_pids[pid];
+ root_of_pids->prev = p;
- all_pids[pid]->next = root_of_pids;
- root_of_pids = all_pids[pid];
+ p->next = root_of_pids;
+ root_of_pids = p;
- all_pids[pid]->pid = pid;
- all_pids[pid]->new_entry = 1;
+ p->pid = pid;
+ all_pids[pid] = p;
all_pids_count++;
- return all_pids[pid];
+ return p;
}
static inline void del_pid_entry(pid_t pid) {
- if(unlikely(!all_pids[pid])) {
+ struct pid_stat *p = all_pids[pid];
+
+ if(unlikely(!p)) {
error("attempted to free pid %d that is not allocated.", pid);
return;
}
if(unlikely(debug))
- fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
+ fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, p->comm);
- if(root_of_pids == all_pids[pid])
- root_of_pids = all_pids[pid]->next;
+ if(root_of_pids == p)
+ root_of_pids = p->next;
- if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
- if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
+ if(p->next) p->next->prev = p->prev;
+ if(p->prev) p->prev->next = p->next;
- freez(all_pids[pid]->fds);
- freez(all_pids[pid]->fds_dirname);
- freez(all_pids[pid]->stat_filename);
- freez(all_pids[pid]->statm_filename);
- freez(all_pids[pid]->io_filename);
- freez(all_pids[pid]->cmdline_filename);
- freez(all_pids[pid]);
+ freez(p->fds);
+ freez(p->fds_dirname);
+ freez(p->stat_filename);
+ freez(p->statm_filename);
+ freez(p->io_filename);
+ freez(p->cmdline_filename);
+ freez(p);
all_pids[pid] = NULL;
all_pids_count--;
}
+// ----------------------------------------------------------------------------
+
+static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
+ if(unlikely(!status)) {
+ // error("command failed log %u, errno %d", log, errno);
+
+ if(unlikely(debug || errno != ENOENT)) {
+ if(unlikely(debug || !(p->log_thrown & log))) {
+ p->log_thrown |= log;
+ switch(log) {
+ case PID_LOG_IO:
+ error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_STATM:
+ error("Cannot process %s/proc/%d/statm (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_CMDLINE:
+ error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_FDS:
+ error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm);
+ break;
+
+ case PID_LOG_STAT:
+ break;
+
+ default:
+ error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
+ break;
+ }
+ }
+ }
+ errno = 0;
+ }
+ else if(unlikely(p->log_thrown & log)) {
+ // error("unsetting log %u on pid %d", log, p->pid);
+ p->log_thrown &= ~log;
+ }
+
+ return status;
+}
+
+static inline void assign_target_to_pid(struct pid_stat *p) {
+ uint32_t hash = simple_hash(p->comm);
+ size_t pclen = strlen(p->comm);
+
+ struct target *w;
+ for(w = apps_groups_root_target; w ; w = w->next) {
+ // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
+
+ // find it - 4 cases:
+ // 1. the target is not a pattern
+ // 2. the target has the prefix
+ // 3. the target has the suffix
+ // 4. the target is something inside cmdline
+
+ if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
+ || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
+ || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
+ || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
+ ))) {
+
+ if(w->target) p->target = w->target;
+ else p->target = w;
+
+ if(debug || (p->target && p->target->debug))
+ fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
+
+ break;
+ }
+ }
+}
+
// ----------------------------------------------------------------------------
// update pids from proc
if(unlikely(!p->cmdline_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", global_host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid);
p->cmdline_filename = strdupz(filename);
}
if(unlikely(!p->stat_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", global_host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid);
p->stat_filename = strdupz(filename);
}
p->stat_collected_usec = now_monotonic_usec();
file_counter++;
- // p->pid = str2pid_t(procfile_lineword(ff, 0, 0+i));
-
- if(unlikely(!p->comm[0]))
- strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
-
+ // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
+ char *comm = procfile_lineword(ff, 0, 1);
// p->state = *(procfile_lineword(ff, 0, 2));
p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
// p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
// p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
// p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
- kernel_uint_t last;
+ if(strcmp(p->comm, comm)) {
+ if(unlikely(debug)) {
+ if(p->comm[0])
+ fprintf(stderr, "apps.plugin: \tpid %d (%s) changed name to '%s'\n", p->pid, p->comm, comm);
+ else
+ fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", p->pid, comm);
+ }
+
+ strncpyz(p->comm, comm, MAX_COMPARE_NAME);
+
+ // /proc/<pid>/cmdline
+ if(likely(proc_pid_cmdline_is_needed))
+ managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
+
+ assign_target_to_pid(p);
+ }
- last = p->minflt_raw;
+ kernel_uint_t last = p->minflt_raw;
p->minflt_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 9));
p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
}
if(unlikely(debug || (p->target && p->target->debug)))
- fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d\n", global_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
+ fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d\n", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
if(unlikely(global_iterations_counter == 1)) {
p->minflt = 0;
if(unlikely(!p->statm_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", global_host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", netdata_configured_host_prefix, p->pid);
p->statm_filename = strdupz(filename);
}
if(unlikely(!p->io_filename)) {
char filename[FILENAME_MAX + 1];
- snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", global_host_prefix, p->pid);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid);
p->io_filename = strdupz(filename);
}
static usec_t collected_usec = 0, last_collected_usec = 0;
if(unlikely(!ff)) {
- snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix);
+ snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix);
ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
if(unlikely(!ff)) goto cleanup;
}
// not found
FD_FILETYPE type;
- if(name[0] == '/') type = FILETYPE_FILE;
- else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
- else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
- else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
- else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
- else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
- else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
- else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
- else if(strncmp(name, "anon_inode:", 11) == 0) {
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
+ if(likely(name[0] == '/')) type = FILETYPE_FILE;
+ else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
+ else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
+ else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
+ const char *t = &name[11];
+
+ if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
+ else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
+ else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
+ else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
+ else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
+ else {
+ if(unlikely(debug))
+ fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
- type = FILETYPE_OTHER;
+ type = FILETYPE_OTHER;
+ }
}
+ else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
else {
if(unlikely(debug))
fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
static inline int read_pid_file_descriptors(struct pid_stat *p) {
if(unlikely(!p->fds_dirname)) {
char dirname[FILENAME_MAX+1];
- snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid);
+ snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid);
p->fds_dirname = strdupz(dirname);
}
if(unlikely(p->fds[fdid] == 0)) {
// we don't know this fd, get it
- sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name);
+ sprintf(fdname, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name);
ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
if(unlikely(l == -1)) {
if(debug || (p->target && p->target->debug)) {
return 1;
}
-static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
- if(unlikely(!status)) {
- // error("command failed log %u, errno %d", log, errno);
-
- if(unlikely(debug || errno != ENOENT)) {
- if(unlikely(debug || !(p->log_thrown & log))) {
- p->log_thrown |= log;
- switch(log) {
- case PID_LOG_IO:
- error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm);
- break;
-
- case PID_LOG_STATM:
- error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm);
- break;
-
- case PID_LOG_CMDLINE:
- error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm);
- break;
-
- case PID_LOG_FDS:
- error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm);
- break;
-
- case PID_LOG_STAT:
- break;
-
- default:
- error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
- break;
- }
- }
- }
- errno = 0;
- }
- else if(unlikely(p->log_thrown & log)) {
- // error("unsetting log %u on pid %d", log, p->pid);
- p->log_thrown &= ~log;
- }
-
- return status;
-}
-
-static inline void assign_target_to_pid(struct pid_stat *p) {
- uint32_t hash = simple_hash(p->comm);
- size_t pclen = strlen(p->comm);
-
- struct target *w;
- for(w = apps_groups_root_target; w ; w = w->next) {
- // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
-
- // find it - 4 cases:
- // 1. the target is not a pattern
- // 2. the target has the prefix
- // 3. the target has the suffix
- // 4. the target is something inside cmdline
-
- if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
- || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
- || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
- || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
- ))) {
-
- if(w->target) p->target = w->target;
- else p->target = w;
-
- if(debug || (p->target && p->target->debug))
- fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
-
- break;
- }
- }
-}
-
static inline int collect_data_for_pid(pid_t pid) {
if(unlikely(pid <= 0 || pid > pid_max)) {
error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
// there is no reason to proceed if we cannot get its memory status
return 0;
- // --------------------------------------------------------------------
- // link it
-
- // check if it is target
- // we do this only once, the first time this pid is loaded
- if(unlikely(p->new_entry)) {
- // /proc/<pid>/cmdline
- if(likely(proc_pid_cmdline_is_needed))
- managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
-
- if(unlikely(debug))
- fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
-
- assign_target_to_pid(p);
- }
-
// --------------------------------------------------------------------
// /proc/<pid>/fd
for(p = root_of_pids; p ; p = p->next) {
p->read = 0; // mark it as not read, so that collect_data_for_pid() will read it
p->updated = 0;
- p->new_entry = 0;
p->merged = 0;
p->children_count = 0;
p->parent = NULL;
char dirname[FILENAME_MAX + 1];
- snprintfz(dirname, FILENAME_MAX, "%s/proc", global_host_prefix);
+ snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
DIR *dir = opendir(dirname);
if(!dir) return 0;
"\n"
" netdata apps.plugin %s\n"
" Copyright (C) 2016-2017 Costa Tsaousis <costa@tsaousis.gr>\n"
- " Released under GNU Public License v3 or later.\n"
+ " Released under GNU General Public License v3 or later.\n"
" All rights reserved.\n"
"\n"
" This program is a data collector plugin for netdata.\n"
#ifdef HAVE_CAPABILITY
static int check_capabilities() {
- if(!CAP_IS_SUPPORTED(CAP_DAC_READ_SEARCH)) {
- error("This system does not support CAP_DAC_READ_SEARCH capability. Please setuid to root apps.plugin.");
- return 0;
- }
- else if(debug)
- info("System has CAP_DAC_READ_SEARCH capability.");
-
- if(!CAP_IS_SUPPORTED(CAP_SYS_PTRACE)) {
- error("This system does not support CAP_SYS_PTRACE capability. Please setuid to root apps.plugin.");
- return 0;
- }
- else if(debug)
- info("System has CAP_SYS_PTRACE capability.");
-
cap_t caps = cap_get_proc();
if(!caps) {
error("Cannot get current capabilities.");
error_log_errors_per_period = 100;
error_log_throttle_period = 3600;
- global_host_prefix = getenv("NETDATA_HOST_PREFIX");
- if(global_host_prefix == NULL) {
+ netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
+ if(netdata_configured_host_prefix == NULL) {
// info("NETDATA_HOST_PREFIX is not passed from netdata");
- global_host_prefix = "";
+ netdata_configured_host_prefix = "";
}
- // else info("Found NETDATA_HOST_PREFIX='%s'", global_host_prefix);
+ // else info("Found NETDATA_HOST_PREFIX='%s'", netdata_configured_host_prefix);
config_dir = getenv("NETDATA_CONFIG_DIR");
if(config_dir == NULL) {
struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
if(setrlimit(RLIMIT_CORE, &rl) != 0)
info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+#ifdef HAVE_SYS_PRCTL_H
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+#endif
}
#endif /* NETDATA_INTERNAL_CHECKS */
parse_args(argc, argv);
- if(!am_i_running_as_root())
- if(!check_capabilities())
+ if(!check_capabilities()) {
+ if(!am_i_running_as_root()) {
+ uid_t uid = getuid(), euid = geteuid();
#ifdef HAVE_CAPABILITY
- error("apps.plugin should either run as root or have special capabilities. "
+ error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
"Without these, apps.plugin cannot report disk I/O utilization of other processes. "
- "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %1$s; "
- "To enable setuid to root run: sudo chown root %1$s; sudo chmod 4755 %1$s; "
- , argv[0]
+ "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; "
+ "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+ , uid, euid, argv[0], argv[0], argv[0]
);
#else
- error("apps.plugin should either run as root or have special capabilities. "
+ error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. "
"Without these, apps.plugin cannot report disk I/O utilization of other processes. "
"Your system does not support capabilities. "
- "To enable setuid to root run: sudo chown root %1$s; sudo chmod 4755 %1$s; "
- , argv[0]
+ "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; "
+ , uid, euid, argv[0], argv[0]
);
#endif
+ }
+ }
all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max);