proc_sources = proc_net_dev.c proc_net_ip_vs_stats.c proc_diskstats.c proc_meminfo.c proc_net_netstat.c proc_net_snmp.c proc_net_stat_conntrack.c proc_stat.c proc_vmstat.c proc_net_rpc_nfsd.c
sources = procfile.c common.c log.c popen.c url.c config.c web_buffer.c storage_number.c web_client.c global_statistics.c rrd.c rrd2json.c web_server.c plugins_d.c daemon.c plugin_tc.c plugin_checks.c plugin_idlejitter.c plugin_proc.c unit_test.c main.c
+libs = -pthread -lz -lm
+
+# nfacct requires root access, so we prefer it as a plugin.d external plugin
+ifdef INTERNAL_PLUGIN_NFACCT
+CFLAGS += -DINTERNAL_PLUGIN_NFACCT=1
+sources += plugin_nfacct.c
+libs += -lmnl -lnetfilter_acct
+endif
+
headers = $(patsubst %.c,%.h,$(sources))
objects = $(patsubst %.c,%.o,$(sources) $(proc_sources))
netdata: $(objects)
@echo " $(CC) netdata"
- @$(CC) -o netdata $(objects) -pthread -lz
+ @$(CC) -o netdata $(objects) $(libs)
.PHONY: plugins
plugins:
#define D_CHILDS 0x00001000
#define D_EXIT 0x00002000
#define D_CHECKS 0x00004000
+#define D_NFACCT_LOOP 0x00008000
#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
//#define DEBUG 0xffffffff
#include "plugin_tc.h"
#include "plugin_checks.h"
#include "plugin_proc.h"
+#include "plugin_nfacct.h"
#include "main.h"
int enabled;
pthread_t *thread;
+
+ void (*init_routine) (void);
void *(*start_routine) (void *);
};
struct netdata_static_thread static_threads[] = {
- {"tc", "plugins", "tc", 1, NULL, tc_main},
- {"idlejitter", "plugins", "idlejitter", 1, NULL, cpuidlejitter_main},
- {"proc", "plugins", "proc", 1, NULL, proc_main},
- {"plugins.d", NULL, NULL, 1, NULL, pluginsd_main},
- {"check", "plugins", "checks", 0, NULL, checks_main},
- {"web", NULL, NULL, 1, NULL, socket_listen_main},
- {NULL, NULL, NULL, 0, NULL, NULL}
+ {"tc", "plugins", "tc", 1, NULL, NULL, tc_main},
+ {"idlejitter", "plugins", "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
+ {"proc", "plugins", "proc", 1, NULL, NULL, proc_main},
+
+#ifdef INTERNAL_PLUGIN_NFACCT
+ // nfacct requires root access
+ // so, we build it as an external plugin with setuid to root
+ {"nfacct", "plugins", "nfacct", 1, NULL, NULL, nfacct_main},
+#endif
+
+ {"plugins.d", NULL, NULL, 1, NULL, NULL, pluginsd_main},
+ {"check", "plugins", "checks", 0, NULL, NULL, checks_main},
+ {"web", NULL, NULL, 1, NULL, NULL, socket_listen_main},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL}
};
void kill_childs()
// --------------------------------------------------------------------
+ update_every = config_get_number("global", "update every", UPDATE_EVERY);
+ if(update_every < 1 || update_every > 600) {
+ fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY_MAX);
+ update_every = UPDATE_EVERY;
+ }
+ else debug(D_OPTIONS, "update timer set to %d.", update_every);
+
+ // --------------------------------------------------------------------
+
+ for (i = 0; static_threads[i].name != NULL ; i++) {
+ struct netdata_static_thread *st = &static_threads[i];
+
+ if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
+ if(st->enabled && st->init_routine) st->init_routine();
+ }
+
+ // --------------------------------------------------------------------
+
prepare_rundir();
char *user = config_get("global", "run as user", (getuid() == 0)?"nobody":"");
if(*user) {
// --------------------------------------------------------------------
- update_every = config_get_number("global", "update every", UPDATE_EVERY);
- if(update_every < 1 || update_every > 600) {
- fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY_MAX);
- update_every = UPDATE_EVERY;
- }
- else debug(D_OPTIONS, "update timer set to %d.", update_every);
-
- // --------------------------------------------------------------------
-
listen_port = config_get_number("global", "port", LISTEN_PORT);
if(listen_port < 1 || listen_port > 65535) {
fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT);
for (i = 0; static_threads[i].name != NULL ; i++) {
struct netdata_static_thread *st = &static_threads[i];
- if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
-
if(st->enabled) {
st->thread = malloc(sizeof(pthread_t));
#include "rrd.h"
#include "plugin_proc.h"
-#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
+#define RRD_TYPE_NET_STAT_CONNTRACK "netfilter"
#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
if(!aentries) aentries = tentries;
// sum all the cpus together
- asearched += tsearched; // conntrack.search
- afound += tfound; // conntrack.search
- anew += tnew; // conntrack.new
- ainvalid += tinvalid; // conntrack.new
- aignore += tignore; // conntrack.new
- adelete += tdelete; // conntrack.changes
+ asearched += tsearched; // conntrack.search
+ afound += tfound; // conntrack.search
+ anew += tnew; // conntrack.new
+ ainvalid += tinvalid; // conntrack.new
+ aignore += tignore; // conntrack.new
+ adelete += tdelete; // conntrack.changes
adelete_list += tdelete_list; // conntrack.changes
- ainsert += tinsert; // conntrack.changes
+ ainsert += tinsert; // conntrack.changes
ainsert_failed += tinsert_failed; // conntrack.errors
- adrop += tdrop; // conntrack.errors
+ adrop += tdrop; // conntrack.errors
aearly_drop += tearly_drop; // conntrack.errors
aicmp_error += ticmp_error; // conntrack.errors
aexpect_new += texpect_new; // conntrack.expect
rd->collected_value = value;
}
-int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value)
+int rrd_stats_dimension_set(RRD_STATS *st, const char *id, collected_number value)
{
RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
if(!rd) {
struct rrd_dimension {
char magic[sizeof(RRD_DIMENSION_MAGIC) + 1]; // our magic
- char id[RRD_STATS_NAME_MAX + 1]; // the id of this dimension (for internal identification)
- char *name; // the name of this dimension (as presented to user)
+ char id[RRD_STATS_NAME_MAX + 1]; // the id of this dimension (for internal identification)
+ char *name; // the name of this dimension (as presented to user)
char cache_file[FILENAME_MAX+1];
- unsigned long hash; // a simple hash on the id, to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ unsigned long hash; // a simple hash on the id, to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
- long entries; // how many entries this dimension has
- // this should be the same to the entries of the data set
+ long entries; // how many entries this dimension has
+ // this should be the same to the entries of the data set
- long current_entry; // the entry that is currently being updated
- int update_every; // every how many seconds is this updated?
+ long current_entry; // the entry that is currently being updated
+ int update_every; // every how many seconds is this updated?
- int hidden; // if set to non zero, this dimension will not be sent to the client
- int mapped; // 1 if the file is mapped
- unsigned long memsize; // the memory allocated for this dimension
+ int hidden; // if set to non zero, this dimension will not be sent to the client
+ int mapped; // 1 if the file is mapped
+ unsigned long memsize; // the memory allocated for this dimension
int algorithm;
long multiplier;
long divisor;
- struct timeval last_collected_time; // when was this dimension last updated
- // this is only used to detect un-updated dimensions
- // which are removed after some time
+ struct timeval last_collected_time; // when was this dimension last updated
+ // this is only used to detect un-updated dimensions
+ // which are removed after some time
calculated_number calculated_value;
calculated_number last_calculated_value;
- collected_number collected_value; // the value collected at this round
- collected_number last_collected_value; // the value that was collected at the last round
+ collected_number collected_value; // the value collected at this round
+ collected_number last_collected_value; // the value that was collected at the last round
- struct rrd_dimension *next; // linking of dimensions within the same data set
+ struct rrd_dimension *next; // linking of dimensions within the same data set
- storage_number values[]; // the array of values - THIS HAS TO BE THE LAST MEMBER
+ storage_number values[]; // the array of values - THIS HAS TO BE THE LAST MEMBER
};
typedef struct rrd_dimension RRD_DIMENSION;
struct rrd_stats {
- char magic[sizeof(RRD_STATS_MAGIC) + 1]; // our magic
+ char magic[sizeof(RRD_STATS_MAGIC) + 1]; // our magic
- char id[RRD_STATS_NAME_MAX + 1]; // id of the data set
- char *name; // name of the data set
- char *cache_dir; // the directory to store dimension maps
+ char id[RRD_STATS_NAME_MAX + 1]; // id of the data set
+ char *name; // name of the data set
+ char *cache_dir; // the directory to store dimension maps
char cache_file[FILENAME_MAX+1];
- char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options)
- char *family; // the family of this data set (for grouping them together)
- char *title; // title shown to user
- char *units; // units of measurement
+ char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options)
+ char *family; // the family of this data set (for grouping them together)
+ char *title; // title shown to user
+ char *units; // units of measurement
pthread_rwlock_t rwlock;
- unsigned long counter; // the number of times we added values to this rrd
- unsigned long counter_done; // the number of times we added values to this rrd
+ unsigned long counter; // the number of times we added values to this rrd
+ unsigned long counter_done; // the number of times we added values to this rrd
- int mapped; // if set to 1, this is memory mapped
- unsigned long memsize; // how much mem we have allocated for this (without dimensions)
+ int mapped; // if set to 1, this is memory mapped
+ unsigned long memsize; // how much mem we have allocated for this (without dimensions)
- unsigned long hash_name; // a simple hash on the name
- unsigned long hash; // a simple hash on the id, to speed up searching
- // we first compare hashes, and only if the hashes are equal we do string comparisons
+ unsigned long hash_name; // a simple hash on the name
+ unsigned long hash; // a simple hash on the id, to speed up searching
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
long priority;
- long entries; // total number of entries in the data set
- long current_entry; // the entry that is currently being updated
- // it goes around in a round-robin fashion
+ long entries; // total number of entries in the data set
+ long current_entry; // the entry that is currently being updated
+ // it goes around in a round-robin fashion
- int update_every; // every how many seconds is this updated?
- unsigned long long first_entry_t; // the timestamp (in microseconds) of the oldest entry in the db
- struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function)
- struct timeval last_collected_time; //
+ int update_every; // every how many seconds is this updated?
+ unsigned long long first_entry_t; // the timestamp (in microseconds) of the oldest entry in the db
+ struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function)
+ struct timeval last_collected_time; //
unsigned long long usec_since_last_update;
total_number collected_total;
int chart_type;
int debug;
int enabled;
- int isdetail; // if set, the data set should be considered as a detail of another
- // (the master data set should be the one that has the same family and is not detail)
+ int isdetail; // if set, the data set should be considered as a detail of another
+ // (the master data set should be the one that has the same family and is not detail)
- RRD_DIMENSION *dimensions; // the actual data for every dimension
+ RRD_DIMENSION *dimensions; // the actual data for every dimension
- struct rrd_stats *next; // linking of rrd stats
+ struct rrd_stats *next; // linking of rrd stats
};
typedef struct rrd_stats RRD_STATS;
extern int rrd_stats_dimension_hide(RRD_STATS *st, const char *id);
extern void rrd_stats_dimension_set_by_pointer(RRD_STATS *st, RRD_DIMENSION *rd, collected_number value);
-extern int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value);
+extern int rrd_stats_dimension_set(RRD_STATS *st, const char *id, collected_number value);
extern void rrd_stats_next_usec(RRD_STATS *st, unsigned long long microseconds);
extern void rrd_stats_next(RRD_STATS *st);
+#include <math.h>
#include "log.h"
#include "storage_number.h"
mul = m;
+ // without this there are rounding problems
+ // example: 0.9 becomes 0.89
+ n = lrint(n);
+
r = (sign << 31) + (exp << 30) + (mul << 27) + n;
// fprintf(stderr, "PACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", r, sign, exp, mul, n);
#include <stdio.h>
+#include <stdlib.h>
#include "storage_number.h"
#include "rrd.h"
int unit_test_storage()
{
- char buffer[100];
+ char buffer[100], *msg;
storage_number s;
- calculated_number c, a = 0, d, f;
+ calculated_number c, a = 0, d, ddiff, dcdiff, f, p, pdiff, pcdiff, maxddiff = 0, maxpdiff = 0;
int i, j, g, r = 0, l;
for(g = -1; g <= 1 ; g++) {
a += 0.0000001;
c = a * g;
for(i = 0; i < 21 ;i++, c *= 10) {
+ if(c > 0 && c < 0.00001) continue;
+ if(c < 0 && c > -0.00001) continue;
+
s = pack_storage_number(c);
d = unpack_storage_number(s);
+ ddiff = d - c;
+ dcdiff = ddiff * 100.0 / c;
+ if(dcdiff < 0) dcdiff = -dcdiff;
+ if(dcdiff > maxddiff) maxddiff = dcdiff;
+
f = d / c;
l = print_calculated_number(buffer, d);
+ p = strtold(buffer, NULL);
+ pdiff = c - p;
+ pcdiff = pdiff * 100.0 / c;
+ if(pcdiff < 0) pcdiff = -pcdiff;
+ if(pcdiff > maxpdiff) maxpdiff = pcdiff;
if(f < 0.99999 || f > 1.00001) {
- fprintf(stderr, "\nERROR\n" CALCULATED_NUMBER_FORMAT " original\n" CALCULATED_NUMBER_FORMAT " unpacked, (stored as 0x%08X)\n%s printed as %d bytes\n", c, d, s, buffer, l);
+ msg = "ERROR";
r++;
}
- else {
- fprintf(stderr, "\nOK\n" CALCULATED_NUMBER_FORMAT " original\n" CALCULATED_NUMBER_FORMAT " unpacked, (stored as 0x%08X)\n%s printed as %d bytes\n", c, d, s, buffer, l);
- }
+ else msg = "OK";
+
+ fprintf(stderr, "%s\n"
+ CALCULATED_NUMBER_FORMAT " original\n"
+ CALCULATED_NUMBER_FORMAT " unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
+ "%s printed (%d bytes)\n"
+ CALCULATED_NUMBER_FORMAT " re-parsed with diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%\n\n",
+ msg,
+ c,
+ d, s, ddiff, dcdiff,
+ buffer,
+ l, p, pdiff, pcdiff
+ );
}
}
}
+ fprintf(stderr, "Worst accuracy loss on unpacked numbers: " CALCULATED_NUMBER_FORMAT "%%\n", maxddiff);
+ fprintf(stderr, "Worst accuracy loss on printed numbers: " CALCULATED_NUMBER_FORMAT "%%\n", maxpdiff);
+
return r;
}
#include <stdlib.h>
+#include <math.h>
#include "web_buffer.h"
{
char *wstr = str;
- // make sure it is unsigned
- unsigned long long uvalue = (unsigned long long)(((value < 0) ? -value : value) * (calculated_number)10000);
+ int sign = (value < 0) ? 1 : 0;
+ if(sign) value = -value;
+
+ // without llrint() there are rounding problems
+ // for example 0.9 becomes 0.89
+ unsigned long long uvalue = llrint(value * (calculated_number)100000);
// print each digit
do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
- // make sure we have 8 bytes at least
- while((wstr - str) < 5) *wstr++ = '0';
+ // make sure we have 6 bytes at least
+ while((wstr - str) < 6) *wstr++ = '0';
// put the sign back
- if (value < 0) *wstr++ = '-';
+ if(sign) *wstr++ = '-';
// reverse it
wstr--;
strreverse(str, wstr);
+ // remove trailing zeros
+ int decimal = 5;
+ while(decimal > 0 && *wstr == '0') {
+ *wstr-- = '\0';
+ decimal--;
+ }
+
// terminate it, one position to the right
// to let space for a dot
wstr[2] = '\0';
+ // make space for the dot
int i;
- for(i = 0; i < 4 ;i++) {
+ for(i = 0; i < decimal ;i++) {
wstr[1] = wstr[0];
wstr--;
}
- wstr[1] = '.';
+
+ // put the dot
+ if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
+ else wstr[1] = '.';
// return the buffer length
- return ( (wstr - str) + 6 );
+ return ( (wstr - str) + 2 + decimal );
}
void web_buffer_rrd_value(struct web_buffer *wb, calculated_number value)
break;
case "tc":
- json.charts[i].category = "QoS";
+ json.charts[i].category = "Quality of Service";
json.charts[i].categoryPriority = 30;
json.charts[i].glyphicon = "glyphicon-random";
json.charts[i].group = 15;
break;
case "ipvs":
- json.charts[i].category = "IPVS";
+ json.charts[i].category = "IP Virtual Server";
json.charts[i].categoryPriority = 40;
json.charts[i].glyphicon = "glyphicon-sort";
json.charts[i].group = 5;
break;
- case "conntrack":
+ case "netfilter":
json.charts[i].category = "Netfilter";
json.charts[i].categoryPriority = 50;
json.charts[i].glyphicon = "glyphicon-cloud";
json.charts[i].group = 5;
break;
+ case "nfsd":
+ json.charts[i].category = "NFS Server";
+ json.charts[i].categoryPriority = 100;
+ json.charts[i].glyphicon = "glyphicon-hdd";
+ json.charts[i].group = 5;
+ break;
+
+ case "nut":
+ json.charts[i].category = "UPS";
+ json.charts[i].categoryPriority = 110;
+ json.charts[i].glyphicon = "glyphicon-dashboard";
+ json.charts[i].group = 5;
+ break;
+
case "netdata":
json.charts[i].category = "NetData";
json.charts[i].categoryPriority = 3000;