mirror of
https://github.com/netdata/netdata.git
synced 2025-04-13 17:19:11 +00:00

* network viewer based on local-sockets * added more fields for the UI * added socket state * added inodes to the lists
482 lines
20 KiB
C
482 lines
20 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "collectors/all.h"
|
|
#include "libnetdata/libnetdata.h"
|
|
#include "libnetdata/required_dummies.h"
|
|
#include "collectors/plugins.d/local-sockets.h"
|
|
|
|
#define NETWORK_VIEWER_FUNCTION "network-viewer"
|
|
#define NETWORK_VIEWER_HELP "Network dependencies (outbound connections)"
|
|
|
|
netdata_mutex_t stdout_mutex = NETDATA_MUTEX_INITIALIZER;
|
|
static bool plugin_should_exit = false;
|
|
|
|
ENUM_STR_MAP_DEFINE(SOCKET_DIRECTION) = {
|
|
{ .id = SOCKET_DIRECTION_LISTEN, .name = "listen" },
|
|
{ .id = SOCKET_DIRECTION_LOCAL, .name = "local" },
|
|
{ .id = SOCKET_DIRECTION_INBOUND, .name = "inbound" },
|
|
{ .id = SOCKET_DIRECTION_OUTBOUND, .name = "outbound" },
|
|
|
|
// terminator
|
|
{ . id = 0, .name = NULL }
|
|
};
|
|
ENUM_STR_DEFINE_FUNCTIONS(SOCKET_DIRECTION, SOCKET_DIRECTION_LISTEN, "unknown");
|
|
|
|
typedef int TCP_STATE;
|
|
ENUM_STR_MAP_DEFINE(TCP_STATE) = {
|
|
{ .id = TCP_ESTABLISHED, .name = "established" },
|
|
{ .id = TCP_SYN_SENT, .name = "syn-sent" },
|
|
{ .id = TCP_SYN_RECV, .name = "syn-received" },
|
|
{ .id = TCP_FIN_WAIT1, .name = "fin1-wait1" },
|
|
{ .id = TCP_FIN_WAIT2, .name = "fin1-wait2" },
|
|
{ .id = TCP_TIME_WAIT, .name = "time-wait" },
|
|
{ .id = TCP_CLOSE, .name = "close" },
|
|
{ .id = TCP_CLOSE_WAIT, .name = "close-wait" },
|
|
{ .id = TCP_LAST_ACK, .name = "last-ack" },
|
|
{ .id = TCP_LISTEN, .name = "listen" },
|
|
{ .id = TCP_CLOSING, .name = "closing" },
|
|
|
|
// terminator
|
|
{ . id = 0, .name = NULL }
|
|
};
|
|
ENUM_STR_DEFINE_FUNCTIONS(TCP_STATE, 0, "unknown");
|
|
|
|
|
|
static void local_socket_to_array(struct local_socket_state *ls, struct local_socket *n, void *data) {
|
|
BUFFER *wb = data;
|
|
|
|
char local_address[INET6_ADDRSTRLEN];
|
|
char remote_address[INET6_ADDRSTRLEN];
|
|
char *protocol;
|
|
|
|
if(n->local.family == AF_INET) {
|
|
ipv4_address_to_txt(n->local.ip.ipv4, local_address);
|
|
ipv4_address_to_txt(n->remote.ip.ipv4, remote_address);
|
|
protocol = n->local.protocol == IPPROTO_TCP ? "tcp4" : "udp4";
|
|
}
|
|
else if(n->local.family == AF_INET6) {
|
|
ipv6_address_to_txt(&n->local.ip.ipv6, local_address);
|
|
ipv6_address_to_txt(&n->remote.ip.ipv6, remote_address);
|
|
protocol = n->local.protocol == IPPROTO_TCP ? "tcp6" : "udp6";
|
|
}
|
|
else
|
|
return;
|
|
|
|
const char *type;
|
|
if(n->net_ns_inode == ls->proc_self_net_ns_inode)
|
|
type = "system";
|
|
else
|
|
type = "container";
|
|
|
|
buffer_json_add_array_item_array(wb);
|
|
{
|
|
buffer_json_add_array_item_string(wb, SOCKET_DIRECTION_2str(n->direction));
|
|
buffer_json_add_array_item_string(wb, protocol);
|
|
buffer_json_add_array_item_string(wb, type); // system or container
|
|
if(n->local.protocol == IPPROTO_TCP)
|
|
buffer_json_add_array_item_string(wb, TCP_STATE_2str(n->state));
|
|
else
|
|
buffer_json_add_array_item_string(wb, "stateless");
|
|
buffer_json_add_array_item_uint64(wb, n->pid);
|
|
buffer_json_add_array_item_string(wb, n->comm);
|
|
buffer_json_add_array_item_string(wb, n->cmdline);
|
|
buffer_json_add_array_item_string(wb, local_address);
|
|
buffer_json_add_array_item_uint64(wb, n->local.port);
|
|
buffer_json_add_array_item_string(wb, local_sockets_address_space(&n->local));
|
|
buffer_json_add_array_item_string(wb, remote_address);
|
|
buffer_json_add_array_item_uint64(wb, n->remote.port);
|
|
buffer_json_add_array_item_string(wb, local_sockets_address_space(&n->remote));
|
|
buffer_json_add_array_item_uint64(wb, n->inode);
|
|
buffer_json_add_array_item_uint64(wb, n->net_ns_inode);
|
|
buffer_json_add_array_item_uint64(wb, 1); // count
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
|
|
void network_viewer_function(const char *transaction, char *function __maybe_unused, usec_t *stop_monotonic_ut __maybe_unused,
|
|
bool *cancelled __maybe_unused, BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused,
|
|
const char *source __maybe_unused, void *data __maybe_unused) {
|
|
|
|
CLEAN_BUFFER *wb = buffer_create(0, NULL);
|
|
buffer_flush(wb);
|
|
wb->content_type = CT_APPLICATION_JSON;
|
|
buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
|
|
|
|
buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
|
|
buffer_json_member_add_string(wb, "type", "table");
|
|
buffer_json_member_add_time_t(wb, "update_every", 1);
|
|
buffer_json_member_add_string(wb, "help", NETWORK_VIEWER_HELP);
|
|
buffer_json_member_add_array(wb, "data");
|
|
|
|
LS_STATE ls = {
|
|
.config = {
|
|
.listening = true,
|
|
.inbound = true,
|
|
.outbound = true,
|
|
.local = true,
|
|
.tcp4 = true,
|
|
.tcp6 = true,
|
|
.udp4 = true,
|
|
.udp6 = true,
|
|
.pid = true,
|
|
.cmdline = true,
|
|
.comm = true,
|
|
.namespaces = true,
|
|
|
|
.max_errors = 10,
|
|
|
|
.cb = local_socket_to_array,
|
|
.data = wb,
|
|
},
|
|
.stats = { 0 },
|
|
.sockets_hashtable = { 0 },
|
|
.local_ips_hashtable = { 0 },
|
|
.listening_ports_hashtable = { 0 },
|
|
};
|
|
|
|
local_sockets_process(&ls);
|
|
|
|
buffer_json_array_close(wb);
|
|
buffer_json_member_add_object(wb, "columns");
|
|
{
|
|
size_t field_id = 0;
|
|
|
|
// Direction
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Direction", "Socket Direction",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Protocol
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Protocol", "Socket Protocol",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Type
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Namespace", "Namespace",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// State
|
|
buffer_rrdf_table_add_field(wb, field_id++, "State", "Socket State",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Pid
|
|
buffer_rrdf_table_add_field(wb, field_id++, "PID", "Process ID",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Comm
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Process", "Process Name",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
|
|
NULL);
|
|
|
|
// Cmdline
|
|
buffer_rrdf_table_add_field(wb, field_id++, "CommandLine", "Command Line",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_NONE|RRDF_FIELD_OPTS_FULL_WIDTH,
|
|
NULL);
|
|
|
|
// Local Address
|
|
buffer_rrdf_table_add_field(wb, field_id++, "LocalIP", "Local IP Address",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
|
|
NULL);
|
|
|
|
// Local Port
|
|
buffer_rrdf_table_add_field(wb, field_id++, "LocalPort", "Local Port",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Local Address Space
|
|
buffer_rrdf_table_add_field(wb, field_id++, "LocalAddressSpace", "Local IP Address Space",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_NONE,
|
|
NULL);
|
|
|
|
// Remote Address
|
|
buffer_rrdf_table_add_field(wb, field_id++, "RemoteIP", "Remote IP Address",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE|RRDF_FIELD_OPTS_FULL_WIDTH,
|
|
NULL);
|
|
|
|
// Remote Port
|
|
buffer_rrdf_table_add_field(wb, field_id++, "RemotePort", "Remote Port",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_VISIBLE,
|
|
NULL);
|
|
|
|
// Remote Address Space
|
|
buffer_rrdf_table_add_field(wb, field_id++, "RemoteAddressSpace", "Remote IP Address Space",
|
|
RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_NONE,
|
|
NULL);
|
|
|
|
// inode
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Inode", "Socket Inode",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_NONE,
|
|
NULL);
|
|
|
|
// Namespace inode
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Namespace Inode", "Namespace Inode",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT,
|
|
RRDF_FIELD_OPTS_NONE,
|
|
NULL);
|
|
|
|
// Count
|
|
buffer_rrdf_table_add_field(wb, field_id++, "Count", "Count",
|
|
RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE,
|
|
0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL,
|
|
RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_NONE,
|
|
RRDF_FIELD_OPTS_NONE,
|
|
NULL);
|
|
}
|
|
buffer_json_object_close(wb); // columns
|
|
buffer_json_member_add_string(wb, "default_sort_column", "Direction");
|
|
|
|
buffer_json_member_add_object(wb, "charts");
|
|
{
|
|
// Data Collection Age chart
|
|
buffer_json_member_add_object(wb, "Count");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Connections");
|
|
buffer_json_member_add_string(wb, "type", "stacked-bar");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Direction");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
// Streaming Age chart
|
|
buffer_json_member_add_object(wb, "Count");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Connections");
|
|
buffer_json_member_add_string(wb, "type", "stacked-bar");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Process");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
// DB Duration
|
|
buffer_json_member_add_object(wb, "Count");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Connections");
|
|
buffer_json_member_add_string(wb, "type", "stacked-bar");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Protocol");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
}
|
|
buffer_json_object_close(wb); // charts
|
|
|
|
buffer_json_member_add_array(wb, "default_charts");
|
|
{
|
|
buffer_json_add_array_item_array(wb);
|
|
buffer_json_add_array_item_string(wb, "Count");
|
|
buffer_json_add_array_item_string(wb, "Direction");
|
|
buffer_json_array_close(wb);
|
|
|
|
buffer_json_add_array_item_array(wb);
|
|
buffer_json_add_array_item_string(wb, "Count");
|
|
buffer_json_add_array_item_string(wb, "Process");
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_array_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "group_by");
|
|
{
|
|
buffer_json_member_add_object(wb, "Direction");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Direction");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Direction");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "Protocol");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Protocol");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Protocol");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "Namespace");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Namespace");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Namespace");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "Process");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Process");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "Process");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "LocalIP");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Local IP");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "LocalIP");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "LocalPort");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Local Port");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "LocalPort");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "RemoteIP");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Remote IP");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "RemoteIP");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
|
|
buffer_json_member_add_object(wb, "RemotePort");
|
|
{
|
|
buffer_json_member_add_string(wb, "name", "Remote Port");
|
|
buffer_json_member_add_array(wb, "columns");
|
|
{
|
|
buffer_json_add_array_item_string(wb, "RemotePort");
|
|
}
|
|
buffer_json_array_close(wb);
|
|
}
|
|
buffer_json_object_close(wb);
|
|
}
|
|
buffer_json_object_close(wb); // group_by
|
|
|
|
|
|
buffer_json_member_add_time_t(wb, "expires", now_realtime_sec() + 1);
|
|
buffer_json_finalize(wb);
|
|
|
|
netdata_mutex_lock(&stdout_mutex);
|
|
pluginsd_function_result_to_stdout(transaction, HTTP_RESP_OK, "application/json", now_realtime_sec(), wb);
|
|
netdata_mutex_unlock(&stdout_mutex);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------------------
|
|
// main
|
|
|
|
int main(int argc __maybe_unused, char **argv __maybe_unused) {
|
|
clocks_init();
|
|
netdata_thread_set_tag("NETWORK-VIEWER");
|
|
nd_log_initialize_for_external_plugins("network-viewer.plugin");
|
|
|
|
netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
|
|
if(verify_netdata_host_prefix(true) == -1) exit(1);
|
|
|
|
// ----------------------------------------------------------------------------------------------------------------
|
|
|
|
fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"%s\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n",
|
|
NETWORK_VIEWER_FUNCTION, 60, NETWORK_VIEWER_HELP,
|
|
(HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_SIGNED_ID | HTTP_ACCESS_SAME_SPACE | HTTP_ACCESS_SENSITIVE_DATA),
|
|
RRDFUNCTIONS_PRIORITY_DEFAULT);
|
|
|
|
// ----------------------------------------------------------------------------------------------------------------
|
|
|
|
struct functions_evloop_globals *wg =
|
|
functions_evloop_init(5, "Network-Viewer", &stdout_mutex, &plugin_should_exit);
|
|
|
|
functions_evloop_add_function(wg,
|
|
NETWORK_VIEWER_FUNCTION,
|
|
network_viewer_function,
|
|
PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT,
|
|
NULL);
|
|
|
|
// ----------------------------------------------------------------------------------------------------------------
|
|
|
|
usec_t step_ut = 100 * USEC_PER_MS;
|
|
usec_t send_newline_ut = 0;
|
|
bool tty = isatty(fileno(stdout)) == 1;
|
|
|
|
heartbeat_t hb;
|
|
heartbeat_init(&hb);
|
|
while(!plugin_should_exit) {
|
|
|
|
usec_t dt_ut = heartbeat_next(&hb, step_ut);
|
|
send_newline_ut += dt_ut;
|
|
|
|
if(!tty && send_newline_ut > USEC_PER_SEC) {
|
|
send_newline_and_flush(&stdout_mutex);
|
|
send_newline_ut = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|