src/web_client.h
src/web_server.c
src/web_server.h
- config.h)
+ config.h src/health.h src/health.c src/eval.h src/eval.c)
set(APPS_PLUGIN_SOURCE_FILES
src/appconfig.c
*
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include "dictionary.h"
-#include "main.h"
-#include "log.h"
#include "common.h"
struct myvalue {
/*
* compile with
- * gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o benchmark-registry benchmark-registry.c ../src/dictionary.o ../src/log.o ../src/avl.o ../src/common.o ../src/appconfig.o ../src/web_buffer.o ../src/storage_number.o ../src/rrd.o -pthread -luuid -lm -DHAVE_CONFIG_H -DVARLIB_DIR="\"/tmp\""
+ * gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o benchmark-registry benchmark-registry.c ../src/dictionary.o ../src/log.o ../src/avl.o ../src/common.o ../src/appconfig.o ../src/web_buffer.o ../src/storage_number.o ../src/rrd.o ../src/health.o -pthread -luuid -lm -DHAVE_CONFIG_H -DVARLIB_DIR="\"/tmp\""
*/
char *hostname = "me";
#include "../src/registry.c"
+void netdata_cleanup_and_exit(int ret) { exit(ret); }
+
// ----------------------------------------------------------------------------
// TESTS
--- /dev/null
+
+/*
+ * 1. build netdata (as normally)
+ * 2. cd profile/
+ * 3. compile with:
+ * gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o test-eval test-eval.c ../src/log.o ../src/eval.o ../src/common.o ../src/web_buffer.o ../src/storage_number.o -pthread -lm
+ *
+ */
+
+#include "common.h"
+
+void netdata_cleanup_and_exit(int ret) { exit(ret); }
+
+/*
+void indent(int level, int show) {
+ int i = level;
+ while(i--) printf(" | ");
+ if(show) printf(" \\_ ");
+ else printf(" \\_ ");
+}
+
+void print_node(EVAL_NODE *op, int level);
+
+void print_value(EVAL_VALUE *v, int level) {
+ indent(level, 0);
+
+ switch(v->type) {
+ case EVAL_VALUE_INVALID:
+ printf("value (NOP)\n");
+ break;
+
+ case EVAL_VALUE_NUMBER:
+ printf("value %Lf (NUMBER)\n", v->number);
+ break;
+
+ case EVAL_VALUE_EXPRESSION:
+ printf("value (SUB-EXPRESSION)\n");
+ print_node(v->expression, level+1);
+ break;
+
+ default:
+ printf("value (INVALID type %d)\n", v->type);
+ break;
+
+ }
+}
+
+void print_node(EVAL_NODE *op, int level) {
+
+// if(op->operator != EVAL_OPERATOR_NOP) {
+ indent(level, 1);
+ if(op->operator) printf("%c (node %d, precedence: %d)\n", op->operator, op->id, op->precedence);
+ else printf("NOP (node %d, precedence: %d)\n", op->id, op->precedence);
+// }
+
+ int i = op->count;
+ while(i--) print_value(&op->ops[i], level + 1);
+}
+
+calculated_number evaluate(EVAL_NODE *op, int depth);
+
+calculated_number evaluate_value(EVAL_VALUE *v, int depth) {
+ switch(v->type) {
+ case EVAL_VALUE_NUMBER:
+ return v->number;
+
+ case EVAL_VALUE_EXPRESSION:
+ return evaluate(v->expression, depth);
+
+ default:
+ fatal("I don't know how to handle EVAL_VALUE type %d", v->type);
+ }
+}
+
+void print_depth(int depth) {
+ static int count = 0;
+
+ printf("%d. ", ++count);
+ while(depth--) printf(" ");
+}
+
+calculated_number evaluate(EVAL_NODE *op, int depth) {
+ calculated_number n1, n2, r;
+
+ switch(op->operator) {
+ case EVAL_OPERATOR_SIGN_PLUS:
+ r = evaluate_value(&op->ops[0], depth);
+ break;
+
+ case EVAL_OPERATOR_SIGN_MINUS:
+ r = -evaluate_value(&op->ops[0], depth);
+ break;
+
+ case EVAL_OPERATOR_PLUS:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 + n2;
+ print_depth(depth);
+ printf("%Lf = %Lf + %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_MINUS:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 - n2;
+ print_depth(depth);
+ printf("%Lf = %Lf - %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_MULTIPLY:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 * n2;
+ print_depth(depth);
+ printf("%Lf = %Lf * %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_DIVIDE:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 / n2;
+ print_depth(depth);
+ printf("%Lf = %Lf / %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_NOT:
+ n1 = evaluate_value(&op->ops[0], depth);
+ r = !n1;
+ print_depth(depth);
+ printf("%Lf = NOT %Lf\n", r, n1);
+ break;
+
+ case EVAL_OPERATOR_AND:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 && n2;
+ print_depth(depth);
+ printf("%Lf = %Lf AND %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_OR:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 || n2;
+ print_depth(depth);
+ printf("%Lf = %Lf OR %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_GREATER_THAN_OR_EQUAL:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 >= n2;
+ print_depth(depth);
+ printf("%Lf = %Lf >= %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_LESS_THAN_OR_EQUAL:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 <= n2;
+ print_depth(depth);
+ printf("%Lf = %Lf <= %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_GREATER:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 > n2;
+ print_depth(depth);
+ printf("%Lf = %Lf > %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_LESS:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 < n2;
+ print_depth(depth);
+ printf("%Lf = %Lf < %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_NOT_EQUAL:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 != n2;
+ print_depth(depth);
+ printf("%Lf = %Lf <> %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_EQUAL:
+ if(op->count != 2)
+ fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+ n1 = evaluate_value(&op->ops[0], depth);
+ n2 = evaluate_value(&op->ops[1], depth);
+ r = n1 == n2;
+ print_depth(depth);
+ printf("%Lf = %Lf == %Lf\n", r, n1, n2);
+ break;
+
+ case EVAL_OPERATOR_EXPRESSION_OPEN:
+ printf("BEGIN SUB-EXPRESSION\n");
+ r = evaluate_value(&op->ops[0], depth + 1);
+ printf("END SUB-EXPRESSION\n");
+ break;
+
+ case EVAL_OPERATOR_NOP:
+ case EVAL_OPERATOR_VALUE:
+ r = evaluate_value(&op->ops[0], depth);
+ break;
+
+ default:
+ error("I don't know how to handle operator '%c'", op->operator);
+ r = 0;
+ break;
+ }
+
+ return r;
+}
+
+
+void print_expression(EVAL_NODE *op, const char *failed_at, int error) {
+ if(op) {
+ printf("expression tree:\n");
+ print_node(op, 0);
+
+ printf("\nevaluation steps:\n");
+ evaluate(op, 0);
+
+ int error;
+ calculated_number ret = expression_evaluate(op, &error);
+ printf("\ninternal evaluator:\nSTATUS: %d, RESULT = %Lf\n", error, ret);
+
+ expression_free(op);
+ }
+ else {
+ printf("error: %d, failed_at: '%s'\n", error, (failed_at)?failed_at:"<NONE>");
+ }
+}
+*/
+
+int main(int argc, char **argv) {
+ if(argc != 2) {
+ fprintf(stderr, "I need an epxression (enclose it in single-quotes (') as a single parameter)\n");
+ exit(1);
+ }
+
+ const char *failed_at = NULL;
+ int error;
+
+ EVAL_EXPRESSION *exp = expression_parse(argv[1], &failed_at, &error);
+ if(!exp)
+ printf("\nFAILED\nExpression: '%s'\nParsing stopped at: '%s'\nError code: %d (%s)\n", argv[1], (failed_at)?((*failed_at)?failed_at:"<END OF EXPRESSION>"):"<NONE>", error, expression_strerror(error));
+
+ else {
+ printf("\nOK\nExpression: '%s'\nParsed as : '%s'\nError code: %d (%s)\n", argv[1], exp->parsed_as, error, expression_strerror(error));
+
+ if(expression_evaluate(exp)) {
+ printf("\nEvaluates to: %Lf\n\n", exp->result);
+ }
+ else {
+ printf("\nEvaluation failed with code %d and message: %s\n\n", exp->error, buffer_tostring(exp->error_msg));
+ }
+ expression_free(exp);
+ }
+
+ return 0;
+}
common.c common.h \
daemon.c daemon.h \
dictionary.c dictionary.h \
+ eval.c eval.h \
global_statistics.c global_statistics.h \
+ health.c health.h \
log.c log.h \
main.c main.h \
plugin_checks.c plugin_checks.h \
-
-/*
- * TODO
- *
- * 1. Re-write this using DICTIONARY
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "avl.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
#define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
{
debug(D_CONFIG, "Creating section '%s'.", section);
- struct config *co = calloc(1, sizeof(struct config));
- if(!co) fatal("Cannot allocate config");
-
- co->name = strdup(section);
- if(!co->name) fatal("Cannot allocate config.name");
+ struct config *co = callocz(1, sizeof(struct config));
+ co->name = strdupz(section);
co->hash = simple_hash(co->name);
avl_init_lock(&co->values_index, config_value_compare);
{
debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
- struct config_value *cv = calloc(1, sizeof(struct config_value));
- if(!cv) fatal("Cannot allocate config_value");
-
- cv->name = strdup(name);
- if(!cv->name) fatal("Cannot allocate config.name");
+ struct config_value *cv = callocz(1, sizeof(struct config_value));
+ cv->name = strdupz(name);
cv->hash = simple_hash(cv->name);
-
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
+ cv->value = strdupz(value);
config_value_index_add(co, cv);
config_value_index_del(co, cv);
- free(cv->name);
- cv->name = strdup(new);
- if(!cv->name) fatal("Cannot allocate memory for config_rename()");
+ freez(cv->name);
+ cv->name = strdupz(new);
cv->hash = simple_hash(cv->name);
if(strcmp(cv->value, value) != 0) {
cv->flags |= CONFIG_VALUE_CHANGED;
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
+ freez(cv->value);
+ cv->value = strdupz(value);
}
return cv->value;
if(strcmp(cv->value, value) != 0) {
cv->flags |= CONFIG_VALUE_CHANGED;
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
+ freez(cv->value);
+ cv->value = strdupz(value);
}
return value;
else {
if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
- free(cv->value);
- cv->value = strdup(value);
- if(!cv->value) fatal("Cannot allocate config.value");
+ freez(cv->value);
+ cv->value = strdupz(value);
}
else
debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
-#include "web_buffer.h"
-
#ifndef NETDATA_CONFIG_H
#define NETDATA_CONFIG_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include <sys/resource.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <locale.h>
-#include <ctype.h>
-#include <fcntl.h>
-
-#include <malloc.h>
-#include <dirent.h>
-#include <arpa/inet.h>
-
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "avl.h"
-
#include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
-#include "web_buffer.h"
-
-#ifdef NETDATA_INTERNAL_CHECKS
-#include <sys/prctl.h>
-#endif
#define MAX_COMPARE_NAME 100
#define MAX_NAME 100
for(w = users_root_target ; w ; w = w->next)
if(w->uid == uid) return w;
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
-
+ w = callocz(sizeof(struct target), 1);
snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
w->comparehash = simple_hash(w->compare);
w->comparelen = strlen(w->compare);
for(w = groups_root_target ; w ; w = w->next)
if(w->gid == gid) return w;
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
-
+ w = callocz(sizeof(struct target), 1);
snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
w->comparehash = simple_hash(w->compare);
w->comparelen = strlen(w->compare);
// find or create a new target
// there are targets that are just aggregated to other target (the second argument)
-struct target *get_apps_groups_target(const char *id, struct target *target)
-{
+struct target *get_apps_groups_target(const char *id, struct target *target) {
int tdebug = 0, thidden = 0, ends_with = 0;
const char *nid = id;
last = w;
}
- w = calloc(sizeof(struct target), 1);
- if(unlikely(!w)) {
- error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
- return NULL;
- }
-
+ w = callocz(sizeof(struct target), 1);
strncpyz(w->id, nid, MAX_NAME);
w->idhash = simple_hash(w->id);
strncpyz(w->name, nid, MAX_NAME);
strncpyz(w->compare, nid, MAX_COMPARE_NAME);
- int len = strlen(w->compare);
+ size_t len = strlen(w->compare);
if(w->compare[len - 1] == '*') {
w->compare[len - 1] = '\0';
w->starts_with = 1;
return all_pids[pid];
}
- all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
- if(!all_pids[pid]) {
- error("Cannot allocate %zu bytes of memory", (size_t)sizeof(struct pid_stat));
- return NULL;
- }
-
- all_pids[pid]->fds = calloc(sizeof(int), 100);
- if(!all_pids[pid]->fds)
- error("Cannot allocate %zu bytes of memory", (size_t)(sizeof(int) * 100));
- else all_pids[pid]->fds_size = 100;
+ all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
+ all_pids[pid]->fds = callocz(sizeof(int), 100);
+ all_pids[pid]->fds_size = 100;
if(root_of_pids) root_of_pids->prev = all_pids[pid];
all_pids[pid]->next = root_of_pids;
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(all_pids[pid]->fds) free(all_pids[pid]->fds);
- if(all_pids[pid]->stat_filename) free(all_pids[pid]->stat_filename);
- if(all_pids[pid]->statm_filename) free(all_pids[pid]->statm_filename);
- if(all_pids[pid]->io_filename) free(all_pids[pid]->io_filename);
- if(all_pids[pid]->cmdline_filename) free(all_pids[pid]->cmdline_filename);
- free(all_pids[pid]);
+ if(all_pids[pid]->fds) freez(all_pids[pid]->fds);
+ if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename);
+ if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename);
+ if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename);
+ if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename);
+ freez(all_pids[pid]);
all_pids[pid] = NULL;
all_pids_count--;
if(unlikely(!p->cmdline_filename)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
- if(!(p->cmdline_filename = strdup(filename)))
- fatal("Cannot allocate memory for filename '%s'", filename);
+ p->cmdline_filename = strdupz(filename);
}
int fd = open(p->cmdline_filename, O_RDONLY, 0666);
if(unlikely(fd == -1)) goto cleanup;
- int i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
+ ssize_t i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
close(fd);
if(unlikely(bytes <= 0)) goto cleanup;
if(unlikely(!p->stat_filename)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
- if(!(p->stat_filename = strdup(filename)))
- fatal("Cannot allocate memory for filename '%s'", filename);
+ p->stat_filename = strdupz(filename);
}
int set_quotes = (!ff)?1:0;
if(unlikely(!p->statm_filename)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
- if(!(p->statm_filename = strdup(filename)))
- fatal("Cannot allocate memory for filename '%s'", filename);
+ p->statm_filename = strdupz(filename);
}
ff = procfile_reopen(ff, p->statm_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
if(unlikely(!p->io_filename)) {
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
- if(!(p->io_filename = strdup(filename)))
- fatal("Cannot allocate memory for filename '%s'", filename);
+ p->io_filename = strdupz(filename);
}
// open the file
if(unlikely(debug))
fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
- all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
+ all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
// if the address changed, we have to rebuild the index
// since all pointers are now invalid
if(unlikely(debug))
fprintf(stderr, "apps.plugin: >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
- if(all_files[c].name) free((void *)all_files[c].name);
+ if(all_files[c].name) freez((void *)all_files[c].name);
all_files[c].name = NULL;
last_pos = c;
break;
type = FILETYPE_OTHER;
}
- all_files[c].name = strdup(name);
+ all_files[c].name = strdupz(name);
all_files[c].hash = hash;
all_files[c].type = type;
all_files[c].pos = c;
if(unlikely(debug))
fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
- p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
+ p->fds = reallocz(p->fds, (fdid + 100) * sizeof(int));
if(!p->fds) {
fatal("Cannot re-allocate fds for %s", p->comm);
break;
for (w = root; w ; w = w->next) {
count++;
- if(w->fds) free(w->fds);
+ if(w->fds) freez(w->fds);
w->fds = NULL;
w->minflt = 0;
void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
(void)o;
- if(unlikely(!w->fds)) {
- w->fds = calloc(sizeof(int), (size_t) all_files_size);
- if(unlikely(!w->fds))
- error("Cannot allocate memory for fds in %s", w->name);
- }
+ if(unlikely(!w->fds))
+ w->fds = callocz(sizeof(int), (size_t) all_files_size);
if(likely(p->updated)) {
w->cutime += p->cutime;
}
}
- free(w->fds);
+ freez(w->fds);
w->fds = NULL;
}
}
parse_args(argc, argv);
- all_pids_sortlist = calloc(sizeof(pid_t), (size_t)pid_max);
- if(!all_pids_sortlist) {
- error("Cannot allocate %zu bytes of memory.", sizeof(pid_t) * pid_max);
- printf("DISABLE\n");
- exit(1);
- }
-
- all_pids = calloc(sizeof(struct pid_stat *), (size_t) pid_max);
- if(!all_pids) {
- error("Cannot allocate %zu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
- printf("DISABLE\n");
- exit(1);
- }
+ all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
+ all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
output = buffer_create(1024);
- if(!output)
- fatal("Cannot create BUFFER.");
-
buffer_sprintf(output,
"CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
"DIMENSION user '' incremental 1 1000\n"
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-// #include <assert.h>
-
-#include "avl.h"
-#include "log.h"
+#include "common.h"
/* ------------------------------------------------------------------------- */
/*
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/syscall.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <time.h>
-
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "../config.h"
char *global_host_prefix = "";
int enable_ksm = 1;
+volatile sig_atomic_t netdata_exit = 0;
+
+// ----------------------------------------------------------------------------
+// memory allocation functions that handle failures
+
+// although netdata does not use memory allocations too often (netdata tries to
+// maintain its memory footprint stable during runtime, i.e. all buffers are
+// allocated during initialization and are adapted to current use throughout
+// its lifetime), these can be used to override the default system allocation
+// routines.
+
+char *strdupz(const char *s) {
+ char *t = strdup(s);
+ if(unlikely(!t)) fatal("Cannot strdup() string '%s'", s);
+ return t;
+}
+
+void *mallocz(size_t size) {
+ void *p = malloc(size);
+ if(unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", size);
+ return p;
+}
+
+void *callocz(size_t nmemb, size_t size) {
+ void *p = calloc(nmemb, size);
+ if(unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", nmemb * size);
+ return p;
+}
+
+void *reallocz(void *ptr, size_t size) {
+ void *p = realloc(ptr, size);
+ if(unlikely(!p)) fatal("Cannot re-allocate memory to %zu bytes.", size);
+ return p;
+}
+
+void freez(void *ptr) {
+ free(ptr);
+}
+
+// ----------------------------------------------------------------------------
+
// time(NULL) in milliseconds
unsigned long long timems(void) {
struct timeval now;
#ifndef NETDATA_WITH_USLEEP
// we expect microseconds (1.000.000 per second)
// but timespec is nanoseconds (1.000.000.000 per second)
- struct timespec req = { .tv_sec = usec / 1000000, .tv_nsec = (usec % 1000000) * 1000 }, rem;
+ struct timespec rem, req = {
+ .tv_sec = (time_t)(usec / 1000000),
+ .tv_nsec = (suseconds_t)((usec % 1000000) * 1000)
+ };
while(nanosleep(&req, &rem) == -1) {
if(likely(errno == EINTR)) {
+#ifndef NETDATA_COMMON_H
+#define NETDATA_COMMON_H 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
#include <stdarg.h>
-#include <sys/time.h>
+#include <stddef.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <locale.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
-#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
-#ifndef NETDATA_COMMON_H
-#define NETDATA_COMMON_H 1
+#ifdef STORAGE_WITH_MATH
+#include <math.h>
+#endif
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif
-#include <sys/types.h>
-#include <unistd.h>
+#ifdef NETDATA_WITH_ZLIB
+#include <zlib.h>
+#endif
+
+#include "avl.h"
+#include "log.h"
+#include "global_statistics.h"
+#include "storage_number.h"
+#include "web_buffer.h"
+#include "web_buffer_svg.h"
+#include "url.h"
+#include "popen.h"
+
+#include "procfile.h"
+#include "appconfig.h"
+#include "dictionary.h"
+#include "proc_self_mountinfo.h"
+#include "plugin_checks.h"
+#include "plugin_idlejitter.h"
+#include "plugin_nfacct.h"
+#include "plugin_proc.h"
+#include "plugin_tc.h"
+#include "plugins_d.h"
+
+#include "eval.h"
+#include "health.h"
+
+#include "rrd.h"
+#include "rrd2json.h"
+
+#include "web_client.h"
+#include "web_server.h"
+
+#include "registry.h"
+#include "daemon.h"
+#include "main.h"
+#include "unit_test.h"
+
+#if __GNUC__
+#if __x86_64__ || __ppc64__
+#define ENVIRONMENT64
+#else
+#define ENVIRONMENT32
+#endif
+#endif
+
+#ifdef abs
+#undef abs
+#endif
#define abs(x) ((x < 0)? -x : x)
+
#define usecdiff(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
extern void netdata_fix_chart_id(char *s);
extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
extern int snprintfz(char *dst, size_t n, const char *fmt, ...) __attribute__ (( format (printf, 3, 4)));
+// memory allocation functions that handle failures
+extern char *strdupz(const char *s);
+extern void *callocz(size_t nmemb, size_t size);
+extern void *mallocz(size_t size);
+extern void freez(void *ptr);
+extern void *reallocz(void *ptr, size_t size);
+
extern void *mymmap(const char *filename, size_t size, int flags, int ksm);
extern int savememory(const char *filename, void *mem, size_t size);
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-#include <pthread.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "web_client.h"
-#include "plugins_d.h"
-#include "rrd.h"
-#include "popen.h"
-#include "main.h"
-#include "daemon.h"
char pidfile[FILENAME_MAX + 1] = "";
uid_t uid = pw->pw_uid;
gid_t gid = pw->pw_gid;
- int ngroups = sysconf(_SC_NGROUPS_MAX);
+ int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
gid_t *supplementary_groups = NULL;
if(ngroups) {
- supplementary_groups = malloc(sizeof(gid_t) * ngroups);
- if(supplementary_groups) {
- if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
- error("Cannot get supplementary groups of user '%s'.", username);
- free(supplementary_groups);
- supplementary_groups = NULL;
- ngroups = 0;
- }
+ supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
+ if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
+ error("Cannot get supplementary groups of user '%s'.", username);
+ freez(supplementary_groups);
+ supplementary_groups = NULL;
+ ngroups = 0;
}
- else fatal("Cannot allocate memory for %d supplementary groups", ngroups);
}
properly_chown_netdata_generated_file(access_fd, uid, gid);
if(setgroups(ngroups, supplementary_groups) == -1)
error("Cannot set supplementary groups for user '%s'", username);
- free(supplementary_groups);
+ freez(supplementary_groups);
supplementary_groups = NULL;
ngroups = 0;
}
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "avl.h"
#include "common.h"
-#include "log.h"
-
-#include "dictionary.h"
// ----------------------------------------------------------------------------
// dictionary statistics
static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
- NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
- if(unlikely(!nv)) fatal("Cannot allocate name_value of size %zu", sizeof(NAME_VALUE));
+ NAME_VALUE *nv = callocz(1, sizeof(NAME_VALUE));
if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
nv->name = (char *)name;
else {
- nv->name = strdup(name);
- if (unlikely(!nv->name))
- fatal("Cannot allocate name_value.name of size %zu", strlen(name));
+ nv->name = strdupz(name);
}
nv->hash = (hash)?hash:simple_hash(nv->name);
if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
nv->value = value;
else {
- nv->value = malloc(value_len);
- if (unlikely(!nv->value))
- fatal("Cannot allocate name_value.value of size %zu", value_len);
-
+ nv->value = mallocz(value_len);
memcpy(nv->value, value, value_len);
}
if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
- free(nv->value);
+ freez(nv->value);
}
if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
- free(nv->name);
+ freez(nv->name);
}
- free(nv);
+ freez(nv);
}
// ----------------------------------------------------------------------------
// API - basic methods
-DICTIONARY *dictionary_create(uint32_t flags) {
+DICTIONARY *dictionary_create(uint8_t flags) {
debug(D_DICTIONARY, "Creating dictionary.");
- DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
- if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
+ DICTIONARY *dict = callocz(1, sizeof(DICTIONARY));
- if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
- dict->stats = calloc(1, sizeof(struct dictionary_stats));
- if(!dict->stats) fatal("Cannot allocate statistics for DICTIONARY");
- }
+ if(flags & DICTIONARY_FLAG_WITH_STATISTICS)
+ dict->stats = callocz(1, sizeof(struct dictionary_stats));
if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
- dict->rwlock = calloc(1, sizeof(pthread_rwlock_t));
- if(!dict->rwlock) fatal("Cannot allocate pthread_rwlock_t for DICTIONARY");
+ dict->rwlock = callocz(1, sizeof(pthread_rwlock_t));
pthread_rwlock_init(dict->rwlock, NULL);
}
dictionary_unlock(dict);
if(dict->stats)
- free(dict->stats);
+ freez(dict->stats);
if(dict->rwlock)
- free(dict->rwlock);
+ freez(dict->rwlock);
- free(dict);
+ freez(dict);
}
// ----------------------------------------------------------------------------
// copy the new value without breaking
// any other thread accessing the same entry
- void *new = malloc(value_len),
+ void *new = mallocz(value_len),
*old = nv->value;
- if(unlikely(!new))
- fatal("Cannot allocate value of size %zu", value_len);
-
memcpy(new, value, value_len);
nv->value = new;
debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
- free(old);
+ freez(old);
}
}
-#include <pthread.h>
-
-#include "web_buffer.h"
-#include "avl.h"
-
#ifndef NETDATA_DICTIONARY_H
#define NETDATA_DICTIONARY_H 1
#define DICTIONARY_FLAG_NAME_LINK_DONT_CLONE 0x00000004
#define DICTIONARY_FLAG_WITH_STATISTICS 0x00000008
-extern DICTIONARY *dictionary_create(uint32_t flags);
+extern DICTIONARY *dictionary_create(uint8_t flags);
extern void dictionary_destroy(DICTIONARY *dict);
extern void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len);
extern void *dictionary_get(DICTIONARY *dict, const char *name);
extern int dictionary_del(DICTIONARY *dict, const char *name);
-extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data);
+extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *d), void *data);
#endif /* NETDATA_DICTIONARY_H */
--- /dev/null
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// data structures for storing the parsed expression in memory
+
+typedef struct eval_value {
+ int type;
+
+ union {
+ calculated_number number;
+ EVAL_VARIABLE *variable;
+ struct eval_node *expression;
+ };
+} EVAL_VALUE;
+
+typedef struct eval_node {
+ int id;
+ unsigned char operator;
+ int precedence;
+
+ int count;
+ EVAL_VALUE ops[];
+} EVAL_NODE;
+
+// these are used for EVAL_NODE.operator
+// they are used as internal IDs to identify an operator
+// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
+#define EVAL_OPERATOR_NOP '\0'
+#define EVAL_OPERATOR_VALUE ':'
+#define EVAL_OPERATOR_EXPRESSION_OPEN '('
+#define EVAL_OPERATOR_EXPRESSION_CLOSE ')'
+#define EVAL_OPERATOR_NOT '!'
+#define EVAL_OPERATOR_PLUS '+'
+#define EVAL_OPERATOR_MINUS '-'
+#define EVAL_OPERATOR_AND '&'
+#define EVAL_OPERATOR_OR '|'
+#define EVAL_OPERATOR_GREATER_THAN_OR_EQUAL 'G'
+#define EVAL_OPERATOR_LESS_THAN_OR_EQUAL 'L'
+#define EVAL_OPERATOR_NOT_EQUAL '~'
+#define EVAL_OPERATOR_EQUAL '='
+#define EVAL_OPERATOR_LESS '<'
+#define EVAL_OPERATOR_GREATER '>'
+#define EVAL_OPERATOR_MULTIPLY '*'
+#define EVAL_OPERATOR_DIVIDE '/'
+#define EVAL_OPERATOR_SIGN_PLUS 'P'
+#define EVAL_OPERATOR_SIGN_MINUS 'M'
+
+// ----------------------------------------------------------------------------
+// forward function definitions
+
+static inline void eval_node_free(EVAL_NODE *op);
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
+
+// ----------------------------------------------------------------------------
+// evaluation of expressions
+
+static inline calculated_number eval_check_number(calculated_number n, int *error) {
+ if(unlikely(isnan(n))) {
+ *error = EVAL_ERROR_VALUE_IS_NAN;
+ return 0;
+ }
+
+ if(unlikely(isinf(n))) {
+ *error = EVAL_ERROR_VALUE_IS_INFINITE;
+ return 0;
+ }
+
+ return n;
+}
+
+static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
+ // FIXME: do the variable look up here
+
+// if(!exp->data) {
+ *error = EVAL_ERROR_UNKNOWN_VARIABLE;
+ buffer_sprintf(exp->error_msg, "unknown variable '%s'", v->name);
+// }
+
+ return 0;
+}
+
+static inline calculated_number eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
+ calculated_number n;
+
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ n = eval_node(exp, v->expression, error);
+ break;
+
+ case EVAL_VALUE_NUMBER:
+ n = v->number;
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ n = eval_variable(exp, v->variable, error);
+ break;
+
+ default:
+ *error = EVAL_ERROR_INVALID_VALUE;
+ n = 0;
+ break;
+ }
+
+ return eval_check_number(n, error);
+}
+
+calculated_number eval_and(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) && eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_or(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) || eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_greater_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) >= eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_less_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) <= eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_not_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) != eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) == eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_less(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) < eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_greater(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) > eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) + eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) - eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_multiply(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) * eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_divide(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error) / eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_nop(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_not(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return !eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_sign_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ return -eval_value(exp, &op->ops[0], error);
+}
+
+static struct operator {
+ const char *print_as;
+ char precedence;
+ char parameters;
+ calculated_number (*eval)(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+} operators[256] = {
+ // this is a random access array
+ // we always access it with a known EVAL_OPERATOR_X
+
+ [EVAL_OPERATOR_AND] = { "&&", 2, 2, eval_and },
+ [EVAL_OPERATOR_OR] = { "||", 2, 2, eval_or },
+ [EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, eval_greater_than_or_equal },
+ [EVAL_OPERATOR_LESS_THAN_OR_EQUAL] = { "<=", 3, 2, eval_less_than_or_equal },
+ [EVAL_OPERATOR_NOT_EQUAL] = { "!=", 3, 2, eval_not_equal },
+ [EVAL_OPERATOR_EQUAL] = { "==", 3, 2, eval_equal },
+ [EVAL_OPERATOR_LESS] = { "<", 3, 2, eval_less },
+ [EVAL_OPERATOR_GREATER] = { ">", 3, 2, eval_greater },
+ [EVAL_OPERATOR_PLUS] = { "+", 4, 2, eval_plus },
+ [EVAL_OPERATOR_MINUS] = { "-", 4, 2, eval_minus },
+ [EVAL_OPERATOR_MULTIPLY] = { "*", 5, 2, eval_multiply },
+ [EVAL_OPERATOR_DIVIDE] = { "/", 5, 2, eval_divide },
+ [EVAL_OPERATOR_NOT] = { "!", 6, 1, eval_not },
+ [EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, eval_sign_plus },
+ [EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, eval_sign_minus },
+ [EVAL_OPERATOR_NOP] = { NULL, 7, 1, eval_nop },
+ [EVAL_OPERATOR_VALUE] = { NULL, 7, 1, eval_nop },
+ [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 7, 1, eval_nop },
+
+ // this should exist in our evaluation list
+ [EVAL_OPERATOR_EXPRESSION_CLOSE] = { NULL, 7, 1, eval_nop }
+};
+
+#define eval_precedence(operator) (operators[(unsigned char)(operator)].precedence)
+
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+ if(unlikely(op->count != operators[op->operator].parameters)) {
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+ return 0;
+ }
+
+ calculated_number n = operators[op->operator].eval(exp, op, error);
+
+ return eval_check_number(n, error);
+}
+
+// ----------------------------------------------------------------------------
+// parsed-as generation
+
+static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error) {
+ (void)error;
+ buffer_sprintf(out, "$%s", v->name);
+}
+
+static inline void print_parsed_as_constant(BUFFER *out, calculated_number n) {
+ char b[100+1], *s;
+ snprintfz(b, 100, CALCULATED_NUMBER_FORMAT, n);
+
+ s = &b[strlen(b) - 1];
+ while(s > b && *s == '0') {
+ *s ='\0';
+ s--;
+ }
+
+ if(s > b && *s == '.')
+ *s = '\0';
+
+ buffer_strcat(out, b);
+}
+
+static inline void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ print_parsed_as_node(out, v->expression, error);
+ break;
+
+ case EVAL_VALUE_NUMBER:
+ print_parsed_as_constant(out, v->number);
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ print_parsed_as_variable(out, v->variable, error);
+ break;
+
+ default:
+ *error = EVAL_ERROR_INVALID_VALUE;
+ break;
+ }
+}
+
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
+ if(unlikely(op->count != operators[op->operator].parameters)) {
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+ return;
+ }
+
+ if(operators[op->operator].parameters == 1) {
+
+ if(operators[op->operator].print_as)
+ buffer_sprintf(out, "%s", operators[op->operator].print_as);
+
+ //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+ // buffer_strcat(out, "(");
+
+ print_parsed_as_value(out, &op->ops[0], error);
+
+ //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+ // buffer_strcat(out, ")");
+ }
+
+ else if(operators[op->operator].parameters == 2) {
+ buffer_strcat(out, "(");
+ print_parsed_as_value(out, &op->ops[0], error);
+
+ if(operators[op->operator].print_as)
+ buffer_sprintf(out, " %s ", operators[op->operator].print_as);
+
+ print_parsed_as_value(out, &op->ops[1], error);
+ buffer_strcat(out, ")");
+ }
+}
+
+// ----------------------------------------------------------------------------
+// parsing expressions
+
+// skip spaces
+static inline void skip_spaces(const char **string) {
+ const char *s = *string;
+ while(isspace(*s)) s++;
+ *string = s;
+}
+
+// what character can appear just after an operator keyword
+// like NOT AND OR ?
+static inline int isoperatorterm_word(const char s) {
+ if(isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s) || !s)
+ return 1;
+
+ return 0;
+}
+
+// what character can appear just after an operator symbol?
+static inline int isoperatorterm_symbol(const char s) {
+ if(isoperatorterm_word(s) || isalpha(s)) return 1;
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+// parse operators
+
+static inline int parse_and(const char **string) {
+ const char *s = *string;
+
+ // AND
+ if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') && isoperatorterm_word(s[3])) {
+ *string = &s[4];
+ return 1;
+ }
+
+ // &&
+ if(s[0] == '&' && s[1] == '&' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_or(const char **string) {
+ const char *s = *string;
+
+ // OR
+ if((s[0] == 'O' || s[0] == 'o') && (s[1] == 'R' || s[1] == 'r') && isoperatorterm_word(s[2])) {
+ *string = &s[3];
+ return 1;
+ }
+
+ // ||
+ if(s[0] == '|' && s[1] == '|' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_greater_than_or_equal(const char **string) {
+ const char *s = *string;
+
+ // >=
+ if(s[0] == '>' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_less_than_or_equal(const char **string) {
+ const char *s = *string;
+
+ // <=
+ if (s[0] == '<' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_greater(const char **string) {
+ const char *s = *string;
+
+ // >
+ if(s[0] == '>' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_less(const char **string) {
+ const char *s = *string;
+
+ // <
+ if(s[0] == '<' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_equal(const char **string) {
+ const char *s = *string;
+
+ // ==
+ if(s[0] == '=' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ // =
+ if(s[0] == '=' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_not_equal(const char **string) {
+ const char *s = *string;
+
+ // !=
+ if(s[0] == '!' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ return 1;
+ }
+
+ // <>
+ if(s[0] == '<' && s[1] == '>' && isoperatorterm_symbol(s[2])) {
+ *string = &s[2];
+ }
+
+ return 0;
+}
+
+static inline int parse_not(const char **string) {
+ const char *s = *string;
+
+ // NOT
+ if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') && isoperatorterm_word(s[3])) {
+ *string = &s[3];
+ return 1;
+ }
+
+ if(s[0] == '!') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_multiply(const char **string) {
+ const char *s = *string;
+
+ // *
+ if(s[0] == '*' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_divide(const char **string) {
+ const char *s = *string;
+
+ // /
+ if(s[0] == '/' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_minus(const char **string) {
+ const char *s = *string;
+
+ // -
+ if(s[0] == '-' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_plus(const char **string) {
+ const char *s = *string;
+
+ // +
+ if(s[0] == '+' && isoperatorterm_symbol(s[1])) {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_open_subexpression(const char **string) {
+ const char *s = *string;
+
+ // (
+ if(s[0] == '(') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_close_subexpression(const char **string) {
+ const char *s = *string;
+
+ // (
+ if(s[0] == ')') {
+ *string = &s[1];
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline int parse_constant(const char **string, calculated_number *number) {
+ char *end = NULL;
+ calculated_number n = strtold(*string, &end);
+ if(unlikely(!end || *string == end || isnan(n) || isinf(n))) {
+ *number = 0;
+ return 0;
+ }
+ *number = n;
+ *string = end;
+ return 1;
+}
+
+static struct operator_parser {
+ unsigned char id;
+ int (*parse)(const char **);
+} operator_parsers[] = {
+ // the order in this list is important!
+ // the first matching will be used
+ // so place the longer of overlapping ones
+ // at the top
+
+ { EVAL_OPERATOR_AND, parse_and },
+ { EVAL_OPERATOR_OR, parse_or },
+ { EVAL_OPERATOR_GREATER_THAN_OR_EQUAL, parse_greater_than_or_equal },
+ { EVAL_OPERATOR_LESS_THAN_OR_EQUAL, parse_less_than_or_equal },
+ { EVAL_OPERATOR_NOT_EQUAL, parse_not_equal },
+ { EVAL_OPERATOR_EQUAL, parse_equal },
+ { EVAL_OPERATOR_LESS, parse_less },
+ { EVAL_OPERATOR_GREATER, parse_greater },
+ { EVAL_OPERATOR_PLUS, parse_plus },
+ { EVAL_OPERATOR_MINUS, parse_minus },
+ { EVAL_OPERATOR_MULTIPLY, parse_multiply },
+ { EVAL_OPERATOR_DIVIDE, parse_divide },
+
+ /* we should not put in this list the following:
+ *
+ * - NOT
+ * - (
+ * - )
+ *
+ * these are handled in code
+ */
+
+ // termination
+ { EVAL_OPERATOR_NOP, NULL }
+};
+
+static inline unsigned char parse_operator(const char **string, int *precedence) {
+ skip_spaces(string);
+
+ int i;
+ for(i = 0 ; operator_parsers[i].parse != NULL ; i++)
+ if(operator_parsers[i].parse(string)) {
+ if(precedence) *precedence = eval_precedence(operator_parsers[i].id);
+ return operator_parsers[i].id;
+ }
+
+ return EVAL_OPERATOR_NOP;
+}
+
+// ----------------------------------------------------------------------------
+// memory management
+
+static inline EVAL_NODE *eval_node_alloc(int count) {
+ static int id = 1;
+
+ EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
+
+ op->id = id++;
+ op->operator = EVAL_OPERATOR_NOP;
+ op->precedence = eval_precedence(EVAL_OPERATOR_NOP);
+ op->count = count;
+ return op;
+}
+
+static inline void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
+ if(pos >= op->count)
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+ op->ops[pos].type = EVAL_VALUE_EXPRESSION;
+ op->ops[pos].expression = value;
+}
+
+static inline void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, calculated_number value) {
+ if(pos >= op->count)
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+ op->ops[pos].type = EVAL_VALUE_NUMBER;
+ op->ops[pos].number = value;
+}
+
+static inline void eval_variable_free(EVAL_VARIABLE *v) {
+ freez(v);
+}
+
+static inline void eval_value_free(EVAL_VALUE *v) {
+ switch(v->type) {
+ case EVAL_VALUE_EXPRESSION:
+ eval_node_free(v->expression);
+ break;
+
+ case EVAL_VALUE_VARIABLE:
+ eval_variable_free(v->variable);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static inline void eval_node_free(EVAL_NODE *op) {
+ if(op->count) {
+ int i;
+ for(i = op->count - 1; i >= 0 ;i--)
+ eval_value_free(&op->ops[i]);
+ }
+
+ freez(op);
+}
+
+// ----------------------------------------------------------------------------
+// the parsing logic
+
+// helper function to avoid allocations all over the place
+static inline EVAL_NODE *parse_next_operand_given_its_operator(const char **string, unsigned char operator_type, int *error) {
+ EVAL_NODE *sub = parse_one_full_operand(string, error);
+ if(!sub) return NULL;
+
+ EVAL_NODE *op = eval_node_alloc(1);
+ op->operator = operator_type;
+ eval_node_set_value_to_node(op, 0, sub);
+ return op;
+}
+
+// parse a full operand, including its sign or other associative operator (e.g. NOT)
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error) {
+ EVAL_NODE *op1 = NULL;
+ calculated_number number;
+
+ *error = EVAL_ERROR_OK;
+
+ skip_spaces(string);
+ if(!(**string)) {
+ *error = EVAL_ERROR_MISSING_OPERAND;
+ return NULL;
+ }
+
+ if(parse_not(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_NOT, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
+ }
+ else if(parse_plus(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_PLUS, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
+ }
+ else if(parse_minus(string)) {
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_MINUS, error);
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
+ }
+ else if(parse_open_subexpression(string)) {
+ EVAL_NODE *sub = parse_full_expression(string, error);
+ if(sub) {
+ op1 = eval_node_alloc(1);
+ op1->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
+ op1->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
+ eval_node_set_value_to_node(op1, 0, sub);
+ if(!parse_close_subexpression(string)) {
+ *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
+ eval_node_free(op1);
+ return NULL;
+ }
+ }
+ }
+// else if(parse_variable(string)) {
+//
+// }
+ else if(parse_constant(string, &number)) {
+ op1 = eval_node_alloc(1);
+ op1->operator = EVAL_OPERATOR_VALUE;
+ eval_node_set_value_to_constant(op1, 0, number);
+ }
+ else if(**string)
+ *error = EVAL_ERROR_UNKNOWN_OPERAND;
+ else
+ *error = EVAL_ERROR_MISSING_OPERAND;
+
+ return op1;
+}
+
+// parse an operator and the rest of the expression
+// precedence processing is handled here
+static inline EVAL_NODE *parse_rest_of_expression(const char **string, int *error, EVAL_NODE *op1) {
+ EVAL_NODE *op2 = NULL;
+ unsigned char operator;
+ int precedence;
+
+ operator = parse_operator(string, &precedence);
+ skip_spaces(string);
+
+ if(operator != EVAL_OPERATOR_NOP) {
+ op2 = parse_one_full_operand(string, error);
+ if(!op2) {
+ // error is already reported
+ eval_node_free(op1);
+ return NULL;
+ }
+
+ EVAL_NODE *op = eval_node_alloc(2);
+ op->operator = operator;
+ op->precedence = precedence;
+
+ eval_node_set_value_to_node(op, 1, op2);
+
+ // precedence processing
+ // if this operator has a higher precedence compared to its next
+ // put the next operator on top of us (top = evaluated later)
+ // function recursion does the rest...
+ if(op->precedence > op1->precedence && op1->count == 2 && op1->operator != '(' && op1->ops[1].type == EVAL_VALUE_EXPRESSION) {
+ eval_node_set_value_to_node(op, 0, op1->ops[1].expression);
+ op1->ops[1].expression = op;
+ op = op1;
+ }
+ else
+ eval_node_set_value_to_node(op, 0, op1);
+
+ return parse_rest_of_expression(string, error, op);
+ }
+ else if(**string == ')') {
+ ;
+ }
+ else if(**string) {
+ if(op1) eval_node_free(op1);
+ op1 = NULL;
+ *error = EVAL_ERROR_MISSING_OPERATOR;
+ }
+
+ return op1;
+}
+
+// high level function to parse an expression or a sub-expression
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error) {
+ EVAL_NODE *op1 = NULL;
+
+ op1 = parse_one_full_operand(string, error);
+ if(!op1) {
+ *error = EVAL_ERROR_MISSING_OPERAND;
+ return NULL;
+ }
+
+ return parse_rest_of_expression(string, error, op1);
+}
+
+// ----------------------------------------------------------------------------
+// public API
+
+int expression_evaluate(EVAL_EXPRESSION *exp) {
+ exp->error = EVAL_ERROR_OK;
+
+ buffer_reset(exp->error_msg);
+ exp->result = eval_node(exp, (EVAL_NODE *)exp->nodes, &exp->error);
+
+ if(exp->error != EVAL_ERROR_OK) {
+ if(buffer_strlen(exp->error_msg))
+ buffer_strcat(exp->error_msg, "; ");
+
+ buffer_sprintf(exp->error_msg, "failed to evaluate expression with error %d (%s)", exp->error, expression_strerror(exp->error));
+ return 0;
+ }
+
+ return 1;
+}
+
+EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
+ const char *s;
+ int err = EVAL_ERROR_OK;
+ unsigned long pos = 0;
+
+ s = string;
+ EVAL_NODE *op = parse_full_expression(&s, &err);
+
+ if(*s) {
+ if(op) {
+ eval_node_free(op);
+ op = NULL;
+ }
+ err = EVAL_ERROR_REMAINING_GARBAGE;
+ }
+
+ if (failed_at) *failed_at = s;
+ if (error) *error = err;
+
+ if(!op) {
+ pos = s - string + 1;
+ error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s);
+ return NULL;
+ }
+
+ BUFFER *out = buffer_create(1024);
+ print_parsed_as_node(out, op, &err);
+ if(err != EVAL_ERROR_OK) {
+ error("failed to re-generate expression '%s' with reason: %s", string, expression_strerror(err));
+ eval_node_free(op);
+ buffer_free(out);
+ return NULL;
+ }
+
+ EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
+
+ exp->parsed_as = strdupz(buffer_tostring(out));
+ buffer_free(out);
+
+ exp->error_msg = buffer_create(100);
+ exp->nodes = (void *)op;
+
+ return exp;
+}
+
+void expression_free(EVAL_EXPRESSION *exp) {
+ if(!exp) return;
+
+ if(exp->nodes) eval_node_free((EVAL_NODE *)exp->nodes);
+ freez((void *)exp->source);
+ freez((void *)exp->parsed_as);
+ buffer_free(exp->error_msg);
+ freez(exp);
+}
+
+const char *expression_strerror(int error) {
+ switch(error) {
+ case EVAL_ERROR_OK:
+ return "success";
+
+ case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
+ return "missing closing parenthesis";
+
+ case EVAL_ERROR_UNKNOWN_OPERAND:
+ return "unknown operand";
+
+ case EVAL_ERROR_MISSING_OPERAND:
+ return "expected operand";
+
+ case EVAL_ERROR_MISSING_OPERATOR:
+ return "expected operator";
+
+ case EVAL_ERROR_REMAINING_GARBAGE:
+ return "remaining characters after expression";
+
+ case EVAL_ERROR_INVALID_VALUE:
+ return "invalid value structure - internal error";
+
+ case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
+ return "wrong number of operands for operation - internal error";
+
+ case EVAL_ERROR_VALUE_IS_NAN:
+ return "value or variable is missing or is not a number";
+
+ case EVAL_ERROR_VALUE_IS_INFINITE:
+ return "computed value is infinite";
+
+ case EVAL_ERROR_UNKNOWN_VARIABLE:
+ return "undefined variable";
+
+ default:
+ return "unknown error";
+ }
+}
--- /dev/null
+#ifndef NETDATA_EVAL_H
+#define NETDATA_EVAL_H
+
+typedef struct eval_variable {
+ char *name;
+ struct rrdvar *rrdvar;
+ struct eval_variable *next;
+} EVAL_VARIABLE;
+
+typedef struct eval_expression {
+ const char *source;
+ const char *parsed_as;
+
+ calculated_number result;
+
+ int error;
+ BUFFER *error_msg;
+
+ // hidden EVAL_NODE *
+ void *nodes;
+
+ // custom data to be used for looking up variables
+ void *data;
+} EVAL_EXPRESSION;
+
+#define EVAL_VALUE_INVALID 0
+#define EVAL_VALUE_NUMBER 1
+#define EVAL_VALUE_VARIABLE 2
+#define EVAL_VALUE_EXPRESSION 3
+
+#define EVAL_ERROR_OK 0
+
+// parsing errors
+#define EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION 1
+#define EVAL_ERROR_UNKNOWN_OPERAND 2
+#define EVAL_ERROR_MISSING_OPERAND 3
+#define EVAL_ERROR_MISSING_OPERATOR 4
+#define EVAL_ERROR_REMAINING_GARBAGE 5
+
+// evaluation errors
+#define EVAL_ERROR_INVALID_VALUE 11
+#define EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS 12
+#define EVAL_ERROR_VALUE_IS_NAN 13
+#define EVAL_ERROR_VALUE_IS_INFINITE 14
+#define EVAL_ERROR_UNKNOWN_VARIABLE 15
+
+// parse the given string as an expression and return:
+// a pointer to an expression if it parsed OK
+// NULL in which case the pointer to error has the error code
+extern EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error);
+
+// free all resources allocated for an expression
+extern void expression_free(EVAL_EXPRESSION *op);
+
+// convert an error code to a message
+extern const char *expression_strerror(int error);
+
+// evaluate an expression and return
+// 1 = OK, the result is in: expression->result
+// 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg)
+extern int expression_evaluate(EVAL_EXPRESSION *expression);
+
+#endif //NETDATA_EVAL_H
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-
-#include "global_statistics.h"
+#include "common.h"
struct global_statistics global_statistics = { 0, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL};
--- /dev/null
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// RRDVAR management
+
+int rrdvar_compare(void* a, void* b) {
+ if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
+ else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
+ else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
+}
+
+static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
+ RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
+ if(ret != rv)
+ debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
+
+ return ret;
+}
+
+static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
+ RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
+ if(!ret)
+ fatal("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
+
+ return ret;
+}
+
+static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
+ RRDVAR tmp;
+ tmp.name = (char *)name;
+ tmp.hash = (hash)?hash:simple_hash(tmp.name);
+
+ return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
+}
+
+static inline RRDVAR *rrdvar_create(const char *name, uint32_t hash, int type, calculated_number *value) {
+ RRDVAR *rv = callocz(1, sizeof(RRDVAR));
+
+ rv->name = (char *)name;
+ rv->hash = (hash)?hash:simple_hash((rv->name));
+ rv->type = type;
+ rv->value = value;
+
+ return rv;
+}
+
+static inline void rrdvar_free(RRDHOST *host, RRDVAR *rv) {
+ if(host) {
+ // FIXME: we may need some kind of locking here
+ // to have mutually exclusive access with eval()
+ EVAL_VARIABLE *rf;
+ for (rf = host->references; rf; rf = rf->next)
+ if (rf->rrdvar == rv) rf->rrdvar = NULL;
+ }
+
+ freez(rv);
+}
+
+static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, uint32_t hash, int type, calculated_number *value) {
+ RRDVAR *rv = rrdvar_index_find(tree, name, hash);
+ if(unlikely(!rv)) {
+ debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", name, scope);
+
+ rv = rrdvar_create(name, hash, type, value);
+ RRDVAR *ret = rrdvar_index_add(tree, rv);
+ if(unlikely(ret != rv)) {
+ debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", name, scope);
+ rrdvar_free(NULL, rv);
+ rv = NULL;
+ }
+ else
+ debug(D_VARIABLES, "Variable '%s' created in scope '%s'", name, scope);
+ }
+ else {
+ // already exists
+ rv = NULL;
+ }
+
+ /*
+ * check
+ if(rv) {
+ RRDVAR *ret = rrdvar_index_find(tree, name, hash);
+ if(ret != rv) fatal("oops! 1");
+
+ ret = rrdvar_index_del(tree, rv);
+ if(ret != rv) fatal("oops! 2");
+
+ ret = rrdvar_index_add(tree, rv);
+ if(ret != rv) fatal("oops! 3");
+ }
+ */
+
+ return rv;
+}
+
+// ----------------------------------------------------------------------------
+// RRDSETVAR management
+
+#define RRDSETVAR_ID_MAX 1024
+
+RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
+ debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
+ RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
+
+ char buffer[RRDSETVAR_ID_MAX + 1];
+ snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->id, variable);
+ rs->fullid = strdupz(buffer);
+ rs->hash_fullid = simple_hash(rs->fullid);
+
+ snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, variable);
+ rs->fullname = strdupz(buffer);
+ rs->hash_fullname = simple_hash(rs->fullname);
+
+ rs->variable = strdupz(variable);
+ rs->hash_variable = simple_hash(rs->variable);
+
+ rs->type = type;
+ rs->value = value;
+ rs->options = options;
+ rs->rrdset = st;
+
+ rs->local = rrdvar_create_and_index("local", &st->variables_root_index, rs->variable, rs->hash_variable, rs->type, rs->value);
+ rs->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
+ rs->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullid, rs->hash_fullid, rs->type, rs->value);
+ rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
+ rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
+
+ rs->next = st->variables;
+ st->variables = rs;
+
+ return rs;
+}
+
+void rrdsetvar_rename_all(RRDSET *st) {
+ debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
+
+ // only these 2 can change name
+ // rs->context_name
+ // rs->host_name
+
+ char buffer[RRDSETVAR_ID_MAX + 1];
+ RRDSETVAR *rs, *next = st->variables;
+ while((rs = next)) {
+ next = rs->next;
+
+ snprintfz(buffer, RRDSETVAR_ID_MAX, "%s.%s", st->name, rs->variable);
+
+ if (strcmp(buffer, rs->fullname)) {
+ // name changed
+ if (rs->context_name) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
+ rrdvar_free(st->rrdhost, rs->context_name);
+ }
+
+ if (rs->host_name) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
+ rrdvar_free(st->rrdhost, rs->host_name);
+ }
+
+ freez(rs->fullname);
+ rs->fullname = strdupz(st->name);
+ rs->hash_fullname = simple_hash(rs->fullname);
+ rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
+ rs->host_name = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullname, rs->hash_fullname, rs->type, rs->value);
+ }
+ }
+
+ rrdsetcalc_link_matching(st);
+}
+
+void rrdsetvar_free(RRDSETVAR *rs) {
+ RRDSET *st = rs->rrdset;
+ debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
+
+ if(rs->local) {
+ rrdvar_index_del(&st->variables_root_index, rs->local);
+ rrdvar_free(st->rrdhost, rs->local);
+ }
+
+ if(rs->context) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context);
+ rrdvar_free(st->rrdhost, rs->context);
+ }
+
+ if(rs->host) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host);
+ rrdvar_free(st->rrdhost, rs->host);
+ }
+
+ if(rs->context_name) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_name);
+ rrdvar_free(st->rrdhost, rs->context_name);
+ }
+
+ if(rs->host_name) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_name);
+ rrdvar_free(st->rrdhost, rs->host_name);
+ }
+
+ if(st->variables == rs) {
+ st->variables = rs->next;
+ }
+ else {
+ RRDSETVAR *t;
+ for (t = st->variables; t && t->next != rs; t = t->next);
+ if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id);
+ else t->next = rs->next;
+ }
+
+ freez(rs->fullid);
+ freez(rs->fullname);
+ freez(rs->variable);
+ freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDDIMVAR management
+
+#define RRDDIMVAR_ID_MAX 1024
+
+RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
+ RRDSET *st = rd->rrdset;
+
+ debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
+
+ if(!prefix) prefix = "";
+ if(!suffix) suffix = "";
+
+ char buffer[RRDDIMVAR_ID_MAX + 1];
+ RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
+
+ rs->prefix = strdupz(prefix);
+ rs->suffix = strdupz(suffix);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
+ rs->id = strdupz(buffer);
+ rs->hash = simple_hash(rs->id);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+ rs->name = strdupz(buffer);
+ rs->hash_name = simple_hash(rs->name);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id);
+ rs->fullidid = strdupz(buffer);
+ rs->hash_fullidid = simple_hash(rs->fullidid);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name);
+ rs->fullidname = strdupz(buffer);
+ rs->hash_fullidname = simple_hash(rs->fullidname);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id);
+ rs->fullnameid = strdupz(buffer);
+ rs->hash_fullnameid = simple_hash(rs->fullnameid);
+
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name);
+ rs->fullnamename = strdupz(buffer);
+ rs->hash_fullnamename = simple_hash(rs->fullnamename);
+
+ rs->type = type;
+ rs->value = value;
+ rs->options = options;
+ rs->rrddim = rd;
+
+ rs->local_id = rrdvar_create_and_index("local", &st->variables_root_index, rs->id, rs->hash, rs->type, rs->value);
+ rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
+
+ rs->context_fullidid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
+ rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
+ rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
+ rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
+
+ rs->host_fullidid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->hash_fullidid, rs->type, rs->value);
+ rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
+ rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
+ rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
+
+ rs->next = rd->variables;
+ rd->variables = rs;
+
+ return rs;
+}
+
+void rrddimvar_rename_all(RRDDIM *rd) {
+ RRDSET *st = rd->rrdset;
+ debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+
+ RRDDIMVAR *rs, *next = rd->variables;
+ while((rs = next)) {
+ next = rs->next;
+
+ if (strcmp(rd->name, rs->name)) {
+ char buffer[RRDDIMVAR_ID_MAX + 1];
+ // name changed
+
+ // name
+ if (rs->local_name) {
+ rrdvar_index_del(&st->variables_root_index, rs->local_name);
+ rrdvar_free(st->rrdhost, rs->local_name);
+ }
+ freez(rs->name);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+ rs->name = strdupz(buffer);
+ rs->hash_name = simple_hash(rs->name);
+ rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->hash_name, rs->type, rs->value);
+
+ // fullidname
+ if (rs->context_fullidname) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
+ rrdvar_free(st->rrdhost, rs->context_fullidname);
+ }
+ if (rs->host_fullidname) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullidname);
+ rrdvar_free(st->rrdhost, rs->host_fullidname);
+ }
+ freez(rs->fullidname);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
+ rs->fullidname = strdupz(buffer);
+ rs->hash_fullidname = simple_hash(rs->fullidname);
+ rs->context_fullidname = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
+ rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
+ rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullidname, rs->hash_fullidname, rs->type, rs->value);
+
+ // fullnameid
+ if (rs->context_fullnameid) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
+ rrdvar_free(st->rrdhost, rs->context_fullnameid);
+ }
+ if (rs->host_fullnameid) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnameid);
+ rrdvar_free(st->rrdhost, rs->host_fullnameid);
+ }
+ freez(rs->fullnameid);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
+ rs->fullnameid = strdupz(buffer);
+ rs->hash_fullnameid = simple_hash(rs->fullnameid);
+ rs->context_fullnameid = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
+ rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
+ rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullnameid, rs->hash_fullnameid, rs->type, rs->value);
+
+ // fullnamename
+ if (rs->context_fullnamename) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
+ rrdvar_free(st->rrdhost, rs->context_fullnamename);
+ }
+ if (rs->host_fullnamename) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->context_fullnamename);
+ rrdvar_free(st->rrdhost, rs->host_fullnamename);
+ }
+ freez(rs->fullnamename);
+ snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
+ rs->fullnamename = strdupz(buffer);
+ rs->hash_fullnamename = simple_hash(rs->fullnamename);
+ rs->context_fullnamename = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index,
+ rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
+ rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+ rs->fullnamename, rs->hash_fullnamename, rs->type, rs->value);
+ }
+ }
+}
+
+void rrddimvar_free(RRDDIMVAR *rs) {
+ RRDDIM *rd = rs->rrddim;
+ RRDSET *st = rd->rrdset;
+ debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
+
+ if(rs->local_id) {
+ rrdvar_index_del(&st->variables_root_index, rs->local_id);
+ rrdvar_free(st->rrdhost, rs->local_id);
+ }
+ if(rs->local_name) {
+ rrdvar_index_del(&st->variables_root_index, rs->local_name);
+ rrdvar_free(st->rrdhost, rs->local_name);
+ }
+
+ if(rs->context_fullidid) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidid);
+ rrdvar_free(st->rrdhost, rs->context_fullidid);
+ }
+ if(rs->context_fullidname) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullidname);
+ rrdvar_free(st->rrdhost, rs->context_fullidname);
+ }
+ if(rs->context_fullnameid) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnameid);
+ rrdvar_free(st->rrdhost, rs->context_fullnameid);
+ }
+ if(rs->context_fullnamename) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rs->context_fullnamename);
+ rrdvar_free(st->rrdhost, rs->context_fullnamename);
+ }
+
+ if(rs->host_fullidid) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidid);
+ rrdvar_free(st->rrdhost, rs->host_fullidid);
+ }
+ if(rs->host_fullidname) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullidname);
+ rrdvar_free(st->rrdhost, rs->host_fullidname);
+ }
+ if(rs->host_fullnameid) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnameid);
+ rrdvar_free(st->rrdhost, rs->host_fullnameid);
+ }
+ if(rs->host_fullnamename) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rs->host_fullnamename);
+ rrdvar_free(st->rrdhost, rs->host_fullnamename);
+ }
+
+ if(rd->variables == rs) {
+ debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+ rd->variables = rs->next;
+ }
+ else {
+ debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+ RRDDIMVAR *t;
+ for (t = rd->variables; t && t->next != rs; t = t->next) ;
+ if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id);
+ else t->next = rs->next;
+ }
+
+ freez(rs->prefix);
+ freez(rs->suffix);
+ freez(rs->id);
+ freez(rs->name);
+ freez(rs->fullidid);
+ freez(rs->fullidname);
+ freez(rs->fullnameid);
+ freez(rs->fullnamename);
+ freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCALC management
+
+// this has to be called while the caller has locked
+// the RRDHOST
+static inline void rrdhostcalc_linked(RRDHOST *host, RRDCALC *rc) {
+ // move it to be last
+
+ if(!rc->next)
+ // we are last already
+ return;
+
+ RRDCALC *t, *last = NULL, *prev = NULL;
+ for (t = host->calculations; t ; t = t->next) {
+ if(t->next == rc)
+ prev = t;
+
+ if(!t->next)
+ last = t;
+ }
+
+ if(!last) {
+ error("RRDCALC '%s' cannot be linked to the end of host '%s' list", rc->name, host->hostname);
+ return;
+ }
+
+ if(prev)
+ prev->next = rc->next;
+ else {
+ if(host->calculations == rc)
+ host->calculations = rc->next;
+ else {
+ error("RRDCALC '%s' is not found in host '%s' list", rc->name, host->hostname);
+ return;
+ }
+ }
+
+ last->next = rc;
+ rc->next = NULL;
+}
+
+// this has to be called while the caller has locked
+// the RRDHOST
+static inline void rrdhostcalc_unlinked(RRDHOST *host, RRDCALC *rc) {
+ // move it to be first
+
+ if(host->calculations == rc) {
+ // ok, we are the first
+ return;
+ }
+ else {
+ // find the previous one
+ RRDCALC *t;
+ for (t = host->calculations; t && t->next != rc; rc = rc->next) ;
+ if(unlikely(!t)) {
+ error("RRDCALC '%s' is not linked to host '%s'.", rc->name, host->hostname);
+ return;
+ }
+ t->next = rc->next;
+ rc->next = host->calculations;
+ host->calculations = rc;
+ }
+}
+
+static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
+ rc->rrdset = st;
+
+ rc->local = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
+ rc->context = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
+ rc->host = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rc->name, rc->hash, RRDVAR_TYPE_CALCULATED, &rc->value);
+
+ rrdhostcalc_linked(st->rrdhost, rc);
+}
+
+// this has to be called while the RRDHOST is locked
+void rrdsetcalc_link_matching(RRDSET *st) {
+ RRDCALC *rc;
+
+ for(rc = st->rrdhost->calculations; rc ; rc = rc->next) {
+ // since unlinked ones are in front and linked at the end
+ // we stop on the first linked RRDCALC
+ if(rc->rrdset != NULL) break;
+
+ if((rc->hash_chart == st->hash && !strcmp(rc->name, st->id)) ||
+ (rc->hash_chart == st->hash_name && !strcmp(rc->name, st->name))) {
+ rrdsetcalc_link(st, rc);
+ }
+ }
+}
+
+// this has to be called while the RRDHOST is locked
+void rrdsetcalc_unlink(RRDCALC *rc) {
+ RRDSET *st = rc->rrdset;
+
+ if(!st) {
+ error("Requested to unlink RRDCALC '%s' which is not linked to any RRDSET", rc->name);
+ return;
+ }
+
+ RRDHOST *host = st->rrdhost;
+
+ // unlink it
+ if(rc->rrdset_prev)
+ rc->rrdset_prev->rrdset_next = rc->rrdset_next;
+
+ if(rc->rrdset_next)
+ rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
+
+ if(st->calculations == rc)
+ st->calculations = rc->rrdset_next;
+
+ rc->rrdset_prev = rc->rrdset_next = NULL;
+
+ if(rc->local) {
+ rrdvar_index_del(&st->variables_root_index, rc->local);
+ rrdvar_free(st->rrdhost, rc->local);
+ rc->local = NULL;
+ }
+
+ if(rc->context) {
+ rrdvar_index_del(&st->rrdcontext->variables_root_index, rc->context);
+ rc->context = NULL;
+ }
+
+ if(rc->host) {
+ rrdvar_index_del(&st->rrdhost->variables_root_index, rc->host);
+ rc->host = NULL;
+ }
+
+ rc->rrdset = NULL;
+
+ // RRDCALC will remain in RRDHOST
+ // so that if the matching chart is found in the future
+ // it will be applied automatically
+
+ rrdhostcalc_unlinked(host, rc);
+}
+
+RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method, uint32_t after, uint32_t before, int update_every, uint32_t options) {
+ uint32_t hash = simple_hash(name);
+
+ RRDCALC *rc;
+
+ // make sure it does not already exist
+ for(rc = host->calculations; rc ; rc = rc->next) {
+ if (rc->hash == hash && !strcmp(name, rc->name)) {
+ error("Attempted to create RRDCAL '%s' in host '%s', but it already exists. Ignoring it.", name, host->hostname);
+ return NULL;
+ }
+ }
+
+ rc = callocz(1, sizeof(RRDCALC));
+
+ rc->name = strdupz(name);
+ rc->hash = simple_hash(rc->name);
+
+ rc->chart = strdupz(chart);
+ rc->hash_chart = simple_hash(rc->chart);
+
+ if(dimensions) {
+ rc->dimensions = strdupz(dimensions);
+ rc->hash_chart = simple_hash(rc->chart);
+ }
+
+ return NULL;
+}
+
+void rrdcalc_free(RRDCALC *rc) {
+ if(!rc) return;
+
+ if(rc->rrdset) rrdsetcalc_unlink(rc);
+
+ freez(rc);
+}
\ No newline at end of file
--- /dev/null
+#ifndef NETDATA_HEALTH_H
+#define NETDATA_HEALTH_H
+
+extern int rrdvar_compare(void *a, void *b);
+
+/*
+ * RRDVAR
+ * a variable
+ *
+ * There are 4 scopes: local (chart), context, host and global variables
+ *
+ * Standard global variables:
+ * $now
+ *
+ * Standard host variables:
+ * - none -
+ *
+ * Standard context variables:
+ * - none -
+ *
+ * Standard local variables:
+ * $last_updated
+ * $last_collected_value
+ * $last_value
+ *
+ */
+
+#define RRDVAR_TYPE_CALCULATED 1
+#define RRDVAR_TYPE_TIME_T 2
+#define RRDVAR_TYPE_COLLECTED 3
+#define RRDVAR_TYPE_TOTAL 4
+
+// the variables as stored in the variables indexes
+typedef struct rrdvar {
+ avl avl;
+
+ char *name;
+ uint32_t hash;
+
+ int type;
+ void *value;
+
+ time_t last_updated;
+} RRDVAR;
+
+// variables linked to charts
+// We link variables to point the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrdsetvar {
+ char *fullid; // chart type.chart id.variable
+ uint32_t hash_fullid;
+
+ char *fullname; // chart type.chart name.variable
+ uint32_t hash_fullname;
+
+ char *variable; // variable
+ uint32_t hash_variable;
+
+ int type;
+ void *value;
+
+ uint32_t options;
+
+ RRDVAR *local;
+ RRDVAR *context;
+ RRDVAR *host;
+ RRDVAR *context_name;
+ RRDVAR *host_name;
+
+ struct rrdset *rrdset;
+
+ struct rrdsetvar *next;
+} RRDSETVAR;
+
+
+// variables linked to dimensions
+// We link variables to point the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrddimvar {
+ char *prefix;
+ char *suffix;
+
+ char *id; // dimension id
+ uint32_t hash;
+
+ char *name; // dimension name
+ uint32_t hash_name;
+
+ char *fullidid; // chart type.chart id.dimension id
+ uint32_t hash_fullidid;
+
+ char *fullidname; // chart type.chart id.dimension name
+ uint32_t hash_fullidname;
+
+ char *fullnameid; // chart type.chart name.dimension id
+ uint32_t hash_fullnameid;
+
+ char *fullnamename; // chart type.chart name.dimension name
+ uint32_t hash_fullnamename;
+
+ int type;
+ void *value;
+
+ uint32_t options;
+
+ RRDVAR *local_id;
+ RRDVAR *local_name;
+
+ RRDVAR *context_fullidid;
+ RRDVAR *context_fullidname;
+ RRDVAR *context_fullnameid;
+ RRDVAR *context_fullnamename;
+
+ RRDVAR *host_fullidid;
+ RRDVAR *host_fullidname;
+ RRDVAR *host_fullnameid;
+ RRDVAR *host_fullnamename;
+
+ struct rrddim *rrddim;
+
+ struct rrddimvar *next;
+} RRDDIMVAR;
+
+// additional calculated variables
+// These aggregate time-series data at fixed intervals
+// (defined in their update_every member below)
+// These increase the overhead of netdata.
+//
+// These calculations are allocated and linked (->next)
+// to RRDHOST.
+// Then are also linked to RRDSET (of course only when the
+// chart is found, via ->rrdset_next and ->rrdset_prev).
+// This double-linked list is maintained sorted at all times
+// having as RRDSET->calculations the RRDCALC to be processed
+// next.
+typedef struct rrdcalc {
+ char *name;
+ uint32_t hash;
+
+ char *chart; // the chart name
+ uint32_t hash_chart;
+
+ char *dimensions; // the chart dimensions
+
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
+ int update_every; // update frequency for the calculation
+
+ time_t last_updated;
+ time_t next_update;
+
+ EVAL_EXPRESSION *expression;
+
+ calculated_number value;
+
+ RRDVAR *local;
+ RRDVAR *context;
+ RRDVAR *host;
+
+ struct rrdset *rrdset;
+ struct rrdcalc *rrdset_next;
+ struct rrdcalc *rrdset_prev;
+
+ struct rrdcalc *next;
+} RRDCALC;
+
+
+// RRDCALCTEMPLATE
+// these are to be applied to charts found dynamically
+// based on their context.
+typedef struct rrdcalctemplate {
+ char *name;
+ uint32_t hash_name;
+
+ char *context;
+ uint32_t hash_context;
+
+ char *dimensions;
+
+ int group; // grouping method: average, max, etc.
+ int before; // ending point in time-series
+ int after; // starting point in time-series
+ uint32_t options; // calculation options
+ int update_every; // update frequency for the calculation
+
+ struct rrdcalctemplate *next;
+} RRDCALCTEMPLATE;
+
+#include "rrd.h"
+
+extern void rrdsetvar_rename_all(RRDSET *st);
+extern RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options);
+extern void rrdsetvar_free(RRDSETVAR *rs);
+
+extern void rrddimvar_rename_all(RRDDIM *rd);
+extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options);
+extern void rrddimvar_free(RRDDIMVAR *rs);
+
+extern void rrdsetcalc_link_matching(RRDSET *st);
+extern void rrdsetcalc_unlink(RRDCALC *rc);
+
+#endif //NETDATA_HEALTH_H
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <time.h>
-#include <syslog.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "log.h"
#include "common.h"
-
// ----------------------------------------------------------------------------
// LOG
vsyslog(LOG_ERR, fmt, args );
va_end( args );
}
+
+ fflush(stdout);
}
void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
-#include <stdio.h>
-#include <stdarg.h>
-#include <time.h>
-
-#include "main.h"
-
#ifndef NETDATA_LOG_H
#define NETDATA_LOG_H 1
#define D_MEMORY 0x00080000
#define D_CGROUP 0x00100000
#define D_REGISTRY 0x00200000
+#define D_VARIABLES 0x00400000
//#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
//#define DEBUG 0xffffffff
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <errno.h>
-#include <getopt.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/wait.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "appconfig.h"
#include "common.h"
-#include "daemon.h"
-#include "log.h"
-#include "popen.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "web_client.h"
-#include "web_server.h"
-
-#include "unit_test.h"
-
-#include "plugin_checks.h"
-#include "plugin_idlejitter.h"
-#include "plugin_nfacct.h"
-#include "registry.h"
-#include "plugin_proc.h"
-#include "plugin_tc.h"
-#include "plugins_d.h"
-
-#include "main.h"
extern void *cgroups_main(void *ptr);
-volatile sig_atomic_t netdata_exit = 0;
-
void netdata_cleanup_and_exit(int ret) {
netdata_exit = 1;
error_log_limit_unlimited();
info("Called: netdata_cleanup_and_exit()");
+#ifdef NETDATA_INTERNAL_CHECKS
+ rrdset_free_all();
+#else
rrdset_save_all();
+#endif
// kill_childs();
if(pidfile[0]) {
struct netdata_static_thread *st = &static_threads[i];
if(st->enabled) {
- st->thread = malloc(sizeof(pthread_t));
- if(!st->thread)
- fatal("Cannot allocate pthread_t memory");
+ st->thread = mallocz(sizeof(pthread_t));
info("Starting thread %s.", st->name);
-#include <getopt.h>
-
#ifndef NETDATA_MAIN_H
#define NETDATA_MAIN_H 1
-#include <signal.h>
-
extern volatile sig_atomic_t netdata_exit;
/**
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_checks.h"
void *checks_main(void *ptr)
{
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_idlejitter.h"
#define CPU_IDLEJITTER_SLEEP_TIME_MS 20
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
#ifdef INTERNAL_PLUGIN_NFACCT
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-
#include <libmnl/libmnl.h>
#include <libnetfilter_acct/libnetfilter_acct.h>
-#include "main.h"
-#include "global_statistics.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
struct mynfacct {
const char *name;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "main.h"
-#include "registry.h"
void *proc_main(void *ptr)
{
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include "avl.h"
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugin_tc.h"
-#include "main.h"
#define RRD_TYPE_TC "tc"
#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
tc_class_index_del(n, c);
- if(c->id) free(c->id);
- if(c->name) free(c->name);
- if(c->leafid) free(c->leafid);
- if(c->parentid) free(c->parentid);
-
- free(c);
+ freez(c->id);
+ freez(c->name);
+ freez(c->leafid);
+ freez(c->parentid);
+ freez(c);
}
static inline void tc_device_classes_cleanup(struct tc_device *d) {
{
struct tc_class *c = tc_class_index_find(d, id, 0);
if(likely(c)) {
- if(likely(c->name))
- free(c->name);
-
+ freez(c->name);
c->name = NULL;
if(likely(name && *name && strcmp(c->id, name) != 0)) {
debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
- c->name = strdup(name);
- if(likely(c->name))
- c->name_updated = 1;
+ c->name = strdupz(name);
+ c->name_updated = 1;
}
}
}
static inline void tc_device_set_device_name(struct tc_device *d, char *name) {
- if(likely(d->name))
- free(d->name);
-
+ freez(d->name);
d->name = NULL;
if(likely(name && *name && strcmp(d->id, name) != 0)) {
debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
- d->name = strdup(name);
- if(likely(d->name))
- d->name_updated = 1;
+ d->name = strdupz(name);
+ d->name_updated = 1;
}
}
static inline void tc_device_set_device_family(struct tc_device *d, char *family) {
- if(likely(d->family))
- free(d->family);
-
+ freez(d->family);
d->family = NULL;
if(likely(family && *family && strcmp(d->id, family) != 0)) {
debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
- d->family = strdup(family);
- if(likely(d->family))
- d->family_updated = 1;
+ d->family = strdupz(family);
+ d->family_updated = 1;
}
// no need for null termination - it is already null
}
if(!d) {
debug(D_TC_LOOP, "TC: Creating device '%s'", id);
- d = calloc(1, sizeof(struct tc_device));
- if(!d) {
- fatal("Cannot allocate memory for tc_device %s", id);
- return NULL;
- }
+ d = callocz(1, sizeof(struct tc_device));
- d->id = strdup(id);
+ d->id = strdupz(id);
d->hash = simple_hash(d->id);
d->enabled = -1;
if(!c) {
debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
- c = calloc(1, sizeof(struct tc_class));
- if(!c) {
- fatal("Cannot allocate memory for tc class");
- return NULL;
- }
+ c = callocz(1, sizeof(struct tc_class));
if(n->classes) n->classes->prev = c;
c->next = n->classes;
n->classes = c;
- c->id = strdup(id);
- if(!c->id) {
- free(c);
- return NULL;
- }
+ c->id = strdupz(id);
c->hash = simple_hash(c->id);
if(parentid && *parentid) {
- c->parentid = strdup(parentid);
+ c->parentid = strdupz(parentid);
c->parent_hash = simple_hash(c->parentid);
}
if(leafid && *leafid) {
- c->leafid = strdup(leafid);
+ c->leafid = strdupz(leafid);
c->leaf_hash = simple_hash(c->leafid);
}
while(n->classes) tc_class_free(n, n->classes);
- if(n->id) free(n->id);
- if(n->name) free(n->name);
- if(n->family) free(n->family);
-
- free(n);
+ freez(n->id);
+ freez(n->name);
+ freez(n->family);
+ freez(n);
}
static inline void tc_device_free_all()
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/types.h>
-#include <dirent.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-
-#include "main.h"
#include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugins_d.h"
-#include "../config.h"
struct plugind *pluginsd_root = NULL;
// it is not running
// allocate a new one, or use the obsolete one
if(unlikely(!cd)) {
- cd = calloc(sizeof(struct plugind), 1);
- if(unlikely(!cd)) fatal("Cannot allocate memory for plugin.");
+ cd = callocz(sizeof(struct plugind), 1);
snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
-#include <sys/types.h>
-#include <unistd.h>
-
-
#ifndef NETDATA_PLUGINS_D_H
#define NETDATA_PLUGINS_D_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "log.h"
-#include "popen.h"
#include "common.h"
/*
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-
#ifndef NETDATA_POPEN_H
#define NETDATA_POPEN_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-
-#include "proc_self_mountinfo.h"
#define RRD_TYPE_DISK "disk"
// not found
// create a new disk structure
- d = (struct disk *)malloc(sizeof(struct disk));
- if(!d) fatal("Cannot allocate memory for struct disk in proc_diskstats.");
+ d = (struct disk *)mallocz(sizeof(struct disk));
- d->disk = strdup(disk);
+ d->disk = strdupz(disk);
d->major = major;
d->minor = minor;
d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct.
}
if(mi)
- d->mount_point = strdup(mi->mount_point);
+ d->mount_point = strdupz(mi->mount_point);
// no need to check for NULL
else
d->mount_point = NULL;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
#define MAX_INTERRUPT_NAME 50
if(lines < allocated) return irrs;
else {
- irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
- if(!irrs)
- fatal("Cannot allocate memory for %d interrupts", lines);
-
+ irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
allocated = lines;
}
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
+
+// linux calculates this once every 5 seconds
+#define MIN_LOADAVG_UPDATE_EVERY 5
int do_proc_loadavg(int update_every, unsigned long long dt) {
static procfile *ff = NULL;
if(do_loadavg) {
st = rrdset_find_byname("system.load");
if(!st) {
- st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, update_every, RRDSET_TYPE_LINE);
+ st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)?MIN_LOADAVG_UPDATE_EVERY:update_every, RRDSET_TYPE_LINE);
rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define MAX_PROC_MEMINFO_LINE 4096
#define MAX_PROC_MEMINFO_NAME 1024
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_net_dev(int update_every, unsigned long long dt) {
static procfile *ff = NULL;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define RRD_TYPE_NET_IPVS "ipvs"
#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_net_netstat(int update_every, unsigned long long dt) {
static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
struct nfsd_procs {
char name[30];
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define RRD_TYPE_NET_SNMP "ipv4"
#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define RRD_TYPE_NET_SNMP6 "ipv6"
#define RRD_TYPE_NET_SNMP6_LEN strlen(RRD_TYPE_NET_SNMP6)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
#define RRD_TYPE_NET_STAT_NETFILTER "netfilter"
#define RRD_TYPE_NET_STAT_SYNPROXY "synproxy"
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "proc_self_mountinfo.h"
// find the mount info with the given major:minor
// in the supplied linked list of mountinfo structures
if(likely(mi->next))
mountinfo_free(mi->next);
- if(mi->root) free(mi->root);
- if(mi->mount_point) free(mi->mount_point);
- if(mi->mount_options) free(mi->mount_options);
+ freez(mi->root);
+ freez(mi->mount_point);
+ freez(mi->mount_options);
/*
if(mi->optional_fields_count) {
}
free(mi->optional_fields);
*/
- free(mi->filesystem);
- free(mi->mount_source);
- free(mi->super_options);
- free(mi);
+ freez(mi->filesystem);
+ freez(mi->mount_source);
+ freez(mi->super_options);
+ freez(mi);
}
-static char *strdup_decoding_octal(const char *string) {
- char *buffer = strdup(string);
- if(!buffer) fatal("Cannot allocate memory.");
+static char *strdupz_decoding_octal(const char *string) {
+ char *buffer = strdupz(string);
char *d = buffer;
const char *s = string;
if(procfile_linewords(ff, l) < 5)
continue;
- mi = malloc(sizeof(struct mountinfo));
- if(unlikely(!mi)) fatal("Cannot allocate memory for mountinfo");
+ mi = mallocz(sizeof(struct mountinfo));
if(unlikely(!root))
root = last = mi;
mi->major = strtoul(major, NULL, 10);
mi->minor = strtoul(minor, NULL, 10);
- mi->root = strdup(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->root)) fatal("Cannot allocate memory");
+ mi->root = strdupz(procfile_lineword(ff, l, w)); w++;
mi->root_hash = simple_hash(mi->root);
- mi->mount_point = strdup_decoding_octal(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->mount_point)) fatal("Cannot allocate memory");
+ mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++;
mi->mount_point_hash = simple_hash(mi->mount_point);
- mi->mount_options = strdup(procfile_lineword(ff, l, w)); w++;
- if(unlikely(!mi->mount_options)) fatal("Cannot allocate memory");
+ mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++;
// count the optional fields
/*
if(likely(*s == '-')) {
w++;
- mi->filesystem = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->filesystem) fatal("Cannot allocate memory");
+ mi->filesystem = strdupz(procfile_lineword(ff, l, w)); w++;
mi->filesystem_hash = simple_hash(mi->filesystem);
- mi->mount_source = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->mount_source) fatal("Cannot allocate memory");
+ mi->mount_source = strdupz(procfile_lineword(ff, l, w)); w++;
mi->mount_source_hash = simple_hash(mi->mount_source);
- mi->super_options = strdup(procfile_lineword(ff, l, w)); w++;
- if(!mi->super_options) fatal("Cannot allocate memory");
+ mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++;
}
else {
mi->filesystem = NULL;
-#include "procfile.h"
-
#ifndef NETDATA_PROC_SELF_MOUNTINFO_H
#define NETDATA_PROC_SELF_MOUNTINFO_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
#define MAX_INTERRUPT_NAME 50
if(lines < allocated) return irrs;
else {
- irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
- if(!irrs)
- fatal("Cannot allocate memory for %d interrupts", lines);
-
+ irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
allocated = lines;
}
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_stat(int update_every, unsigned long long dt) {
(void)dt;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt) {
static procfile *ff = NULL;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
int do_proc_vmstat(int update_every, unsigned long long dt) {
static procfile *ff = NULL;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <malloc.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
#include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
#define PF_PREFIX "PROCFILE"
if(unlikely(fw->len == fw->size)) {
// debug(D_PROCFILE, PF_PREFIX ": expanding words");
- pfwords *new = realloc(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
- if(unlikely(!new)) {
- error(PF_PREFIX ": failed to expand words");
- free(fw);
- return NULL;
- }
- fw = new;
+ fw = reallocz(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
fw->size += PFWORDS_INCREASE_STEP;
}
uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
- pfwords *new = malloc(sizeof(pfwords) + size * sizeof(char *));
- if(unlikely(!new)) return NULL;
-
+ pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *));
new->len = 0;
new->size = size;
return new;
void pfwords_free(pfwords *fw) {
// debug(D_PROCFILE, PF_PREFIX ": freeing words");
- free(fw);
+ freez(fw);
}
if(unlikely(fl->len == fl->size)) {
// debug(D_PROCFILE, PF_PREFIX ": expanding lines");
- pflines *new = realloc(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
- if(unlikely(!new)) {
- error(PF_PREFIX ": failed to expand lines");
- free(fl);
- return NULL;
- }
- fl = new;
+ fl = reallocz(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
fl->size += PFLINES_INCREASE_STEP;
}
uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
- pflines *new = malloc(sizeof(pflines) + size * sizeof(ffline));
- if(unlikely(!new)) return NULL;
-
+ pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline));
new->len = 0;
new->size = size;
return new;
void pflines_free(pflines *fl) {
// debug(D_PROCFILE, PF_PREFIX ": freeing lines");
- free(fl);
+ freez(fl);
}
if(likely(ff->words)) pfwords_free(ff->words);
if(likely(ff->fd != -1)) close(ff->fd);
- free(ff);
+ freez(ff);
}
procfile *procfile_parser(procfile *ff) {
if(!x) {
debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
- procfile *new = realloc(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
- if(unlikely(!new)) {
- error(PF_PREFIX ": Cannot allocate memory for file '%s'", ff->filename);
- procfile_close(ff);
- return NULL;
- }
- ff = new;
+ ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
ff->size += PROCFILE_INCREMENT_BUFFER;
}
}
size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
- procfile *ff = malloc(sizeof(procfile) + size);
- if(unlikely(!ff)) {
- error(PF_PREFIX ": Cannot allocate memory for file '%s'", filename);
- close(fd);
- return NULL;
- }
-
+ procfile *ff = mallocz(sizeof(procfile) + size);
strncpyz(ff->filename, filename, FILENAME_MAX);
ff->fd = fd;
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <uuid/uuid.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "log.h"
#include "common.h"
-#include "dictionary.h"
-#include "appconfig.h"
-
-#include "web_client.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-
// ----------------------------------------------------------------------------
// TODO
urllen = registry.max_url_length;
debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
- URL *u = malloc(sizeof(URL) + urllen);
- if(!u) fatal("Cannot allocate %zu bytes for URL '%s'", sizeof(URL) + urllen, url);
+ URL *u = mallocz(sizeof(URL) + urllen);
// a simple strcpy() should do the job
// but I prefer to be safe, since the caller specified urllen
- strncpyz(u->url, url, urllen);
-
- u->len = urllen;
+ u->len = (uint16_t)urllen;
+ strncpyz(u->url, url, u->len);
u->links = 0;
registry.urls_memory += sizeof(URL) + urllen;
if(!u->links) {
debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
dictionary_del(registry.urls, u->url);
- free(u);
+ freez(u);
}
else
debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
static inline MACHINE_URL *registry_machine_url_allocate(MACHINE *m, URL *u, time_t when) {
debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL));
- MACHINE_URL *mu = malloc(sizeof(MACHINE_URL));
- if(!mu) fatal("registry_machine_link_to_url('%s', '%s'): cannot allocate %zu bytes.", m->guid, u->url, sizeof(MACHINE_URL));
+ MACHINE_URL *mu = mallocz(sizeof(MACHINE_URL));
// mu->persons = dictionary_create(DICTIONARY_FLAGS);
// dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
- mu->first_t = mu->last_t = when;
+ mu->first_t = mu->last_t = (uint32_t)when;
mu->usages = 1;
mu->url = u;
mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
static inline MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE));
- MACHINE *m = malloc(sizeof(MACHINE));
- if(!m) fatal("Registry: cannot allocate memory for new machine '%s'", machine_guid);
+ MACHINE *m = mallocz(sizeof(MACHINE));
strncpyz(m->guid, machine_guid, 36);
debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
m->urls = dictionary_create(DICTIONARY_FLAGS);
- m->first_t = m->last_t = when;
+ m->first_t = m->last_t = (uint32_t)when;
m->usages = 0;
registry.machines_memory += sizeof(MACHINE);
debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
sizeof(PERSON_URL) + namelen);
- PERSON_URL *pu = malloc(sizeof(PERSON_URL) + namelen);
- if(!pu) fatal("registry_person_url_allocate('%s', '%s', '%s'): cannot allocate %zu bytes.", p->guid, m->guid, u->url, sizeof(PERSON_URL) + namelen);
+ PERSON_URL *pu = mallocz(sizeof(PERSON_URL) + namelen);
// a simple strcpy() should do the job
// but I prefer to be safe, since the caller specified urllen
registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name);
registry_url_unlink_nolock(u);
- free(pu);
+ freez(pu);
return tpu;
}
debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON));
- p = malloc(sizeof(PERSON));
- if(!p) fatal("Registry: cannot allocate memory for new person.");
+ p = mallocz(sizeof(PERSON));
if(!person_guid) {
for (; ;) {
dictionary_del(p->urls, dpu->url->url);
registry_url_unlink_nolock(dpu->url);
- free(dpu);
+ freez(dpu);
registry_person_urls_unlock(p);
return p;
registry_url_unlink_nolock(pu->url);
debug(D_REGISTRY, "Registry: freeing person url");
- free(pu);
+ freez(pu);
}
debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
dictionary_destroy(p->urls);
debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
- free(p);
+ freez(p);
}
while(registry.machines->values_index.root) {
registry_url_unlink_nolock(mu->url);
debug(D_REGISTRY, "Registry: freeing machine url");
- free(mu);
+ freez(mu);
}
debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
dictionary_destroy(m->urls);
debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
- free(m);
+ freez(m);
}
// and free the memory of remaining dictionary structures
-#include "web_client.h"
-
#ifndef NETDATA_REGISTRY_H
#define NETDATA_REGISTRY_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <pthread.h>
-#include <errno.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "main.h"
-#include "rrd.h"
#define RRD_DEFAULT_GAP_INTERPOLATIONS 1
int rrd_update_every = UPDATE_EVERY;
int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
-RRDSET *rrdset_root = NULL;
-pthread_rwlock_t rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static int rrdset_compare(void* a, void* b);
+static int rrdset_compare_name(void* a, void* b);
+static int rrdcontext_compare(void* a, void* b);
+
+RRDHOST localhost = {
+ .hostname = "localhost",
+ .rrdset_root = NULL,
+ .rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER,
+ .rrdset_root_index = {
+ { NULL, rrdset_compare },
+ AVL_LOCK_INITIALIZER
+ },
+ .rrdset_root_index_name = {
+ { NULL, rrdset_compare_name },
+ AVL_LOCK_INITIALIZER
+ },
+ .rrdcontext_root_index = {
+ { NULL, rrdcontext_compare },
+ AVL_LOCK_INITIALIZER
+ },
+ .variables_root_index = {
+ { NULL, rrdvar_compare },
+ AVL_LOCK_INITIALIZER
+ }
+};
-int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
+// ----------------------------------------------------------------------------
+// RRDCONTEXT index
+
+static int rrdcontext_compare(void* a, void* b) {
+ if(((RRDCONTEXT *)a)->hash < ((RRDCONTEXT *)b)->hash) return -1;
+ else if(((RRDCONTEXT *)a)->hash > ((RRDCONTEXT *)b)->hash) return 1;
+ else return strcmp(((RRDCONTEXT *)a)->id, ((RRDCONTEXT *)b)->id);
+}
+
+#define rrdcontext_index_add(host, rc) (RRDCONTEXT *)avl_insert_lock(&((host)->rrdcontext_root_index), (avl *)(rc))
+#define rrdcontext_index_del(host, rc) (RRDCONTEXT *)avl_remove_lock(&((host)->rrdcontext_root_index), (avl *)(rc))
+
+static RRDCONTEXT *rrdcontext_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+ RRDCONTEXT tmp;
+ tmp.id = id;
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
+
+ return (RRDCONTEXT *)avl_search_lock(&(host->rrdcontext_root_index), (avl *) &tmp);
+}
+
+RRDCONTEXT *rrdcontext_create(const char *id) {
+ RRDCONTEXT *rc = rrdcontext_index_find(&localhost, id, 0);
+ if(!rc) {
+ rc = callocz(1, sizeof(RRDCONTEXT));
+
+ rc->id = strdupz(id);
+ rc->hash = simple_hash(rc->id);
+
+ // initialize the variables index
+ avl_init_lock(&rc->variables_root_index, rrdvar_compare);
+
+ RRDCONTEXT *ret = rrdcontext_index_add(&localhost, rc);
+ if(ret != rc)
+ fatal("INTERNAL ERROR: Expected to INSERT RRDCONTEXT '%s' into index, but inserted '%s'.", rc->id, (ret)?ret->id:"NONE");
+ }
+
+ rc->use_count++;
+ return rc;
+}
+
+void rrdcontext_free(RRDCONTEXT *rc) {
+ rc->use_count--;
+ if(!rc->use_count) {
+ RRDCONTEXT *ret = rrdcontext_index_del(&localhost, rc);
+ if(ret != rc)
+ fatal("INTERNAL ERROR: Expected to DELETE RRDCONTEXT '%s' from index, but deleted '%s'.", rc->id, (ret)?ret->id:"NONE");
+
+ if(rc->variables_root_index.avl_tree.root != NULL)
+ fatal("INTERNAL ERROR: Variables index of RRDCONTEXT '%s' that is freed, is not empty.", rc->id);
+ freez((void *)rc->id);
+ freez(rc);
+ }
+}
// ----------------------------------------------------------------------------
// RRDSET index
static int rrdset_compare(void* a, void* b) {
- if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
- else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
- else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
+ if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
+ else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
+ else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
}
-avl_tree_lock rrdset_root_index = {
- { NULL, rrdset_compare },
- AVL_LOCK_INITIALIZER
-};
+#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl *)(st))
+#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl *)(st))
-#define rrdset_index_add(st) avl_insert_lock(&rrdset_root_index, (avl *)(st))
-#define rrdset_index_del(st) avl_remove_lock(&rrdset_root_index, (avl *)(st))
+static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+ RRDSET tmp;
+ strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+ tmp.hash = (hash)?hash:simple_hash(tmp.id);
-static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
- RRDSET tmp;
- strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
- tmp.hash = (hash)?hash:simple_hash(tmp.id);
-
- return (RRDSET *)avl_search_lock(&(rrdset_root_index), (avl *) &tmp);
+ return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl *) &tmp);
}
// ----------------------------------------------------------------------------
else return strcmp(A->name, B->name);
}
-avl_tree_lock rrdset_root_index_name = {
- { NULL, rrdset_compare_name },
- AVL_LOCK_INITIALIZER
-};
-
-RRDSET *rrdset_index_add_name(RRDSET *st) {
+RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) {
+ void *result;
// fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
- return (RRDSET *)avl_insert_lock(&rrdset_root_index_name, (avl *) (&st->avlname));
+ result = avl_insert_lock(&host->rrdset_root_index_name, (avl *) (&st->avlname));
+ if(result) return rrdset_from_avlname(result);
+ return NULL;
}
-#define rrdset_index_del_name(st) avl_remove_lock(&rrdset_root_index_name, (avl *)(&st->avlname))
+RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) {
+ void *result;
+ // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name);
+ return (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl *)(&st->avlname));
+ if(result) return rrdset_from_avlname(result);
+ return NULL;
+}
-static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
+static RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) {
void *result = NULL;
RRDSET tmp;
tmp.name = name;
tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
// fprintf(stderr, "SEARCHING: %s\n", name);
- result = avl_search_lock(&(rrdset_root_index_name), (avl *) (&(tmp.avlname)));
+ result = avl_search_lock(&host->rrdset_root_index_name, (avl *) (&(tmp.avlname)));
if(result) {
RRDSET *st = rrdset_from_avlname(result);
if(strcmp(st->magic, RRDSET_MAGIC))
{
debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
- if(st->name) rrdset_index_del_name(st);
+ if(st->name) {
+ rrdset_index_del_name(&localhost, st);
+ rrdsetvar_rename_all(st);
+ }
char b[CONFIG_MAX_VALUE + 1];
char n[RRD_ID_LENGTH_MAX + 1];
st->name = config_get(st->id, "name", b);
st->hash_name = simple_hash(st->name);
- rrdset_index_add_name(st);
+ rrdset_index_add_name(&localhost, st);
}
// ----------------------------------------------------------------------------
st->dimensions = NULL;
st->next = NULL;
st->mapped = rrd_memory_mode;
+ st->variables = NULL;
}
else {
- st = calloc(1, size);
- if(!st) {
- fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
- return NULL;
- }
+ st = callocz(1, size);
st->mapped = RRD_MEMORY_MODE_RAM;
}
st->memsize = size;
config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
avl_init_lock(&st->dimensions_index, rrddim_compare);
+ avl_init_lock(&st->variables_root_index, rrdvar_compare);
pthread_rwlock_init(&st->rwlock, NULL);
- pthread_rwlock_wrlock(&rrdset_root_rwlock);
+ pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
if(name && *name) rrdset_set_name(st, name);
else rrdset_set_name(st, id);
st->title = config_get(st->id, "title", varvalue);
}
- st->next = rrdset_root;
- rrdset_root = st;
+ st->rrdcontext = rrdcontext_create(st->context);
+ st->rrdhost = &localhost;
+
+ st->next = localhost.rrdset_root;
+ localhost.rrdset_root = st;
+
+ rrdsetvar_create(st, "last_collected", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, 0);
+ rrdsetvar_create(st, "raw_total", RRDVAR_TYPE_TOTAL, &st->collected_total, 0);
- rrdset_index_add(st);
+ rrdset_index_add(&localhost, st);
- pthread_rwlock_unlock(&rrdset_root_rwlock);
+ rrdsetcalc_link_matching(st);
+
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
return(st);
}
// we have a file mapped for rd
rd->mapped = rrd_memory_mode;
rd->flags = 0x00000000;
+ rd->variables = NULL;
rd->next = NULL;
rd->name = NULL;
}
else {
// if we didn't manage to get a mmap'd dimension, just create one
- rd = calloc(1, size);
- if(!rd) {
- fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
- return NULL;
- }
-
+ rd = callocz(1, size);
rd->mapped = RRD_MEMORY_MODE_RAM;
}
rd->memsize = size;
rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
rd->last_collected_time.tv_sec = 0;
rd->last_collected_time.tv_usec = 0;
+ rd->rrdset = st;
- // append this dimension
+ // append this dimension
pthread_rwlock_wrlock(&st->rwlock);
if(!st->dimensions)
st->dimensions = rd;
for(; td->next; td = td->next) ;
td->next = rd;
}
+
+ rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->calculated_value, 0);
+ rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->collected_value, 0);
+ rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected", &rd->last_collected_time.tv_sec, 0);
+
pthread_rwlock_unlock(&st->rwlock);
rrddim_index_add(st, rd);
char varname[CONFIG_MAX_NAME + 1];
snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
config_set_default(st->id, varname, name);
+
+ rrddimvar_rename_all(rd);
}
void rrddim_free(RRDSET *st, RRDDIM *rd)
else st->dimensions = rd->next;
rd->next = NULL;
- rrddim_index_del(st, rd);
+ while(rd->variables)
+ rrddimvar_free(rd->variables);
+
+ rrddim_index_del(st, rd);
// free(rd->annotations);
if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
}
else {
debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
- free(rd);
+ freez(rd);
}
}
{
info("Freeing all memory...");
+ pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
+
RRDSET *st;
- for(st = rrdset_root; st ;) {
+ for(st = localhost.rrdset_root; st ;) {
RRDSET *next = st->next;
- while(st->dimensions)
+ pthread_rwlock_wrlock(&st->rwlock);
+
+ while(st->variables)
+ rrdsetvar_free(st->variables);
+
+ while(st->calculations)
+ rrdsetcalc_unlink(st->calculations);
+
+ while(st->dimensions)
rrddim_free(st, st->dimensions);
- rrdset_index_del(st);
+ rrdset_index_del(&localhost, st);
+
+ st->rrdcontext->use_count--;
+ if(!st->rrdcontext->use_count)
+ rrdcontext_free(st->rrdcontext);
+
+ pthread_rwlock_unlock(&st->rwlock);
if(st->mapped == RRD_MEMORY_MODE_SAVE) {
debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
munmap(st, st->memsize);
}
else
- free(st);
+ freez(st);
st = next;
}
- rrdset_root = NULL;
+ localhost.rrdset_root = NULL;
+
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
info("Memory cleanup completed...");
}
RRDSET *st;
RRDDIM *rd;
- pthread_rwlock_wrlock(&rrdset_root_rwlock);
- for(st = rrdset_root; st ; st = st->next) {
+ pthread_rwlock_wrlock(&localhost.rrdset_root_rwlock);
+ for(st = localhost.rrdset_root; st ; st = st->next) {
pthread_rwlock_wrlock(&st->rwlock);
if(st->mapped == RRD_MEMORY_MODE_SAVE) {
pthread_rwlock_unlock(&st->rwlock);
}
- pthread_rwlock_unlock(&rrdset_root_rwlock);
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
}
{
debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
- RRDSET *st = rrdset_index_find(id, 0);
+ RRDSET *st = rrdset_index_find(&localhost, id, 0);
return(st);
}
{
debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
- RRDSET *st = rrdset_index_find_name(name, 0);
+ RRDSET *st = rrdset_index_find_name(&localhost, name, 0);
return(st);
}
-#include <sys/time.h>
-#include <pthread.h>
-#include <stdint.h>
-
-#include "avl.h"
-#include "storage_number.h"
-
#ifndef NETDATA_RRD_H
#define NETDATA_RRD_H 1
#define RRD_ID_LENGTH_MAX 1024
-#define RRDSET_MAGIC "NETDATA RRD SET FILE V017"
-#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V017"
+#define RRDSET_MAGIC "NETDATA RRD SET FILE V018"
+#define RRDDIMENSION_MAGIC "NETDATA RRD DIMENSION FILE V018"
typedef long long total_number;
#define TOTAL_NUMBER_FORMAT "%lld"
#define RRDDIM_FLAG_HIDDEN 0x00000001 // this dimension will not be offered to callers
#define RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS 0x00000002 // do not offer RESET or OVERFLOW info to callers
+// ----------------------------------------------------------------------------
+// RRD CONTEXT
+
+struct rrdcontext {
+ avl avl;
+
+ const char *id;
+ uint32_t hash;
+
+ size_t use_count;
+
+ avl_tree_lock variables_root_index;
+};
+typedef struct rrdcontext RRDCONTEXT;
+
// ----------------------------------------------------------------------------
// RRD DIMENSION
calculated_number stored_volume; // the sum of all stored values so far
struct rrddim *next; // linking of dimensions within the same data set
+ struct rrdset *rrdset;
// ------------------------------------------------------------------------
// members for checking the data when loading from disk
char magic[sizeof(RRDDIMENSION_MAGIC) + 1]; // a string to be saved, used to identify our data file
+ struct rrddimvar *variables;
+
// ------------------------------------------------------------------------
// the values stored in this dimension, using our floating point numbers
total_number collected_total; // used internally to calculate percentages
total_number last_collected_total; // used internally to calculate percentages
+ RRDCONTEXT *rrdcontext;
+ struct rrdhost *rrdhost;
+
struct rrdset *next; // linking of rrdsets
+ // ------------------------------------------------------------------------
+ // local variables
+
+ avl_tree_lock variables_root_index;
+ RRDSETVAR *variables;
+ RRDCALC *calculations;
+
// ------------------------------------------------------------------------
// members for checking the data when loading from disk
avl_tree_lock dimensions_index; // the root of the dimensions index
RRDDIM *dimensions; // the actual data for every dimension
+
};
typedef struct rrdset RRDSET;
-extern RRDSET *rrdset_root;
-extern pthread_rwlock_t rrdset_root_rwlock;
+// ----------------------------------------------------------------------------
+// RRD HOST
+
+struct rrdhost {
+ avl avl;
+
+ char *hostname;
+
+ RRDSET *rrdset_root;
+ pthread_rwlock_t rrdset_root_rwlock;
+
+ avl_tree_lock rrdset_root_index;
+ avl_tree_lock rrdset_root_index_name;
+
+ avl_tree_lock rrdcontext_root_index;
+ avl_tree_lock variables_root_index;
+
+ // all RRDCALCs are primarily allocated and linked here
+ // RRDCALCs may be linked to charts at any point
+ // (charts may or may not exist when these are loaded)
+ RRDCALC *calculations;
+
+ // all variable references are linked here
+ // RRDVARs may be free'd, so every time this happens
+ // we need to find all their references and invalidate them
+ EVAL_VARIABLE *references;
+};
+typedef struct rrdhost RRDHOST;
+
+extern RRDHOST localhost;
// ----------------------------------------------------------------------------
// RRD SET functions
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-
-#include "log.h"
#include "common.h"
-#include "rrd2json.h"
#define HOSTNAME_MAX 1024
char *hostname = "unknown";
, rrd_default_history_entries
);
- pthread_rwlock_rdlock(&rrdset_root_rwlock);
- for(st = rrdset_root, c = 0; st ; st = st->next) {
+ pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+ for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
if(st->enabled && st->dimensions) {
if(c) buffer_strcat(wb, ",");
buffer_strcat(wb, "\n\t\t\"");
c++;
}
}
- pthread_rwlock_unlock(&rrdset_root_rwlock);
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
buffer_strcat(wb, "\n\t}\n}\n");
}
buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
- pthread_rwlock_rdlock(&rrdset_root_rwlock);
- for(st = rrdset_root, c = 0; st ; st = st->next) {
+ pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+ for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
if(st->enabled && st->dimensions) {
if(c) buffer_strcat(wb, ",\n");
memory += rrd_stats_one_json(st, NULL, wb);
c++;
}
}
- pthread_rwlock_unlock(&rrdset_root_rwlock);
+ pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
buffer_sprintf(wb, "\n\t],\n"
"\t\"hostname\": \"%s\",\n"
}
rrdr_unlock_rrdset(r);
- if(likely(r->t)) free(r->t);
- if(likely(r->v)) free(r->v);
- if(likely(r->o)) free(r->o);
- if(likely(r->od)) free(r->od);
- free(r);
+ freez(r->t);
+ freez(r->v);
+ freez(r->o);
+ freez(r->od);
+ freez(r);
}
inline void rrdr_done(RRDR *r)
return NULL;
}
- RRDR *r = calloc(1, sizeof(RRDR));
- if(unlikely(!r)) goto cleanup;
-
+ RRDR *r = callocz(1, sizeof(RRDR));
r->st = st;
rrdr_lock_rrdset(r);
r->n = n;
- r->t = malloc(n * sizeof(time_t));
- if(unlikely(!r->t)) goto cleanup;
-
- r->v = malloc(n * r->d * sizeof(calculated_number));
- if(unlikely(!r->v)) goto cleanup;
-
- r->o = malloc(n * r->d * sizeof(uint8_t));
- if(unlikely(!r->o)) goto cleanup;
-
- r->od = malloc(r->d * sizeof(uint8_t));
- if(unlikely(!r->od)) goto cleanup;
+ r->t = mallocz(n * sizeof(time_t));
+ r->v = mallocz(n * r->d * sizeof(calculated_number));
+ r->o = mallocz(n * r->d * sizeof(uint8_t));
+ r->od = mallocz(r->d * sizeof(uint8_t));
// set the hidden flag on hidden dimensions
int c;
}
r->c = -1;
-
r->group = 1;
r->update_every = 1;
return r;
-
-cleanup:
- error("Cannot allocate RRDR memory for %ld entries", n);
- if(likely(r)) rrdr_free(r);
- return NULL;
}
RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
-#include <time.h>
-
-#include "web_buffer.h"
-#include "rrd.h"
-
#ifndef NETDATA_RRD2JSON_H
#define NETDATA_RRD2JSON_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
#include "common.h"
-#include "log.h"
-#include "storage_number.h"
-
-#if __GNUC__
-#if __x86_64__ || __ppc64__
-#define ENVIRONMENT64
-#else
-#define ENVIRONMENT32
-#endif
-#endif
extern char *print_number_lu_r(char *str, unsigned long uvalue);
extern char *print_number_llu_r(char *str, unsigned long long uvalue);
-#include <stdint.h>
-
#ifndef NETDATA_STORAGE_NUMBER_H
#define NETDATA_STORAGE_NUMBER_H
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-#include <sys/stat.h>
-
#include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "log.h"
-#include "rrd.h"
-#include "main.h"
-#include "popen.h"
-#include "proc_self_mountinfo.h"
// ----------------------------------------------------------------------------
// cgroup globals
}
if(i != ca->cpus) {
- free(ca->cpu_percpu);
-
- ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
- if(!ca->cpu_percpu)
- fatal("Cannot allocate memory (%zu bytes)", sizeof(unsigned long long) * i);
-
- ca->cpus = i;
+ freez(ca->cpu_percpu);
+ ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
+ ca->cpus = (unsigned int)i;
}
for(i = 0; i < ca->cpus ;i++) {
trim(s);
- free(cg->chart_title);
- cg->chart_title = strdup(s);
- if(!cg->chart_title)
- fatal("CGROUP: Cannot allocate memory for chart name of cgroup '%s' chart name: '%s'", cg->id, s);
-
+ freez(cg->chart_title);
+ cg->chart_title = strdupz(s);
netdata_fix_chart_name(cg->chart_title);
- free(cg->chart_id);
- cg->chart_id = strdup(s);
- if(!cg->chart_id)
- fatal("CGROUP: Cannot allocate memory for chart id of cgroup '%s' chart id: '%s'", cg->id, s);
+ freez(cg->chart_id);
+ cg->chart_id = strdupz(s);
netdata_fix_chart_id(cg->chart_id);
}
}
- struct cgroup *cg = calloc(1, sizeof(struct cgroup));
- if(!cg) fatal("Cannot allocate memory for cgroup '%s'", id);
+ struct cgroup *cg = callocz(1, sizeof(struct cgroup));
debug(D_CGROUP, "adding cgroup '%s'", id);
- cg->id = strdup(id);
- if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
+ cg->id = strdupz(id);
cg->hash = simple_hash(cg->id);
- cg->chart_id = strdup(chart_id);
- if(!cg->chart_id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
- cg->chart_title = strdup(chart_id);
- if(!cg->chart_title) fatal("Cannot allocate memory for cgroup '%s'", id);
+ cg->chart_id = strdupz(chart_id);
+ cg->chart_title = strdupz(chart_id);
if(!cgroup_root)
cgroup_root = cg;
void cgroup_free(struct cgroup *cg) {
debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
- free(cg->cpuacct_usage.cpu_percpu);
+ freez(cg->cpuacct_usage.cpu_percpu);
- free(cg->cpuacct_stat.filename);
- free(cg->cpuacct_usage.filename);
- free(cg->memory.filename);
- free(cg->io_service_bytes.filename);
- free(cg->io_serviced.filename);
- free(cg->throttle_io_service_bytes.filename);
- free(cg->throttle_io_serviced.filename);
- free(cg->io_merged.filename);
- free(cg->io_queued.filename);
+ freez(cg->cpuacct_stat.filename);
+ freez(cg->cpuacct_usage.filename);
+ freez(cg->memory.filename);
+ freez(cg->io_service_bytes.filename);
+ freez(cg->io_serviced.filename);
+ freez(cg->throttle_io_service_bytes.filename);
+ freez(cg->throttle_io_serviced.filename);
+ freez(cg->io_merged.filename);
+ freez(cg->io_queued.filename);
- free(cg->id);
- free(cg->chart_id);
- free(cg->chart_title);
- free(cg);
+ freez(cg->id);
+ freez(cg->chart_id);
+ freez(cg->chart_title);
+ freez(cg);
cgroup_root_count--;
}
}
if(enabled) {
- char *s = malloc(dirlen + strlen(de->d_name) + 2);
- if(s) {
- strcpy(s, this);
- strcat(s, "/");
- strcat(s, de->d_name);
- find_dir_in_subdirs(base, s, callback);
- free(s);
- }
- else error("Cannot allocate memory.");
+ char *s = mallocz(dirlen + strlen(de->d_name) + 2);
+ strcpy(s, this);
+ strcat(s, "/");
+ strcat(s, de->d_name);
+ find_dir_in_subdirs(base, s, callback);
+ freez(s);
}
}
}
if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->cpuacct_stat.filename = strdup(filename);
+ cg->cpuacct_stat.filename = strdupz(filename);
debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
}
else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->cpuacct_usage.filename = strdup(filename);
+ cg->cpuacct_usage.filename = strdupz(filename);
debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
}
else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(cgroup_enable_memory && !cg->memory.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->memory.filename = strdup(filename);
+ cg->memory.filename = strdupz(filename);
debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
}
else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->io_service_bytes.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->io_service_bytes.filename = strdup(filename);
+ cg->io_service_bytes.filename = strdupz(filename);
debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
}
else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->io_serviced.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->io_serviced.filename = strdup(filename);
+ cg->io_serviced.filename = strdupz(filename);
debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
}
else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->throttle_io_service_bytes.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->throttle_io_service_bytes.filename = strdup(filename);
+ cg->throttle_io_service_bytes.filename = strdupz(filename);
debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
}
else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->throttle_io_serviced.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->throttle_io_serviced.filename = strdup(filename);
+ cg->throttle_io_serviced.filename = strdupz(filename);
debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
}
else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->io_merged.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->io_merged.filename = strdup(filename);
+ cg->io_merged.filename = strdupz(filename);
debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
}
else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
if(!cg->io_queued.filename) {
snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
if(stat(filename, &buf) != -1) {
- cg->io_queued.filename = strdup(filename);
+ cg->io_queued.filename = strdupz(filename);
debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
}
else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-typedef struct name_value {
+typedef struct ksm_name_value {
char filename[FILENAME_MAX + 1];
unsigned long long value;
-} NAME_VALUE;
+} KSM_NAME_VALUE;
#define PAGES_SHARED 0
#define PAGES_SHARING 1
#define PAGES_VOLATILE 3
#define PAGES_TO_SCAN 4
-NAME_VALUE values[] = {
+KSM_NAME_VALUE values[] = {
[PAGES_SHARED] = { "/sys/kernel/mm/ksm/pages_shared", 0ULL },
[PAGES_SHARING] = { "/sys/kernel/mm/ksm/pages_sharing", 0ULL },
[PAGES_UNSHARED] = { "/sys/kernel/mm/ksm/pages_unshared", 0ULL },
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <math.h>
-
#include "common.h"
-#include "storage_number.h"
-#include "rrd.h"
-#include "log.h"
-#include "web_buffer.h"
int check_storage_number(calculated_number n, int debug) {
char buffer[100];
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
#include "common.h"
-#include "log.h"
-#include "url.h"
// ----------------------------------------------------------------------------
// URL encode / decode
char *url_encode(char *str) {
char *buf, *pbuf;
- pbuf = buf = malloc(strlen(str) * 3 + 1);
-
- if(!buf)
- fatal("Cannot allocate memory.");
+ pbuf = buf = mallocz(strlen(str) * 3 + 1);
while (*str) {
if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
}
*pbuf = '\0';
- // FIX: I think this is prudent. URLs can be as long as 2 KiB or more.
- // We allocated 3 times more space to accomodate %NN encoding of
- // non ASCII chars. If URL has none of these kind of chars we will
- // end up with a big unused buffer.
- //
- // Try to shrink the buffer...
- if (!!(pbuf = (char *)realloc(buf, strlen(buf)+1)))
- buf = pbuf;
-
- return buf;
+ pbuf = strdupz(buf);
+ freez(buf);
+ return pbuf;
}
/* Returns a url-decoded version of str */
char *url_decode(char *str) {
size_t size = strlen(str) + 1;
- char *buf = malloc(size);
- if(!buf)
- fatal("Cannot allocate %zu bytes of memory.", size);
-
+ char *buf = mallocz(size);
return url_decode_r(buf, str, size);
}
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
#include "common.h"
-#include "web_buffer.h"
-#include "log.h"
#define BUFFER_OVERFLOW_EOF "EOF"
debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
- b = calloc(1, sizeof(BUFFER));
- if(!b) {
- error("Cannot allocate a web_buffer.");
- return NULL;
- }
-
- b->buffer = malloc(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- if(!b->buffer) {
- error("Cannot allocate a buffer of size %zu.", size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- free(b);
- return NULL;
- }
+ b = callocz(1, sizeof(BUFFER));
+ b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
b->buffer[0] = '\0';
b->size = size;
b->contenttype = CT_TEXT_PLAIN;
debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
- free(b->buffer);
- free(b);
+ freez(b->buffer);
+ freez(b);
}
void buffer_increase(BUFFER *b, size_t free_size_required)
debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
- b->buffer = realloc(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
- if(!b->buffer)
- fatal("Failed to increase data buffer from size %zu to %zu.",
- b->size + sizeof(BUFFER_OVERFLOW_EOF) + 2,
- b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
-
+ b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
b->size += increase;
buffer_overflow_init(b);
-#include <stdarg.h>
-#include <time.h>
-#include "storage_number.h"
-
#ifndef NETDATA_WEB_BUFFER_H
#define NETDATA_WEB_BUFFER_H 1
-#define WEB_DATA_LENGTH_INCREASE_STEP 16384
+#define WEB_DATA_LENGTH_INCREASE_STEP 1024
typedef struct web_buffer {
size_t size; // allocation size of buffer
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <math.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_buffer_svg.h"
#define BADGE_HORIZONTAL_PADDING 4
#define VERDANA_KERNING 0.5
-#include "web_buffer.h"
-#include "dictionary.h"
-
#ifndef NETDATA_WEB_BUFFER_SVG_H
#define NETDATA_WEB_BUFFER_SVG_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <pwd.h>
-#include <grp.h>
-#include <ctype.h>
-#include <poll.h>
-
-// TCP_CORK
-#include <netinet/tcp.h>
-
#include "common.h"
-#include "log.h"
-#include "main.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-#include "web_buffer_svg.h"
-#include "web_client.h"
#define INITIAL_WEB_DATA_LENGTH 16384
#define WEB_REQUEST_LENGTH 16384
{
struct web_client *w;
- w = calloc(1, sizeof(struct web_client));
- if(!w) {
- error("Cannot allocate new web_client memory.");
- return NULL;
- }
-
+ w = callocz(1, sizeof(struct web_client));
w->id = ++web_clients_count;
w->mode = WEB_CLIENT_MODE_NORMAL;
w->ifd = accept4(listener, sadr, &addrlen, SOCK_NONBLOCK);
if (w->ifd == -1) {
error("%llu: Cannot accept new incoming connection.", w->id);
- free(w);
+ freez(w);
return NULL;
}
w->ofd = w->ifd;
}
w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
- if(unlikely(!w->response.data)) {
- // no need for error log - web_buffer_create already logged the error
- close(w->ifd);
- free(w);
- return NULL;
- }
-
w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
- if(unlikely(!w->response.header)) {
- // no need for error log - web_buffer_create already logged the error
- buffer_free(w->response.data);
- close(w->ifd);
- free(w);
- return NULL;
- }
-
w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
- if(unlikely(!w->response.header_output)) {
- // no need for error log - web_buffer_create already logged the error
- buffer_free(w->response.header);
- buffer_free(w->response.data);
- close(w->ifd);
- free(w);
- return NULL;
- }
-
w->origin[0] = '*';
w->wait_receive = 1;
if(w->response.data) buffer_free(w->response.data);
if(w->ifd != -1) close(w->ifd);
if(w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
- free(w);
+ freez(w);
global_statistics.connected_clients--;
if(!strcmp(name, "chart")) chart = value;
else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
if(!dimensions)
- dimensions = buffer_create(strlen(value));
+ dimensions = buffer_create(100);
- if(dimensions) {
- buffer_strcat(dimensions, "|");
- buffer_strcat(dimensions, value);
- }
+ buffer_strcat(dimensions, "|");
+ buffer_strcat(dimensions, value);
}
else if(!strcmp(name, "after")) after_str = value;
else if(!strcmp(name, "before")) before_str = value;
if(!strcmp(name, "chart")) chart = value;
else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
- if(!dimensions) dimensions = buffer_create(strlen(value));
- if(dimensions) {
- buffer_strcat(dimensions, "|");
- buffer_strcat(dimensions, value);
- }
+ if(!dimensions) dimensions = buffer_create(100);
+ buffer_strcat(dimensions, "|");
+ buffer_strcat(dimensions, value);
}
else if(!strcmp(name, "after")) after_str = value;
else if(!strcmp(name, "before")) before_str = value;
debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
buffer_flush(w->response.data);
- RRDSET *st = rrdset_root;
+ RRDSET *st = localhost.rrdset_root;
for ( ; st ; st = st->next )
buffer_sprintf(w->response.data, "%s\n", st->name);
buffer_strcat(w->response.data, "I am doing it already");
error("web request to exit received.");
+ netdata_cleanup_and_exit(0);
netdata_exit = 1;
}
else if(hash == hash_debug && strcmp(tok, "debug") == 0) {
-
-#ifdef NETDATA_WITH_ZLIB
-#include <zlib.h>
-#endif
-
-#include <sys/time.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include "web_buffer.h"
-#include "dictionary.h"
-
#ifndef NETDATA_WEB_CLIENT_H
#define NETDATA_WEB_CLIENT_H 1
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <netinet/tcp.h>
-#include <malloc.h>
-#include <poll.h>
-#include <ctype.h>
-
#include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_client.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "../config.h"
int listen_backlog = LISTEN_BACKLOG;
-int listen_fds_count = 0;
+size_t listen_fds_count = 0;
int listen_fds[MAX_LISTEN_FDS] = { [0 ... 99] = -1 };
char *listen_fds_names[MAX_LISTEN_FDS] = { [0 ... 99] = NULL };
int listen_port = LISTEN_PORT;
char buffer[100 + 1];
snprintfz(buffer, 100, "[%s]:%d", ip, port);
- listen_fds_names[listen_fds_count] = strdup(buffer);
+ listen_fds_names[listen_fds_count] = strdupz(buffer);
listen_fds_count++;
return 0;
}
int is_listen_socket(int fd) {
- int i;
+ size_t i;
for(i = 0; i < listen_fds_count ;i++)
if(listen_fds[i] == fd) return 1;
}
static inline void close_listen_sockets(void) {
- int i;
+ size_t i;
for(i = 0; i < listen_fds_count ;i++) {
close(listen_fds[i]);
listen_fds[i] = -1;
- if(listen_fds_names[i]) free(listen_fds_names[i]);
+ freez(listen_fds_names[i]);
listen_fds_names[i] = NULL;
}
static inline int bind_to_one(const char *definition, int default_port, int listen_backlog) {
int added = 0;
struct addrinfo hints;
- struct addrinfo *result, *rp;
+ struct addrinfo *result = NULL, *rp = NULL;
char buffer[strlen(definition) + 1];
strcpy(buffer, definition);
}
}
+ freeaddrinfo(result);
+
return added;
}
if(!listen_fds_count)
fatal("Cannot listen on any socket. Exiting...");
- return listen_fds_count;
+ return (int)listen_fds_count;
}
// --------------------------------------------------------------------------------------
if(!listen_fds_count)
fatal("LISTENER: No sockets to listen to.");
- struct pollfd *fds = calloc(sizeof(struct pollfd), listen_fds_count);
- if(!fds)
- fatal("LISTENER: Cannot allocate memory for poll fds.");
+ struct pollfd *fds = callocz(sizeof(struct pollfd), listen_fds_count);
- int i;
+ size_t i;
for(i = 0; i < listen_fds_count ;i++) {
fds[i].fd = listen_fds[i];
fds[i].events = POLLIN;
struct web_client *w;
int retval;
- if(ptr) { ; }
-
if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
error("Cannot set pthread cancel type to DEFERRED.");
if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
error("Cannot set pthread cancel state to ENABLE.");
- int i;
-
if(!listen_fds_count)
fatal("LISTENER: no listen sockets available.");
- for(i = 0; i < FD_SETSIZE ; i++)
+ size_t i;
+ for(i = 0; i < FD_SETSIZE ; i++)
single_threaded_clients[i] = NULL;
fd_set ifds, ofds, efds, rifds, rofds, refds;
}
}
- for(i = 0 ; i <= fdmax ; i++) {
+ for(i = 0 ; i <= (size_t)fdmax ; i++) {
if(likely(!FD_ISSET(i, &rifds) && !FD_ISSET(i, &rofds) && !FD_ISSET(i, &refds)))
continue;
},
'system.load': {
- info: 'Current system load read from <code>/proc/loadavg</code>.',
+ info: 'Current system load, a measurement of the work the system is performing. A completely idle computer has a load average of 0. Each process either using or waiting for system resources (e.g. CPU, disk) adds 1 to the load average. So, if your system has a load of 5, five processes are either using or waiting for system resources. Linux calculates this once every 5 seconds. Netdata reads it from <code>/proc/loadavg</code>. For more information see: <a href="http://www.howtogeek.com/194642/understanding-the-load-average-on-linux-and-other-unix-like-systems/" target="_blank">this article</a>',
height: 0.7
},