summaryrefslogtreecommitdiffstats
path: root/collectors/apps.plugin
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2022-10-05 14:13:46 +0300
committerGitHub <noreply@github.com>2022-10-05 14:13:46 +0300
commit8fc3b351a2e7fc96eced8f924de2e9cec9842128 (patch)
treebde41c66573ccaf8876c280e00742cc6096b587c /collectors/apps.plugin
parent6850878e697d66dc90b9af1e750b22238c63c292 (diff)
Allow netdata plugins to expose functions for querying more information about specific charts (#13720)
* function renames and code cleanup in popen.c; no actual code changes * netdata popen() now opens both child process stdin and stdout and returns FILE * for both * pass both input and output to parser structures * updated rrdset to call custom functions * RRDSET FUNCTION leading calls for both sync and async operation * put RRDSET functions to a separate file * added format and timeout at function definition * support for synchronous (internal plugins) and asynchronous (external plugins and children) functions * /api/v1/function endpoint * functions are now attached to the host and there is a dictionary view per chart * functions implemented at plugins.d * remove the defer until keyword hook from plugins.d when it is done * stream sender implementation of functions * sanitization of all functions so that certain characters are only allowed * strictier sanitization * common max size * 1st working plugins.d example * always init inflight dictionary * properly destroy dictionaries to avoid parallel insertion of items * add more debugging on disconnection reasons * add more debugging on disconnection reasons again * streaming receiver respects newlines * dont use the same fp for both streaming receive and send * dont free dbengine memory with internal checks * make sender proceed in the buffer * added timing info and garbage collection at plugins.d * added info about routing nodes * added info about routing nodes with delay * added more info about delays * added more info about delays again * signal sending thread to wake up * streaming version labeling and commented code to support capabilities * added functions to /api/v1/data, /api/v1/charts, /api/v1/chart, /api/v1/info * redirect top output to stdout * address coverity findings * fix resource leaks of popen * log attempts to connect to individual destinations * better messages * properly parse destinations * try to find a function from the most matching to the least matching * log added streaming destinations * rotate destinations bypassing a node in the middle that does not accept our connection * break the loops properly * use typedef to define callbacks * capabilities negotiation during streaming * functions exposed upstream based on capabilities; compression disabled per node persisting reconnects; always try to connect with all capabilities * restore functionality to lookup functions * better logging of capabilities * remove old versions from capabilities when a newer version is there * fix formatting * optimization for plugins.d rrdlabels to avoid creating and destructing dictionaries all the time * delayed health initialization for rrddim and rrdset * cleanup health initialization * fix for popen() not returning the right value * add health worker jobs for initializing rrdset and rrddim * added content type support for functions; apps.plugin permanent function to display all the processes * fixes for functions parameters parsing in apps.plugin * fix for process matching in apps.plugiin * first working function for apps.plugin * Dashboard ACL is disabled for functions; Function errors are all in JSON format * apps.plugin function processes returns json table * use json_escape_string() to escape message * fix formatting * apps.plugin exposes all its metrics to function processes * fix json formatting when filtering out some rows * reopen the internal pipe of rrdpush in case of errors * misplaced statement * do not use buffer->len * support for GLOBAL functions (functions that are not linked to a chart * added /api/v1/functions endpoint; removed format from the FUNCTIONS api; * swagger documentation about the new api end points * added plugins.d documentation about functions * never re-close a file * remove uncessesary ifdef * fixed issues identified by codacy * fix for null label value * make edit-config copy-and-paste friendly * Revert "make edit-config copy-and-paste friendly" This reverts commit 54500c0e0a97f65a0c66c4d34e966f6a9056698e. * reworked sender handshake to fix coverity findings * timeout is zero, for both send_timeout() and recv_timeout() * properly detect that parent closed the socket * support caching of function responses; limit function response to 10MB; added protection from malformed function responses * disabled excessive logging * added units to apps.plugin function processes and normalized all values to be human readable * shorter field names * fixed issues reported * fixed apps.plugin error response; tested that pluginsd can properly handle faulty responses * use double linked list macros for double linked list management * faster apps.plugin function printing by minimizing file operations * added memory percentage * fix compatibility issues with older compilers and FreeBSD * rrdpush sender code cleanup; rrhost structure cleanup from sender flags and variables; * fix letftover variable in ifdef * apps.plugin: do not call detach from the thread; exit immediately when input is broken * exclude AR charts from health * flush cleaner; prefer sender output * clarity * do not fill the cbuffer if not connected * fix * dont enabled host->sender if streaming is not enabled; send host label updates to parent; * functions are only available through ACLK * Prepared statement reports only in dev mode * fix AR chart detection * fix for streaming not being enabling itself * more cleanup of sender and receiver structures * moved read-only flags and configuration options to rrdhost->options * fixed merge with master * fix for incomplete rename * prevent service thread from working on charts that are being collected Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>
Diffstat (limited to 'collectors/apps.plugin')
-rw-r--r--collectors/apps.plugin/apps_plugin.c831
1 files changed, 737 insertions, 94 deletions
diff --git a/collectors/apps.plugin/apps_plugin.c b/collectors/apps.plugin/apps_plugin.c
index 8521e078e8..212374e828 100644
--- a/collectors/apps.plugin/apps_plugin.c
+++ b/collectors/apps.plugin/apps_plugin.c
@@ -10,6 +10,11 @@
#include "libnetdata/libnetdata.h"
#include "libnetdata/required_dummies.h"
+#define APPS_PLUGIN_FUNCTIONS() do { \
+ fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " \"processes\" 10 \"Detailed information on the currently running processes on this node\"\n"); \
+ } while(0)
+
+
// ----------------------------------------------------------------------------
// debugging
@@ -191,6 +196,18 @@ struct pid_on_target {
struct pid_on_target *next;
};
+struct openfds {
+ kernel_uint_t files;
+ kernel_uint_t pipes;
+ kernel_uint_t sockets;
+ kernel_uint_t inotifies;
+ kernel_uint_t eventfds;
+ kernel_uint_t timerfds;
+ kernel_uint_t signalfds;
+ kernel_uint_t eventpolls;
+ kernel_uint_t other;
+};
+
// ----------------------------------------------------------------------------
// target
//
@@ -235,24 +252,16 @@ struct target {
kernel_uint_t io_logical_bytes_read;
kernel_uint_t io_logical_bytes_written;
- // kernel_uint_t io_read_calls;
- // kernel_uint_t io_write_calls;
+ kernel_uint_t io_read_calls;
+ kernel_uint_t io_write_calls;
kernel_uint_t io_storage_bytes_read;
kernel_uint_t io_storage_bytes_written;
- // kernel_uint_t io_cancelled_write_bytes;
+ kernel_uint_t io_cancelled_write_bytes;
int *target_fds;
int target_fds_size;
- kernel_uint_t openfiles;
- kernel_uint_t openpipes;
- kernel_uint_t opensockets;
- kernel_uint_t openinotifies;
- kernel_uint_t openeventfds;
- kernel_uint_t opentimerfds;
- kernel_uint_t opensignalfds;
- kernel_uint_t openeventpolls;
- kernel_uint_t openother;
+ struct openfds openfds;
kernel_uint_t starttime;
kernel_uint_t collected_starttime;
@@ -382,22 +391,24 @@ struct pid_stat {
kernel_uint_t io_logical_bytes_read_raw;
kernel_uint_t io_logical_bytes_written_raw;
- // kernel_uint_t io_read_calls_raw;
- // kernel_uint_t io_write_calls_raw;
+ kernel_uint_t io_read_calls_raw;
+ kernel_uint_t io_write_calls_raw;
kernel_uint_t io_storage_bytes_read_raw;
kernel_uint_t io_storage_bytes_written_raw;
- // kernel_uint_t io_cancelled_write_bytes_raw;
+ kernel_uint_t io_cancelled_write_bytes_raw;
kernel_uint_t io_logical_bytes_read;
kernel_uint_t io_logical_bytes_written;
- // kernel_uint_t io_read_calls;
- // kernel_uint_t io_write_calls;
+ kernel_uint_t io_read_calls;
+ kernel_uint_t io_write_calls;
kernel_uint_t io_storage_bytes_read;
kernel_uint_t io_storage_bytes_written;
- // kernel_uint_t io_cancelled_write_bytes;
+ kernel_uint_t io_cancelled_write_bytes;
struct pid_fd *fds; // array of fds it uses
- size_t fds_size; // the size of the fds array
+ size_t fds_size; // the size of the fds array
+
+ struct openfds openfds;
int children_count; // number of processes directly referencing this
unsigned char keep:1; // 1 when we need to keep this process in memory even after it exited
@@ -447,8 +458,8 @@ kernel_uint_t global_uptime;
static struct pid_stat
*root_of_pids = NULL, // global list of all processes running
- **all_pids = NULL; // to avoid allocations, we pre-allocate the
- // the entire pid space.
+ **all_pids = NULL; // to avoid allocations, we pre-allocate
+ // a pointer for each pid in the entire pid space.
static size_t
all_pids_count = 0; // the number of processes running
@@ -961,15 +972,10 @@ static inline struct pid_stat *get_pid_entry(pid_t pid) {
p->fds = mallocz(sizeof(struct pid_fd) * MAX_SPARE_FDS);
p->fds_size = MAX_SPARE_FDS;
init_pid_fds(p, 0, p->fds_size);
-
- if(likely(root_of_pids))
- root_of_pids->prev = p;
-
- p->next = root_of_pids;
- root_of_pids = p;
-
p->pid = pid;
+ DOUBLE_LINKED_LIST_APPEND_UNSAFE(root_of_pids, p, prev, next);
+
all_pids[pid] = p;
all_pids_count++;
@@ -986,11 +992,7 @@ static inline void del_pid_entry(pid_t pid) {
debug_log("process %d %s exited, deleting it.", pid, p->comm);
- if(root_of_pids == p)
- root_of_pids = p->next;
-
- if(p->next) p->next->prev = p->prev;
- if(p->prev) p->prev->next = p->next;
+ DOUBLE_LINKED_LIST_REMOVE_UNSAFE(root_of_pids, p, prev, next);
// free the filename
#ifndef __FreeBSD__
@@ -1554,21 +1556,21 @@ static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) {
#else
pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1)));
pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1)));
- // pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
- // pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
+ pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1)));
+ pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1)));
pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1)));
pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1)));
- // pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1)));
+ pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1)));
#endif
if(unlikely(global_iterations_counter == 1)) {
p->io_logical_bytes_read = 0;
p->io_logical_bytes_written = 0;
- // p->io_read_calls = 0;
- // p->io_write_calls = 0;
+ p->io_read_calls = 0;
+ p->io_write_calls = 0;
p->io_storage_bytes_read = 0;
p->io_storage_bytes_written = 0;
- // p->io_cancelled_write_bytes = 0;
+ p->io_cancelled_write_bytes = 0;
}
return 1;
@@ -1577,11 +1579,11 @@ static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) {
cleanup:
p->io_logical_bytes_read = 0;
p->io_logical_bytes_written = 0;
- // p->io_read_calls = 0;
- // p->io_write_calls = 0;
+ p->io_read_calls = 0;
+ p->io_write_calls = 0;
p->io_storage_bytes_read = 0;
p->io_storage_bytes_written = 0;
- // p->io_cancelled_write_bytes = 0;
+ p->io_cancelled_write_bytes = 0;
return 0;
#endif
}
@@ -1888,7 +1890,7 @@ static inline int file_descriptor_find_or_add(const char *name, uint32_t hash) {
else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
const char *t = &name[11];
- if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
+ 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;
@@ -1943,7 +1945,6 @@ static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
struct pid_fd *pfd = &p->fds[first], *pfdend = &p->fds[first + size];
- size_t i = first;
while(pfd < pfdend) {
#ifndef __FreeBSD__
@@ -1951,7 +1952,6 @@ static inline void init_pid_fds(struct pid_stat *p, size_t first, size_t size) {
#endif
clear_pid_fd(pfd);
pfd++;
- i++;
}
}
@@ -2904,24 +2904,24 @@ static size_t zero_all_targets(struct target *root) {
w->io_logical_bytes_read = 0;
w->io_logical_bytes_written = 0;
- // w->io_read_calls = 0;
- // w->io_write_calls = 0;
+ w->io_read_calls = 0;
+ w->io_write_calls = 0;
w->io_storage_bytes_read = 0;
w->io_storage_bytes_written = 0;
- // w->io_cancelled_write_bytes = 0;
+ w->io_cancelled_write_bytes = 0;
// zero file counters
if(w->target_fds) {
memset(w->target_fds, 0, sizeof(int) * w->target_fds_size);
- w->openfiles = 0;
- w->openpipes = 0;
- w->opensockets = 0;
- w->openinotifies = 0;
- w->openeventfds = 0;
- w->opentimerfds = 0;
- w->opensignalfds = 0;
- w->openeventpolls = 0;
- w->openother = 0;
+ w->openfds.files = 0;
+ w->openfds.pipes = 0;
+ w->openfds.sockets = 0;
+ w->openfds.inotifies = 0;
+ w->openfds.eventfds = 0;
+ w->openfds.timerfds = 0;
+ w->openfds.signalfds = 0;
+ w->openfds.eventpolls = 0;
+ w->openfds.other = 0;
}
w->collected_starttime = 0;
@@ -2956,60 +2956,64 @@ static inline void reallocate_target_fds(struct target *w) {
}
}
-static inline void aggregate_fd_on_target(int fd, struct target *w) {
- if(unlikely(!w))
- return;
-
- if(unlikely(w->target_fds[fd])) {
- // it is already aggregated
- // just increase its usage counter
- w->target_fds[fd]++;
- return;
- }
-
- // increase its usage counter
- // so that we will not add it again
- w->target_fds[fd]++;
-
- switch(all_files[fd].type) {
+static void aggregage_fd_type_on_openfds(FD_FILETYPE type, struct openfds *openfds) {
+ switch(type) {
case FILETYPE_FILE:
- w->openfiles++;
+ openfds->files++;
break;
case FILETYPE_PIPE:
- w->openpipes++;
+ openfds->pipes++;
break;
case FILETYPE_SOCKET:
- w->opensockets++;
+ openfds->sockets++;
break;
case FILETYPE_INOTIFY:
- w->openinotifies++;
+ openfds->inotifies++;
break;
case FILETYPE_EVENTFD:
- w->openeventfds++;
+ openfds->eventfds++;
break;
case FILETYPE_TIMERFD:
- w->opentimerfds++;
+ openfds->timerfds++;
break;
case FILETYPE_SIGNALFD:
- w->opensignalfds++;
+ openfds->signalfds++;
break;
case FILETYPE_EVENTPOLL:
- w->openeventpolls++;
+ openfds->eventpolls++;
break;
case FILETYPE_OTHER:
- w->openother++;
+ openfds->other++;
break;
}
}
+static inline void aggregate_fd_on_target(int fd, struct target *w) {
+ if(unlikely(!w))
+ return;
+
+ if(unlikely(w->target_fds[fd])) {
+ // it is already aggregated
+ // just increase its usage counter
+ w->target_fds[fd]++;
+ return;
+ }
+
+ // increase its usage counter
+ // so that we will not add it again
+ w->target_fds[fd]++;
+
+ aggregage_fd_type_on_openfds(all_files[fd].type, &w->openfds);
+}
+
static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) {
if(unlikely(!p->updated)) {
@@ -3023,6 +3027,16 @@ static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) {
reallocate_target_fds(u);
reallocate_target_fds(g);
+ p->openfds.files = 0;
+ p->openfds.pipes = 0;
+ p->openfds.sockets = 0;
+ p->openfds.inotifies = 0;
+ p->openfds.eventfds = 0;
+ p->openfds.timerfds = 0;
+ p->openfds.signalfds = 0;
+ p->openfds.eventpolls = 0;
+ p->openfds.other = 0;
+
long currentfds = 0;
size_t c, size = p->fds_size;
struct pid_fd *fds = p->fds;
@@ -3033,6 +3047,7 @@ static inline void aggregate_pid_fds_on_targets(struct pid_stat *p) {
continue;
currentfds++;
+ aggregage_fd_type_on_openfds(all_files[fd].type, &p->openfds);
aggregate_fd_on_target(fd, w);
aggregate_fd_on_target(fd, u);
@@ -3079,11 +3094,11 @@ static inline void aggregate_pid_on_target(struct target *w, struct pid_stat *p,
w->io_logical_bytes_read += p->io_logical_bytes_read;
w->io_logical_bytes_written += p->io_logical_bytes_written;
- // w->io_read_calls += p->io_read_calls;
- // w->io_write_calls += p->io_write_calls;
+ w->io_read_calls += p->io_read_calls;
+ w->io_write_calls += p->io_write_calls;
w->io_storage_bytes_read += p->io_storage_bytes_read;
w->io_storage_bytes_written += p->io_storage_bytes_written;
- // w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+ w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
w->processes++;
w->num_threads += p->num_threads;
@@ -3638,7 +3653,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type
send_BEGIN(type, "files", dt);
for (w = root; w; w = w->next) {
if (unlikely(w->exposed && w->processes))
- send_SET(w->name, w->openfiles);
+ send_SET(w->name, w->openfds.files);
}
if (!strcmp("apps", type)){
kernel_uint_t usedfdpercentage = (kernel_uint_t) ((currentmaxfds * 100) / sysconf(_SC_OPEN_MAX));
@@ -3649,14 +3664,14 @@ static void send_collected_data_to_netdata(struct target *root, const char *type
send_BEGIN(type, "sockets", dt);
for (w = root; w; w = w->next) {
if (unlikely(w->exposed && w->processes))
- send_SET(w->name, w->opensockets);
+ send_SET(w->name, w->openfds.sockets);
}
send_END();
send_BEGIN(type, "pipes", dt);
for (w = root; w; w = w->next) {
if (unlikely(w->exposed && w->processes))
- send_SET(w->name, w->openpipes);
+ send_SET(w->name, w->openfds.pipes);
}
send_END();
}
@@ -3704,30 +3719,36 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, time_factor * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.mem '' '%s Real Memory (w/o shared)' 'MiB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L);
}
+ APPS_PLUGIN_FUNCTIONS();
+
fprintf(stdout, "CHART %s.vmem '' '%s Virtual Memory Size' 'MiB' mem %s.vmem stacked 20005 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20006 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20007 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
#ifndef __FreeBSD__
fprintf(stdout, "CHART %s.uptime '' '%s Carried Over Uptime' 'seconds' processes %s.uptime line 20008 %d\n", type, title, type, update_every);
@@ -3735,6 +3756,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
if (enable_detailed_uptime_charts) {
fprintf(stdout, "CHART %s.uptime_min '' '%s Minimum Uptime' 'seconds' processes %s.uptime_min line 20009 %d\n", type, title, type, update_every);
@@ -3742,18 +3764,21 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.uptime_avg '' '%s Average Uptime' 'seconds' processes %s.uptime_avg line 20010 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.uptime_max '' '%s Maximum Uptime' 'seconds' processes %s.uptime_max line 20011 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
}
#endif
@@ -3762,12 +3787,14 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.cpu_system '' '%s CPU System Time (100%% = 1 core)' 'percentage' cpu %s.cpu_system stacked 20021 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU);
}
+ APPS_PLUGIN_FUNCTIONS();
if(show_guest_time) {
fprintf(stdout, "CHART %s.cpu_guest '' '%s CPU Guest Time (100%% = 1 core)' 'percentage' cpu %s.cpu_guest stacked 20022 %d\n", type, title, type, update_every);
@@ -3775,6 +3802,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, time_factor * RATES_DETAIL / 100LLU);
}
+ APPS_PLUGIN_FUNCTIONS();
}
#ifndef __FreeBSD__
@@ -3783,6 +3811,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, 1L, 1024L);
}
+ APPS_PLUGIN_FUNCTIONS();
#endif
fprintf(stdout, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20012 %d\n", type, title, type, update_every);
@@ -3790,12 +3819,14 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
#ifdef __FreeBSD__
fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'blocks/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
@@ -3803,36 +3834,42 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'blocks/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
#else
fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'KiB/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'KiB/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'KiB/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.lwrites '' '%s I/O Logical Writes' 'KiB/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
for (w = root; w ; w = w->next) {
if(unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
}
+ APPS_PLUGIN_FUNCTIONS();
#endif
if(enable_file_charts) {
@@ -3842,6 +3879,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if (unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n",
type, title, type, update_every);
@@ -3849,6 +3887,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if (unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
fprintf(stdout, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type,
title, type, update_every);
@@ -3856,6 +3895,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type
if (unlikely(w->exposed))
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
}
+ APPS_PLUGIN_FUNCTIONS();
}
}
@@ -4134,6 +4174,592 @@ static int check_capabilities() {
}
#endif
+netdata_mutex_t mutex = NETDATA_MUTEX_INITIALIZER;
+
+#define PROCESS_FILTER_CATEGORY "category:"
+#define PROCESS_FILTER_USER "user:"
+#define PROCESS_FILTER_GROUP "group:"
+#define PROCESS_FILTER_PROCESS "process:"
+#define PROCESS_FILTER_PID "pid:"
+#define PROCESS_FILTER_UID "uid:"
+#define PROCESS_FILTER_GID "gid:"
+
+static struct target *find_target_by_name(struct target *base, const char *name) {
+ struct target *t;
+ for(t = base; t ; t = t->next) {
+ if (strcmp(t->name, name) == 0)
+ return t;
+ }
+
+ return NULL;
+}
+
+static kernel_uint_t MemTotal = 0;
+
+static void get_MemTotal(void) {
+#ifdef __FreeBSD__
+ // TODO - fix this for FreeBSD
+ return;
+#else
+ char filename[FILENAME_MAX + 1];
+ snprintfz(filename, FILENAME_MAX, "%s/proc/meminfo", netdata_configured_host_prefix);
+
+ procfile *ff = procfile_open(filename, ": \t", PROCFILE_FLAG_DEFAULT);
+ if(!ff)
+ return;
+
+ ff = procfile_readall(ff);
+ if(!ff)
+ return;
+
+ size_t line, lines = procfile_lines(ff);
+
+ for(line = 0; line < lines ;line++) {
+ size_t words = procfile_linewords(ff, line);
+ if(words == 3 && strcmp(procfile_lineword(ff, line, 0), "MemTotal") == 0 && strcmp(procfile_lineword(ff, line, 2), "kB") == 0) {
+ kernel_uint_t n = str2ull(procfile_lineword(ff, line, 1));
+ if(n) MemTotal = n;
+ break;
+ }
+ }
+
+ procfile_close(ff);
+#endif
+}
+
+static void apps_plugin_function_error(const char *transaction, int code, const char *msg) {
+ char buffer[PLUGINSD_LINE_MAX + 1];
+ json_escape_string(buffer, msg, PLUGINSD_LINE_MAX);
+
+ pluginsd_function_result_begin_to_stdout(transaction, code, "application/json", now_realtime_sec());
+ fprintf(stdout, "{\"status\":%d,\"error_message\":\"%s\"}", code, buffer);
+ pluginsd_function_result_end_to_stdout();
+}
+
+static void apps_plugin_function_processes_help(const char *transaction) {
+ pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600);
+ fprintf(stdout, "%s",
+ "apps.plugin / processes\n"
+ "\n"
+ "Function `processes` presents all the currently running processes of the system.\n"
+ "\n"
+ "The following filters are supported:\n"
+ "\n"
+ " category:NAME\n"
+ " Shows only processes that are assigned the category `NAME` in apps_groups.conf\n"
+ "\n"
+ " user:NAME\n"
+ " Shows only processes that are running as user name `NAME`.\n"
+ "\n"
+ " group:NAME\n"
+ " Shows only processes that are running as group name `NAME`.\n"
+ "\n"
+ " process:NAME\n"
+ " Shows only processes that their Command is `NAME` or their parent's Command is `NAME`.\n"
+ "\n"
+ " pid:NUMBER\n"
+ " Shows only processes that their PID is `NUMBER` or their parent's PID is `NUMBER`\n"
+ "\n"
+ " uid:NUMBER\n"
+ " Shows only processes that their UID is `NUMBER`\n"
+ "\n"
+ " gid:NUMBER\n"
+ " Shows only processes that their GID is `NUMBER`\n"
+ "\n"
+ "Filters can be combined. Each filter can be given only one time.\n"
+ );
+ pluginsd_function_result_end_to_stdout();
+}
+
+#define add_table_field(wb, key, name, visible, type, units, max, sort) do { \
+ if(fields_added) buffer_strcat(wb, ","); \
+ buffer_sprintf(wb, "\n \"%s\": {", key); \
+ buffer_sprintf(wb, "\n \"index\":%d,", fields_added); \
+ buffer_sprintf(wb, "\n \"name\":\"%s\",", name); \
+ buffer_sprintf(wb, "\n \"visible\":%s,", (visible)?"true":"false"); \
+ buffer_sprintf(wb, "\n \"type\":\"%s\",", type); \
+ if(units) \
+ buffer_sprintf(wb, "\n \"units\":\"%s\",", (char*)(units)); \
+ if(!isnan(max)) \
+ buffer_sprintf(wb, "\n \"max\":\"%f\",", (NETDATA_DOUBLE)(max)); \
+ buffer_sprintf(wb, "\n \"sort\":\"%s\"", sort); \
+ buffer_sprintf(wb, "\n }"); \
+ fields_added++; \
+ } while(0)
+
+static BUFFER *func_processes_fields = NULL;
+
+static void apps_plugin_function_processes(const char *transaction, char *function __maybe_unused, char *line_buffer __maybe_unused, int line_max __maybe_unused, int timeout __maybe_unused) {
+ struct pid_stat *p;
+
+ char *words[PLUGINSD_MAX_WORDS] = { NULL };
+ pluginsd_split_words(function, words, PLUGINSD_MAX_WORDS, NULL, NULL, 0);
+
+ struct target *category = NULL, *user = NULL, *group = NULL;
+ const char *process_name = NULL;
+ pid_t pid = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ bool filter_pid = false, filter_uid = false, filter_gid = false;
+
+ for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) {
+ if(!words[i]) break;
+
+ if(!category && strncmp(words[i], PROCESS_FILTER_CATEGORY, strlen(PROCESS_FILTER_CATEGORY)) == 0) {
+ category = find_target_by_name(apps_groups_root_target, &words[i][strlen(PROCESS_FILTER_CATEGORY)]);
+ if(!category) {
+ apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No category with that name found.");
+ return;
+ }
+ }
+ else if(!user && strncmp(words[i], PROCESS_FILTER_USER, strlen(PROCESS_FILTER_USER)) == 0) {
+ user = find_target_by_name(users_root_target, &words[i][strlen(PROCESS_FILTER_USER)]);
+ if(!user) {
+ apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No user with that name found.");
+ return;
+ }
+ }
+ else if(strncmp(words[i], PROCESS_FILTER_GROUP, strlen(PROCESS_FILTER_GROUP)) == 0) {
+ group = find_target_by_name(groups_root_target, &words[i][strlen(PROCESS_FILTER_GROUP)]);
+ if(!group) {
+ apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, "No group with that name found.");
+ return;
+ }
+ }
+ else if(!process_name && strncmp(words[i], PROCESS_FILTER_PROCESS, strlen(PROCESS_FILTER_PROCESS)) == 0) {
+ process_name = &words[i][strlen(PROCESS_FILTER_PROCESS)];
+ }
+ else if(!pid && strncmp(words[i], PROCESS_FILTER_PID, strlen(PROCESS_FILTER_PID)) == 0) {
+ pid = str2i(&words[i][strlen(PROCESS_FILTER_PID)]);
+ filter_pid = true;
+ }
+ else if(!uid && strncmp(words[i], PROCESS_FILTER_UID, strlen(PROCESS_FILTER_UID)) == 0) {
+ uid = str2i(&words[i][strlen(PROCESS_FILTER_UID)]);
+ filter_uid = true;
+ }
+ else if(!gid && strncmp(words[i], PROCESS_FILTER_GID, strlen(PROCESS_FILTER_GID)) == 0) {
+ gid = str2i(&words[i][strlen(PROCESS_FILTER_GID)]);
+ filter_gid = true;
+ }
+ else if(strcmp(words[i], "help") == 0) {
+ apps_plugin_function_processes_help(transaction);
+ return;
+ }
+ else {
+ char msg[PLUGINSD_LINE_MAX];
+ snprintfz(msg, PLUGINSD_LINE_MAX, "Invalid parameter '%s'", words[i]);
+ apps_plugin_function_error(transaction, HTTP_RESP_BAD_REQUEST, msg);
+ return;
+ }
+ }
+
+ time_t expires = now_realtime_sec() + update_every;
+ pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires);
+
+ if(unlikely(!func_processes_fields)) {
+ func_processes_fields = buffer_create(1000);
+
+ int fields_added = 0;<