splitted netdata to multiple source files
Costa Tsaousis (ktsaou) committed
Mar 16, 2015 at 16:31 UTC
037bbc245f57e45f9e6f56c705957e38586d7218
53 files changed
+8784
src/Makefile
new
+31
@@ -0,0 +1,31 @@
1
+# COMMON_FLAGS =
2
+
3
+CONFIG_DIR = "conf.d"
4
+LOG_DIR = "log"
5
+PLUGINS_DIR = "plugins.d"
6
+
7
+COMMON_FLAGS = -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)'
8
+
9
+ifdef final
10
+CFLAGS = -Wall -Wextra -O3 $(COMMON_FLAGS)
11
+else
12
+CFLAGS = -Wall -Wextra -ggdb $(COMMON_FLAGS)
13
+endif
14
+
15
+CC = gcc $(CFLAGS)
16
+
17
+proc_sources = proc_net_dev.c proc_net_ip_vs_stats.c proc_diskstats.c proc_meminfo.c proc_net_netstat.c proc_net_snmp.c proc_net_stat_conntrack.c proc_stat.c proc_vmstat.c
18
+sources = procfile.c common.c log.c popen.c url.c config.c web_buffer.c storage_number.c web_client.c global_statistics.c rrd.c rrd2json.c web_server.c plugins_d.c daemon.c plugin_tc.c plugin_checks.c plugin_idlejitter.c plugin_proc.c unit_test.c main.c
19
+headers = $(patsubst %.c,%.h,$(sources))
20
+objects = $(patsubst %.c,%.o,$(sources) $(proc_sources))
21
+
22
+all: netdata
23
+
24
+netdata: $(objects)
25
+ $(CC) -o netdata $(objects) -pthread -lz
26
+
27
+%.o: %.c ${headers}
28
+ $(CC) -c $< -o $@
29
+
30
+clean:
31
+ rm -rfv *.o netdata core
src/Makefile.save
new
+25
@@ -0,0 +1,25 @@
1
+# COMMON_FLAGS =
2
+
3
+ifdef final
4
+CFLAGS = -Wall -Wextra -I $(INCDIR) -O3 $(COMMON_FLAGS)
5
+else
6
+CFLAGS = -Wall -Wextra -ggdb -I $(INCDIR) $(COMMON_FLAGS)
7
+endif
8
+
9
+CC = gcc $(CFLAGS)
10
+
11
+sources = *.c
12
+headers = *.h
13
+objects = $(patsubst %.o,%.c,$(sources))
14
+
15
+all: netdata
16
+
17
+netdata: $(objects) $(headers) $(sources)
18
+ echo "${sources}
19
+ $(CC) -o netdata $(objects) -pthread -lzlib
20
+
21
+$(objects): %.c $(headers)
22
+ $(CC) -c $< -o $@
23
+
24
+clean:
25
+ rm -rfv *.o netdata core
src/common.c
new
+179
@@ -0,0 +1,179 @@
1
+// enable O_NOATIME
2
+#define _GNU_SOURCE
3
+
4
+#include <string.h>
5
+#include <ctype.h>
6
+#include <errno.h>
7
+#include <unistd.h>
8
+#include <sys/types.h>
9
+#include <sys/stat.h>
10
+#include <fcntl.h>
11
+#include <sys/mman.h>
12
+#include <malloc.h>
13
+
14
+
15
+#include "log.h"
16
+#include "common.h"
17
+#include "web_client.h"
18
+
19
+// TODO: this can be optimized to avoid strlen()
20
+unsigned long simple_hash(const char *name)
21
+{
22
+ int i, len = strlen(name);
23
+ unsigned long hash = 0;
24
+
25
+ for(i = 0; i < len ;i++) hash += (i * name[i]) + i + name[i];
26
+
27
+ return hash;
28
+}
29
+
30
+void strreverse(char* begin, char* end)
31
+{
32
+ char aux;
33
+ while (end > begin)
34
+ aux = *end, *end-- = *begin, *begin++ = aux;
35
+}
36
+
37
+char *mystrsep(char **ptr, char *s)
38
+{
39
+ char *p = "";
40
+ while ( p && !p[0] && *ptr ) p = strsep(ptr, s);
41
+ return(p);
42
+}
43
+
44
+// like strsep() but:
45
+// it trims spaces before and after each value
46
+// it accepts quoted values in single or double quotes
47
+char *qstrsep(char **ptr)
48
+{
49
+ if(!*ptr || !**ptr) return NULL;
50
+
51
+ char *s, *p = *ptr;
52
+
53
+ // skip leading spaces
54
+ while(isspace(*p)) p++;
55
+
56
+ // if the first char is a quote, assume quoted
57
+ if(*p == '"' || *p == '\'') {
58
+ char q = *p;
59
+ s = ++p;
60
+ while(*p && *p != q) p++;
61
+
62
+ if(*p == q) {
63
+ *p = '\0';
64
+ p++;
65
+ }
66
+
67
+ *ptr = p;
68
+ return s;
69
+ }
70
+
71
+ s = p;
72
+ while(*p && !isspace(*p)) p++;
73
+ if(!*p) *ptr = NULL;
74
+ else {
75
+ *p = '\0';
76
+ *ptr = ++p;
77
+ }
78
+
79
+ return s;
80
+}
81
+
82
+char *trim(char *s)
83
+{
84
+ // skip leading spaces
85
+ while(*s && isspace(*s)) s++;
86
+ if(!*s || *s == '#') return NULL;
87
+
88
+ // skip tailing spaces
89
+ int c = strlen(s) - 1;
90
+ while(c >= 0 && isspace(s[c])) {
91
+ s[c] = '\0';
92
+ c--;
93
+ }
94
+ if(c < 0) return NULL;
95
+ if(!*s) return NULL;
96
+ return s;
97
+}
98
+
99
+void *mymmap(const char *filename, unsigned long size, int flags)
100
+{
101
+ int fd;
102
+ void *mem = NULL;
103
+
104
+ errno = 0;
105
+ fd = open(filename, O_RDWR|O_CREAT|O_NOATIME, 0664);
106
+ if(fd != -1) {
107
+ if(lseek(fd, size, SEEK_SET) == (long)size) {
108
+ if(write(fd, "", 1) == 1) {
109
+
110
+ if(ftruncate(fd, size))
111
+ error("Cannot truncate file '%s' to size %ld. Will use the larger file.", filename, size);
112
+
113
+ mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags, fd, 0);
114
+ if(mem) {
115
+ if(madvise(mem, size, MADV_SEQUENTIAL|MADV_DONTFORK|MADV_WILLNEED) != 0)
116
+ error("Cannot advise the kernel about the memory usage of file '%s'.", filename);
117
+ }
118
+ }
119
+ else error("Cannot write to file '%s' at position %ld.", filename, size);
120
+ }
121
+ else error("Cannot seek file '%s' to size %ld.", filename, size);
122
+
123
+ close(fd);
124
+ }
125
+ else error("Cannot create/open file '%s'.", filename);
126
+
127
+ return mem;
128
+}
129
+
130
+int savememory(const char *filename, void *mem, unsigned long size)
131
+{
132
+ char tmpfilename[FILENAME_MAX + 1];
133
+
134
+ snprintf(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long)getpid());
135
+
136
+ int fd = open(tmpfilename, O_RDWR|O_CREAT|O_NOATIME, 0664);
137
+ if(fd < 0) {
138
+ error("Cannot create/open file '%s'.", filename);
139
+ return -1;
140
+ }
141
+
142
+ if(write(fd, mem, size) != (long)size) {
143
+ error("Cannot write to file '%s' %ld bytes.", filename, (long)size);
144
+ close(fd);
145
+ return -1;
146
+ }
147
+
148
+ close(fd);
149
+
150
+ int ret = 0;
151
+ if(rename(tmpfilename, filename)) {
152
+ error("Cannot rename '%s' to '%s'", tmpfilename, filename);
153
+ ret = -1;
154
+ }
155
+
156
+ return ret;
157
+}
158
+
159
+void log_allocations(void)
160
+{
161
+ static int mem = 0;
162
+
163
+ struct mallinfo mi;
164
+
165
+ mi = mallinfo();
166
+ if(mi.uordblks > mem) {
167
+ int clients = 0;
168
+ struct web_client *w;
169
+ for(w = web_clients; w ; w = w->next) clients++;
170
+
171
+ info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
172
+ mem = mi.uordblks;
173
+ }
174
+}
175
+
176
+int fd_is_valid(int fd) {
177
+ return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
178
+}
179
+
src/common.h
new
+20
@@ -0,0 +1,20 @@
1
+#ifndef NETDATA_COMMON_H
2
+#define NETDATA_COMMON_H 1
3
+
4
+#define abs(x) ((x < 0)? -x : x)
5
+#define usecdiff(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
6
+
7
+extern unsigned long simple_hash(const char *name);
8
+extern void strreverse(char* begin, char* end);
9
+extern char *mystrsep(char **ptr, char *s);
10
+extern char *qstrsep(char **ptr);
11
+extern char *trim(char *s);
12
+
13
+extern void *mymmap(const char *filename, unsigned long size, int flags);
14
+extern int savememory(const char *filename, void *mem, unsigned long size);
15
+
16
+extern void log_allocations(void);
17
+
18
+extern int fd_is_valid(int fd);
19
+
20
+#endif /* NETDATA_COMMON_H */
src/config.c
new
+363
@@ -0,0 +1,363 @@
1
+#include <pthread.h>
2
+#include <stdlib.h>
3
+#include <string.h>
4
+
5
+#include "common.h"
6
+#include "config.h"
7
+#include "log.h"
8
+
9
+#define CONFIG_FILE_LINE_MAX 4096
10
+
11
+pthread_rwlock_t config_rwlock = PTHREAD_RWLOCK_INITIALIZER;
12
+
13
+struct config_value {
14
+ char name[CONFIG_MAX_NAME + 1];
15
+ char value[CONFIG_MAX_VALUE + 1];
16
+
17
+ unsigned long hash; // a simple hash to speed up searching
18
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
19
+
20
+ int loaded; // loaded from the user config
21
+ int used; // has been accessed from the program
22
+ int changed; // changed from the internal default
23
+
24
+ struct config_value *next;
25
+};
26
+
27
+struct config {
28
+ char name[CONFIG_MAX_NAME + 1];
29
+
30
+ unsigned long hash; // a simple hash to speed up searching
31
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
32
+
33
+ struct config_value *values;
34
+
35
+ struct config *next;
36
+} *config_root = NULL;
37
+
38
+struct config_value *config_value_create(struct config *co, const char *name, const char *value)
39
+{
40
+ debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
41
+
42
+ struct config_value *cv = calloc(1, sizeof(struct config_value));
43
+ if(!cv) fatal("Cannot allocate config_value");
44
+
45
+ strncpy(cv->name, name, CONFIG_MAX_NAME);
46
+ strncpy(cv->value, value, CONFIG_MAX_VALUE);
47
+ cv->hash = simple_hash(cv->name);
48
+
49
+ // no need for string termination, due to calloc()
50
+
51
+ struct config_value *cv2 = co->values;
52
+ if(cv2) {
53
+ while (cv2->next) cv2 = cv2->next;
54
+ cv2->next = cv;
55
+ }
56
+ else co->values = cv;
57
+
58
+ return cv;
59
+}
60
+
61
+struct config *config_create(const char *section)
62
+{
63
+ debug(D_CONFIG, "Creating section '%s'.", section);
64
+
65
+ struct config *co = calloc(1, sizeof(struct config));
66
+ if(!co) fatal("Cannot allocate config");
67
+
68
+ strncpy(co->name, section, CONFIG_MAX_NAME);
69
+ co->hash = simple_hash(co->name);
70
+
71
+ // no need for string termination, due to calloc()
72
+
73
+ struct config *co2 = config_root;
74
+ if(co2) {
75
+ while (co2->next) co2 = co2->next;
76
+ co2->next = co;
77
+ }
78
+ else config_root = co;
79
+
80
+ return co;
81
+}
82
+
83
+struct config *config_find_section(const char *section)
84
+{
85
+ struct config *co;
86
+ unsigned long hash = simple_hash(section);
87
+
88
+ for(co = config_root; co ; co = co->next)
89
+ if(hash == co->hash)
90
+ if(strcmp(co->name, section) == 0)
91
+ break;
92
+
93
+ return co;
94
+}
95
+
96
+int load_config(char *filename, int overwrite_used)
97
+{
98
+ int line = 0;
99
+ struct config *co = NULL;
100
+
101
+ pthread_rwlock_wrlock(&config_rwlock);
102
+
103
+ char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
104
+
105
+ if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
106
+ FILE *fp = fopen(filename, "r");
107
+ if(!fp) {
108
+ error("Cannot open file '%s'", CONFIG_DIR "/" CONFIG_FILENAME);
109
+ pthread_rwlock_unlock(&config_rwlock);
110
+ return 0;
111
+ }
112
+
113
+ while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
114
+ buffer[CONFIG_FILE_LINE_MAX] = '\0';
115
+ line++;
116
+
117
+ s = trim(buffer);
118
+ if(!s) {
119
+ debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
120
+ continue;
121
+ }
122
+
123
+ int len = strlen(s);
124
+ if(*s == '[' && s[len - 1] == ']') {
125
+ // new section
126
+ s[len - 1] = '\0';
127
+ s++;
128
+
129
+ co = config_find_section(s);
130
+ if(!co) co = config_create(s);
131
+
132
+ continue;
133
+ }
134
+
135
+ if(!co) {
136
+ // line outside a section
137
+ error("Ignoring line %d ('%s'), it is outsize all sections.", line, s);
138
+ continue;
139
+ }
140
+
141
+ char *name = s;
142
+ char *value = strchr(s, '=');
143
+ if(!value) {
144
+ error("Ignoring line %d ('%s'), there is no = in it.", line, s);
145
+ continue;
146
+ }
147
+ *value = '\0';
148
+ value++;
149
+
150
+ name = trim(name);
151
+ value = trim(value);
152
+
153
+ if(!name) {
154
+ error("Ignoring line %d, name is empty.", line);
155
+ continue;
156
+ }
157
+ if(!value) {
158
+ debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
159
+ continue;
160
+ }
161
+
162
+ struct config_value *cv;
163
+ for(cv = co->values; cv ; cv = cv->next)
164
+ if(strcmp(cv->name, name) == 0) break;
165
+
166
+ if(!cv) cv = config_value_create(co, name, value);
167
+ else {
168
+ if((cv->used && overwrite_used) || !cv->used) {
169
+ debug(D_CONFIG, "Overwriting '%s/%s'.", line, co->name, cv->name);
170
+ strncpy(cv->value, value, CONFIG_MAX_VALUE);
171
+ // termination is already there
172
+ }
173
+ else
174
+ debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
175
+ }
176
+ cv->loaded = 1;
177
+ }
178
+
179
+ fclose(fp);
180
+
181
+ pthread_rwlock_unlock(&config_rwlock);
182
+ return 1;
183
+}
184
+
185
+char *config_get(const char *section, const char *name, const char *default_value)
186
+{
187
+ struct config_value *cv;
188
+
189
+ debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
190
+
191
+ pthread_rwlock_rdlock(&config_rwlock);
192
+
193
+ struct config *co = config_find_section(section);
194
+ if(!co) co = config_create(section);
195
+
196
+ unsigned long hash = simple_hash(name);
197
+ for(cv = co->values; cv ; cv = cv->next)
198
+ if(hash == cv->hash)
199
+ if(strcmp(cv->name, name) == 0)
200
+ break;
201
+
202
+ if(!cv) cv = config_value_create(co, name, default_value);
203
+ cv->used = 1;
204
+
205
+ if(cv->loaded || cv->changed) {
206
+ // this is a loaded value from the config file
207
+ // if it is different that the default, mark it
208
+ if(strcmp(cv->value, default_value) != 0) cv->changed = 1;
209
+ }
210
+ else {
211
+ // this is not loaded from the config
212
+ // copy the default value to it
213
+ strncpy(cv->value, default_value, CONFIG_MAX_VALUE);
214
+ }
215
+
216
+ pthread_rwlock_unlock(&config_rwlock);
217
+ return(cv->value);
218
+}
219
+
220
+long long config_get_number(const char *section, const char *name, long long value)
221
+{
222
+ char buffer[100], *s;
223
+ sprintf(buffer, "%lld", value);
224
+
225
+ s = config_get(section, name, buffer);
226
+ return strtoll(s, NULL, 0);
227
+}
228
+
229
+int config_get_boolean(const char *section, const char *name, int value)
230
+{
231
+ char *s;
232
+ if(value) s = "yes";
233
+ else s = "no";
234
+
235
+ s = config_get(section, name, s);
236
+
237
+ if(strcmp(s, "yes") == 0 || strcmp(s, "true") == 0 || strcmp(s, "1") == 0) {
238
+ strcpy(s, "yes");
239
+ return 1;
240
+ }
241
+ else {
242
+ strcpy(s, "no");
243
+ return 0;
244
+ }
245
+}
246
+
247
+const char *config_set(const char *section, const char *name, const char *value)
248
+{
249
+ struct config_value *cv;
250
+
251
+ debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
252
+
253
+ pthread_rwlock_wrlock(&config_rwlock);
254
+
255
+ struct config *co = config_find_section(section);
256
+ if(!co) co = config_create(section);
257
+
258
+ unsigned long hash = simple_hash(name);
259
+ for(cv = co->values; cv ; cv = cv->next)
260
+ if(hash == cv->hash)
261
+ if(strcmp(cv->name, name) == 0)
262
+ break;
263
+
264
+ if(!cv) cv = config_value_create(co, name, value);
265
+ cv->used = 1;
266
+
267
+ if(strcmp(cv->value, value) != 0) cv->changed = 1;
268
+
269
+ strncpy(cv->value, value, CONFIG_MAX_VALUE);
270
+ // termination is already there
271
+
272
+ pthread_rwlock_unlock(&config_rwlock);
273
+
274
+ return value;
275
+}
276
+
277
+long long config_set_number(const char *section, const char *name, long long value)
278
+{
279
+ char buffer[100];
280
+ sprintf(buffer, "%lld", value);
281
+
282
+ config_set(section, name, buffer);
283
+
284
+ return value;
285
+}
286
+
287
+int config_set_boolean(const char *section, const char *name, int value)
288
+{
289
+ char *s;
290
+ if(value) s = "yes";
291
+ else s = "no";
292
+
293
+ config_set(section, name, s);
294
+
295
+ return value;
296
+}
297
+
298
+void generate_config(struct web_buffer *wb, int only_changed)
299
+{
300
+ int i, pri;
301
+ struct config *co;
302
+ struct config_value *cv;
303
+
304
+ for(i = 0; i < 3 ;i++) {
305
+ web_buffer_increase(wb, 500);
306
+ switch(i) {
307
+ case 0:
308
+ web_buffer_printf(wb,
309
+ "# NetData Configuration\n"
310
+ "# You can uncomment and change any of the options bellow.\n"
311
+ "# The value shown in the commented settings, is the default value.\n"
312
+ "\n# global netdata configuration\n");
313
+ break;
314
+
315
+ case 1:
316
+ web_buffer_printf(wb, "\n\n# per plugin configuration\n");
317
+ break;
318
+
319
+ case 2:
320
+ web_buffer_printf(wb, "\n\n# per chart configuration\n");
321
+ break;
322
+ }
323
+
324
+ for(co = config_root; co ; co = co->next) {
325
+ if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0) pri = 0;
326
+ else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
327
+ else pri = 2;
328
+
329
+ if(i == pri) {
330
+ int used = 0;
331
+ int changed = 0;
332
+ int count = 0;
333
+ for(cv = co->values; cv ; cv = cv->next) {
334
+ used += cv->used;
335
+ changed += cv->changed;
336
+ count++;
337
+ }
338
+
339
+ if(!count) continue;
340
+ if(only_changed && !changed) continue;
341
+
342
+ if(!used) {
343
+ web_buffer_increase(wb, 500);
344
+ web_buffer_printf(wb, "\n# node '%s' is not used.", co->name);
345
+ }
346
+
347
+ web_buffer_increase(wb, CONFIG_MAX_NAME + 4);
348
+ web_buffer_printf(wb, "\n[%s]\n", co->name);
349
+
350
+ for(cv = co->values; cv ; cv = cv->next) {
351
+
352
+ if(used && !cv->used) {
353
+ web_buffer_increase(wb, CONFIG_MAX_NAME + 200);
354
+ web_buffer_printf(wb, "\n\t# option '%s' is not used.\n", cv->name);
355
+ }
356
+ web_buffer_increase(wb, CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 5);
357
+ web_buffer_printf(wb, "\t%s%s = %s\n", (!cv->changed && cv->used)?"# ":"", cv->name, cv->value);
358
+ }
359
+ }
360
+ }
361
+ }
362
+}
363
+
src/config.h
new
+22
@@ -0,0 +1,22 @@
1
+#include "web_buffer.h"
2
+
3
+#ifndef NETDATA_CONFIG_H
4
+#define NETDATA_CONFIG_H 1
5
+
6
+#define CONFIG_MAX_NAME 100
7
+#define CONFIG_MAX_VALUE 1024
8
+#define CONFIG_FILENAME "netdata.conf"
9
+
10
+extern int load_config(char *filename, int overwrite_used);
11
+
12
+extern char *config_get(const char *section, const char *name, const char *default_value);
13
+extern long long config_get_number(const char *section, const char *name, long long value);
14
+extern int config_get_boolean(const char *section, const char *name, int value);
15
+
16
+extern const char *config_set(const char *section, const char *name, const char *value);
17
+extern long long config_set_number(const char *section, const char *name, long long value);
18
+extern int config_set_boolean(const char *section, const char *name, int value);
19
+
20
+extern void generate_config(struct web_buffer *wb, int only_changed);
21
+
22
+#endif /* NETDATA_CONFIG_H */
src/daemon.c
new
+281
@@ -0,0 +1,281 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <fcntl.h>
5
+#include <errno.h>
6
+#include <signal.h>
7
+#include <string.h>
8
+#include <sys/types.h>
9
+#include <pwd.h>
10
+#include <pthread.h>
11
+#include <sys/wait.h>
12
+#include <sys/stat.h>
13
+
14
+#include "config.h"
15
+#include "log.h"
16
+#include "common.h"
17
+#include "web_client.h"
18
+#include "plugins_d.h"
19
+#include "rrd.h"
20
+#include "popen.h"
21
+#include "main.h"
22
+#include "daemon.h"
23
+
24
+void sig_handler(int signo)
25
+{
26
+ switch(signo) {
27
+ case SIGTERM:
28
+ case SIGQUIT:
29
+ case SIGINT:
30
+ case SIGHUP:
31
+ case SIGFPE:
32
+ case SIGSEGV:
33
+ debug(D_EXIT, "Signaled exit (signal %d). Errno: %d (%s)", signo, errno, strerror(errno));
34
+ signal(SIGCHLD, SIG_IGN);
35
+ signal(SIGPIPE, SIG_IGN);
36
+ signal(SIGTERM, SIG_IGN);
37
+ signal(SIGQUIT, SIG_IGN);
38
+ signal(SIGHUP, SIG_IGN);
39
+ signal(SIGINT, SIG_IGN);
40
+ kill_childs();
41
+ process_childs(0);
42
+ rrd_stats_free_all();
43
+ //unlink("/var/run/netdata.pid");
44
+ info("NetData exiting. Bye bye...");
45
+ exit(1);
46
+ break;
47
+
48
+ case SIGPIPE:
49
+ info("Ignoring signal %d. Errno: %d (%s)", signo, errno, strerror(errno));
50
+ break;
51
+
52
+
53
+ case SIGCHLD:
54
+ info("Received SIGCHLD (signal %d).", signo);
55
+ process_childs(0);
56
+ break;
57
+
58
+ default:
59
+ info("Signal %d received. Falling back to default action for it.", signo);
60
+ signal(signo, SIG_DFL);
61
+ break;
62
+ }
63
+}
64
+
65
+char rundir[FILENAME_MAX + 1] = "/run/netdata";
66
+char pidfile[FILENAME_MAX + 1] = "";
67
+void prepare_rundir() {
68
+ if(getuid() != 0) {
69
+ mkdir("/run/user", 0775);
70
+ snprintf(rundir, FILENAME_MAX, "/run/user/%d", getpid());
71
+ mkdir(rundir, 0775);
72
+ snprintf(rundir, FILENAME_MAX, "/run/user/%d/netdata", getpid());
73
+ }
74
+
75
+ snprintf(pidfile, FILENAME_MAX, "%s/netdata.pid", rundir);
76
+
77
+ if(mkdir(rundir, 0775) != 0)
78
+ fprintf(stderr, "Cannot create directory '%s' (%s).", rundir, strerror(errno));
79
+}
80
+
81
+int become_user(const char *username)
82
+{
83
+ struct passwd *pw = getpwnam(username);
84
+ if(!pw) {
85
+ fprintf(stderr, "User %s is not present. Error: %s\n", username, strerror(errno));
86
+ return -1;
87
+ }
88
+
89
+ if(chown(rundir, pw->pw_uid, pw->pw_gid) != 0) {
90
+ fprintf(stderr, "Cannot chown directory '%s' to user %s. Error: %s\n", rundir, username, strerror(errno));
91
+ return -1;
92
+ }
93
+
94
+ if(setgid(pw->pw_gid) != 0) {
95
+ fprintf(stderr, "Cannot switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno));
96
+ return -1;
97
+ }
98
+ if(setegid(pw->pw_gid) != 0) {
99
+ fprintf(stderr, "Cannot effectively switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno));
100
+ return -1;
101
+ }
102
+ if(setuid(pw->pw_uid) != 0) {
103
+ fprintf(stderr, "Cannot switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno));
104
+ return -1;
105
+ }
106
+ if(seteuid(pw->pw_uid) != 0) {
107
+ fprintf(stderr, "Cannot effectively switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno));
108
+ return -1;
109
+ }
110
+
111
+ return(0);
112
+}
113
+
114
+int become_daemon(int close_all_files, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp)
115
+{
116
+ fflush(NULL);
117
+
118
+ // open the files before forking
119
+ int input_fd = -1, output_fd = -1, error_fd = -1, dev_null = -1;
120
+
121
+ if(input && *input) {
122
+ if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
123
+ fprintf(stderr, "Cannot open input file '%s' (%s).", input, strerror(errno));
124
+ return -1;
125
+ }
126
+ }
127
+
128
+ if(output && *output) {
129
+ if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
130
+ fprintf(stderr, "Cannot open output log file '%s' (%s).", output, strerror(errno));
131
+ if(input_fd != -1) close(input_fd);
132
+ return -1;
133
+ }
134
+ }
135
+
136
+ if(error && *error) {
137
+ if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
138
+ fprintf(stderr, "Cannot open error log file '%s' (%s).", error, strerror(errno));
139
+ if(input_fd != -1) close(input_fd);
140
+ if(output_fd != -1) close(output_fd);
141
+ return -1;
142
+ }
143
+ }
144
+
145
+ if(access && *access && access_fd) {
146
+ if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
147
+ fprintf(stderr, "Cannot open access log file '%s' (%s).", access, strerror(errno));
148
+ if(input_fd != -1) close(input_fd);
149
+ if(output_fd != -1) close(output_fd);
150
+ if(error_fd != -1) close(error_fd);
151
+ return -1;
152
+ }
153
+
154
+ if(access_fp) {
155
+ *access_fp = fdopen(*access_fd, "w");
156
+ if(!*access_fp) {
157
+ fprintf(stderr, "Cannot migrate file's '%s' fd %d (%s).\n", access, *access_fd, strerror(errno));
158
+ if(input_fd != -1) close(input_fd);
159
+ if(output_fd != -1) close(output_fd);
160
+ if(error_fd != -1) close(error_fd);
161
+ close(*access_fd);
162
+ *access_fd = -1;
163
+ return -1;
164
+ }
165
+ }
166
+ }
167
+
168
+ if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
169
+ perror("Cannot open /dev/null");
170
+ if(input_fd != -1) close(input_fd);
171
+ if(output_fd != -1) close(output_fd);
172
+ if(error_fd != -1) close(error_fd);
173
+ if(access && access_fd && *access_fd != -1) {
174
+ close(*access_fd);
175
+ *access_fd = -1;
176
+ if(access_fp) {
177
+ fclose(*access_fp);
178
+ *access_fp = NULL;
179
+ }
180
+ }
181
+ return -1;
182
+ }
183
+
184
+ // all files opened
185
+ // lets do it
186
+
187
+ int i = fork();
188
+ if(i == -1) {
189
+ perror("cannot fork");
190
+ exit(1);
191
+ }
192
+ if(i != 0) {
193
+ exit(0); // the parent
194
+ }
195
+
196
+ // become session leader
197
+ if (setsid() < 0)
198
+ exit(2);
199
+
200
+ signal(SIGCHLD, SIG_IGN);
201
+ signal(SIGHUP, SIG_IGN);
202
+ signal(SIGWINCH, SIG_IGN);
203
+
204
+ // fork() again
205
+ i = fork();
206
+ if(i == -1) {
207
+ perror("cannot fork");
208
+ exit(1);
209
+ }
210
+ if(i != 0) {
211
+ exit(0); // the parent
212
+ }
213
+
214
+ // Set new file permissions
215
+ umask(0);
216
+
217
+ // close all files
218
+ if(close_all_files) {
219
+ for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
220
+ if(
221
+ ((access_fd && i != *access_fd) || !access_fd)
222
+ && i != dev_null
223
+ && i != input_fd
224
+ && i != output_fd
225
+ && i != error_fd
226
+ && fd_is_valid(i)
227
+ ) close(i);
228
+ }
229
+ else {
230
+ close(STDIN_FILENO);
231
+ close(STDOUT_FILENO);
232
+ close(STDERR_FILENO);
233
+ }
234
+
235
+ // put the opened files
236
+ // to our standard file descriptors
237
+ if(input_fd != -1) {
238
+ if(input_fd != STDIN_FILENO) {
239
+ dup2(input_fd, STDIN_FILENO);
240
+ close(input_fd);
241
+ }
242
+ input_fd = -1;
243
+ }
244
+ else dup2(dev_null, STDIN_FILENO);
245
+
246
+ if(output_fd != -1) {
247
+ if(output_fd != STDOUT_FILENO) {
248
+ dup2(output_fd, STDOUT_FILENO);
249
+ close(output_fd);
250
+ }
251
+ output_fd = -1;
252
+ }
253
+ else dup2(dev_null, STDOUT_FILENO);
254
+
255
+ if(error_fd != -1) {
256
+ if(error_fd != STDERR_FILENO) {
257
+ dup2(error_fd, STDERR_FILENO);
258
+ close(error_fd);
259
+ }
260
+ error_fd = -1;
261
+ }
262
+ else dup2(dev_null, STDERR_FILENO);
263
+
264
+ // close /dev/null
265
+ if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
266
+ close(dev_null);
267
+
268
+ // generate our pid file
269
+ {
270
+ unlink(pidfile);
271
+ int fd = open(pidfile, O_RDWR | O_CREAT, 0666);
272
+ if(fd >= 0) {
273
+ char b[100];
274
+ sprintf(b, "%d\n", getpid());
275
+ i = write(fd, b, strlen(b));
276
+ close(fd);
277
+ }
278
+ }
279
+
280
+ return(0);
281
+}
src/daemon.h
new
+12
@@ -0,0 +1,12 @@
1
+#ifndef NETDATA_DAEMON_H
2
+#define NETDATA_DAEMON_H 1
3
+
4
+extern void sig_handler(int signo);
5
+
6
+extern void prepare_rundir();
7
+
8
+extern int become_user(const char *username);
9
+
10
+extern int become_daemon(int close_all_files, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp);
11
+
12
+#endif /* NETDATA_DAEMON_H */
src/global_statistics.c
new
+15
@@ -0,0 +1,15 @@
1
+#include <pthread.h>
2
+
3
+#include "global_statistics.h"
4
+
5
+struct global_statistics global_statistics = { 0ULL, 0ULL, 0ULL, 0ULL };
6
+
7
+pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
8
+
9
+void global_statistics_lock(void) {
10
+ pthread_mutex_lock(&global_statistics_mutex);
11
+}
12
+
13
+void global_statistics_unlock(void) {
14
+ pthread_mutex_unlock(&global_statistics_mutex);
15
+}
src/global_statistics.h
new
+20
@@ -0,0 +1,20 @@
1
+#ifndef NETDATA_GLOBAL_STATISTICS_H
2
+#define NETDATA_GLOBAL_STATISTICS_H 1
3
+
4
+// ----------------------------------------------------------------------------
5
+// global statistics
6
+
7
+struct global_statistics {
8
+ unsigned long long connected_clients;
9
+ unsigned long long web_requests;
10
+ unsigned long long bytes_received;
11
+ unsigned long long bytes_sent;
12
+
13
+};
14
+
15
+extern struct global_statistics global_statistics;
16
+
17
+extern void global_statistics_lock(void);
18
+extern void global_statistics_unlock(void);
19
+
20
+#endif /* NETDATA_GLOBAL_STATISTICS_H */
src/log.c
new
+152
@@ -0,0 +1,152 @@
1
+#include <time.h>
2
+#include <syslog.h>
3
+#include <errno.h>
4
+#include <string.h>
5
+#include <unistd.h>
6
+#include <stdlib.h>
7
+
8
+#include "log.h"
9
+
10
+// ----------------------------------------------------------------------------
11
+// LOG
12
+
13
+unsigned long long debug_flags = DEBUG;
14
+
15
+int silent = 0;
16
+
17
+int access_fd = -1;
18
+FILE *stdaccess = NULL;
19
+
20
+int access_log_syslog = 1;
21
+int error_log_syslog = 1;
22
+int output_log_syslog = 1; // debug log
23
+
24
+
25
+void log_date(FILE *out)
26
+{
27
+ char outstr[200];
28
+ time_t t;
29
+ struct tm *tmp;
30
+
31
+ t = time(NULL);
32
+ tmp = localtime(&t);
33
+
34
+ if (tmp == NULL) return;
35
+ if (strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0) return;
36
+
37
+ fprintf(out, "%s: ", outstr);
38
+}
39
+
40
+void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
41
+{
42
+ if(file) { ; }
43
+ va_list args;
44
+
45
+ log_date(stdout);
46
+ va_start( args, fmt );
47
+ fprintf(stdout, "DEBUG (%04lu@%-15.15s): ", line, function);
48
+ vfprintf( stdout, fmt, args );
49
+ va_end( args );
50
+ fprintf(stdout, "\n");
51
+
52
+ if(output_log_syslog) {
53
+ va_start( args, fmt );
54
+ vsyslog(LOG_ERR, fmt, args );
55
+ va_end( args );
56
+ }
57
+}
58
+
59
+void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
60
+{
61
+ if(file) { ; }
62
+ va_list args;
63
+
64
+ log_date(stderr);
65
+
66
+ va_start( args, fmt );
67
+ if(debug_flags) fprintf(stderr, "INFO (%04lu@%-15.15s): ", line, function);
68
+ else fprintf(stderr, "INFO: ");
69
+ vfprintf( stderr, fmt, args );
70
+ va_end( args );
71
+
72
+ fprintf(stderr, "\n");
73
+
74
+ if(error_log_syslog) {
75
+ va_start( args, fmt );
76
+ vsyslog(LOG_INFO, fmt, args );
77
+ va_end( args );
78
+ }
79
+}
80
+
81
+void error_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
82
+{
83
+ if(file) { ; }
84
+ va_list args;
85
+
86
+ log_date(stderr);
87
+
88
+ va_start( args, fmt );
89
+ if(debug_flags) fprintf(stderr, "ERROR (%04lu@%-15.15s): ", line, function);
90
+ else fprintf(stderr, "ERROR: ");
91
+ vfprintf( stderr, fmt, args );
92
+ va_end( args );
93
+
94
+ if(errno) {
95
+ fprintf(stderr, " (errno %d, %s)\n", errno, strerror(errno));
96
+ errno = 0;
97
+ }
98
+ else fprintf(stderr, "\n");
99
+
100
+ if(error_log_syslog) {
101
+ va_start( args, fmt );
102
+ vsyslog(LOG_ERR, fmt, args );
103
+ va_end( args );
104
+ }
105
+}
106
+
107
+void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
108
+{
109
+ if(file) { ; }
110
+ va_list args;
111
+
112
+ log_date(stderr);
113
+
114
+ va_start( args, fmt );
115
+ if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-15.15s): ", line, function);
116
+ else fprintf(stderr, "FATAL: ");
117
+ vfprintf( stderr, fmt, args );
118
+ va_end( args );
119
+
120
+ perror(" # ");
121
+ fprintf(stderr, "\n");
122
+
123
+ if(error_log_syslog) {
124
+ va_start( args, fmt );
125
+ vsyslog(LOG_CRIT, fmt, args );
126
+ va_end( args );
127
+ }
128
+
129
+ exit(1);
130
+}
131
+
132
+void log_access( const char *fmt, ... )
133
+{
134
+ va_list args;
135
+
136
+ if(stdaccess) {
137
+ log_date(stdaccess);
138
+
139
+ va_start( args, fmt );
140
+ vfprintf( stdaccess, fmt, args );
141
+ va_end( args );
142
+ fprintf( stdaccess, "\n");
143
+ fflush( stdaccess );
144
+ }
145
+
146
+ if(access_log_syslog) {
147
+ va_start( args, fmt );
148
+ vsyslog(LOG_INFO, fmt, args );
149
+ va_end( args );
150
+ }
151
+}
152
+
src/log.h
new
+50
@@ -0,0 +1,50 @@
1
+#include <stdio.h>
2
+#include <stdarg.h>
3
+
4
+#ifndef NETDATA_LOG_H
5
+#define NETDATA_LOG_H 1
6
+
7
+#define D_WEB_BUFFER 0x00000001
8
+#define D_WEB_CLIENT 0x00000002
9
+#define D_LISTENER 0x00000004
10
+#define D_WEB_DATA 0x00000008
11
+#define D_OPTIONS 0x00000010
12
+#define D_PROCNETDEV_LOOP 0x00000020
13
+#define D_RRD_STATS 0x00000040
14
+#define D_WEB_CLIENT_ACCESS 0x00000080
15
+#define D_TC_LOOP 0x00000100
16
+#define D_DEFLATE 0x00000200
17
+#define D_CONFIG 0x00000400
18
+#define D_PLUGINSD 0x00000800
19
+#define D_CHILDS 0x00001000
20
+#define D_EXIT 0x00002000
21
+#define D_CHECKS 0x00004000
22
+
23
+#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
24
+//#define DEBUG 0xffffffff
25
+//#define DEBUG (0)
26
+
27
+extern unsigned long long debug_flags;
28
+
29
+extern int silent;
30
+
31
+extern int access_fd;
32
+extern FILE *stdaccess;
33
+
34
+extern int access_log_syslog;
35
+extern int error_log_syslog;
36
+extern int output_log_syslog;
37
+
38
+#define debug(type, args...) do { if(!silent && debug_flags & type) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
39
+#define info(args...) info_int(__FILE__, __FUNCTION__, __LINE__, ##args)
40
+#define error(args...) error_int(__FILE__, __FUNCTION__, __LINE__, ##args)
41
+#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
42
+
43
+extern void log_date(FILE *out);
44
+extern void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
45
+extern void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
46
+extern void error_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
47
+extern void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... );
48
+extern void log_access( const char *fmt, ... );
49
+
50
+#endif /* NETDATA_LOG_H */
src/main.c
new
+306
@@ -0,0 +1,306 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <signal.h>
5
+#include <syslog.h>
6
+#include <pthread.h>
7
+#include <string.h>
8
+#include <errno.h>
9
+#include <sys/types.h>
10
+#include <sys/wait.h>
11
+
12
+#include "log.h"
13
+#include "daemon.h"
14
+#include "web_server.h"
15
+#include "popen.h"
16
+#include "config.h"
17
+#include "web_client.h"
18
+#include "rrd.h"
19
+#include "rrd2json.h"
20
+
21
+#include "unit_test.h"
22
+
23
+#include "plugins_d.h"
24
+#include "plugin_idlejitter.h"
25
+#include "plugin_tc.h"
26
+#include "plugin_checks.h"
27
+#include "plugin_proc.h"
28
+
29
+#include "main.h"
30
+
31
+struct netdata_static_thread {
32
+ char *name;
33
+
34
+ char *config_section;
35
+ char *config_name;
36
+
37
+ pthread_t *thread;
38
+ void *(*start_routine) (void *);
39
+};
40
+
41
+struct netdata_static_thread static_threads[] = {
42
+ {"tc", "plugins", "tc", NULL, tc_main},
43
+ {"idlejitter", "plugins", "idlejitter", NULL, cpuidlejitter_main},
44
+ {"proc", "plugins", "proc", NULL, proc_main},
45
+ {"plugins.d", NULL, NULL, NULL, pluginsd_main},
46
+ {"check", "plugins", "checks", NULL, checks_main},
47
+ {"web", NULL, NULL, NULL, socket_listen_main},
48
+ {NULL, NULL, NULL, NULL, NULL}
49
+};
50
+
51
+void kill_childs()
52
+{
53
+ siginfo_t info;
54
+
55
+ struct web_client *w;
56
+ for(w = web_clients; w ; w = w->next) {
57
+ debug(D_EXIT, "Stopping web client %s", w->client_ip);
58
+ pthread_cancel(w->thread);
59
+ pthread_join(w->thread, NULL);
60
+ }
61
+
62
+ int i;
63
+ for (i = 0; static_threads[i].name != NULL ; i++) {
64
+ if(static_threads[i].thread) {
65
+ debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
66
+ pthread_cancel(*static_threads[i].thread);
67
+ pthread_join(*static_threads[i].thread, NULL);
68
+ static_threads[i].thread = NULL;
69
+ }
70
+ }
71
+
72
+ if(tc_child_pid) {
73
+ debug(D_EXIT, "Killing tc-qos-helper procees");
74
+ kill(tc_child_pid, SIGTERM);
75
+ waitid(tc_child_pid, 0, &info, WEXITED);
76
+ }
77
+ tc_child_pid = 0;
78
+
79
+ struct plugind *cd;
80
+ for(cd = pluginsd_root ; cd ; cd = cd->next) {
81
+ debug(D_EXIT, "Stopping %s plugin thread", cd->id);
82
+ pthread_cancel(cd->thread);
83
+ pthread_join(cd->thread, NULL);
84
+
85
+ if(cd->pid && !cd->obsolete) {
86
+ debug(D_EXIT, "killing %s plugin process", cd->id);
87
+ kill(cd->pid, SIGTERM);
88
+ waitid(cd->pid, 0, &info, WEXITED);
89
+ }
90
+ }
91
+
92
+ debug(D_EXIT, "All threads/childs stopped.");
93
+}
94
+
95
+
96
+int main(int argc, char **argv)
97
+{
98
+ int i;
99
+ int config_loaded = 0;
100
+
101
+ // parse the arguments
102
+ for(i = 1; i < argc ; i++) {
103
+ if(strcmp(argv[i], "-c") == 0 && (i+1) < argc) {
104
+ if(load_config(argv[i+1], 1) != 1) {
105
+ fprintf(stderr, "Cannot load configuration file %s. Reason: %s\n", argv[i+1], strerror(errno));
106
+ exit(1);
107
+ }
108
+ else {
109
+ debug(D_OPTIONS, "Configuration loaded from %s.", argv[i+1]);
110
+ config_loaded = 1;
111
+ }
112
+ i++;
113
+ }
114
+ else if(strcmp(argv[i], "-df") == 0 && (i+1) < argc) { config_set("global", "debug flags", argv[i+1]); i++; }
115
+ else if(strcmp(argv[i], "-p") == 0 && (i+1) < argc) { config_set("global", "port", argv[i+1]); i++; }
116
+ else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) { config_set("global", "run as user", argv[i+1]); i++; }
117
+ else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) { config_set("global", "history", argv[i+1]); i++; }
118
+ else if(strcmp(argv[i], "-t") == 0 && (i+1) < argc) { config_set("global", "update every", argv[i+1]); i++; }
119
+ else if(strcmp(argv[i], "--unittest") == 0) {
120
+ if(unit_test_storage()) exit(1);
121
+ exit(0);
122
+ update_every = 1;
123
+ if(unit_test(1000000, 0)) exit(1);
124
+ if(unit_test(1000000, 500000)) exit(1);
125
+ if(unit_test(1000000, 100000)) exit(1);
126
+ if(unit_test(1000000, 900000)) exit(1);
127
+ if(unit_test(2000000, 0)) exit(1);
128
+ if(unit_test(2000000, 500000)) exit(1);
129
+ if(unit_test(2000000, 100000)) exit(1);
130
+ if(unit_test(2000000, 900000)) exit(1);
131
+ if(unit_test(500000, 500000)) exit(1);
132
+ //if(unit_test(500000, 100000)) exit(1); // FIXME: the expected numbers are wrong
133
+ //if(unit_test(500000, 900000)) exit(1); // FIXME: the expected numbers are wrong
134
+ if(unit_test(500000, 0)) exit(1);
135
+ fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
136
+ exit(0);
137
+ }
138
+ else {
139
+ fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
140
+ fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT] [-dl debug log file] [-df debug flags].\n\n", argv[0]);
141
+ fprintf(stderr, " -c CONFIG FILE the configuration file to load. Default: %s.\n", CONFIG_DIR "/" CONFIG_FILENAME);
142
+ fprintf(stderr, " -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", HISTORY_MAX, HISTORY);
143
+ fprintf(stderr, " -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
144
+ fprintf(stderr, " -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
145
+ fprintf(stderr, " -u USERNAME can be any system username to run as. Default: none.\n");
146
+ fprintf(stderr, " -df FLAGS debug options. Default: 0x%8llx.\n", debug_flags);
147
+ exit(1);
148
+ }
149
+ }
150
+
151
+ if(!config_loaded) load_config(NULL, 0);
152
+
153
+ char *input_log_file = NULL;
154
+ char *output_log_file = NULL;
155
+ char *error_log_file = NULL;
156
+ char *access_log_file = NULL;
157
+ {
158
+ char buffer[1024];
159
+
160
+ // --------------------------------------------------------------------
161
+
162
+ sprintf(buffer, "0x%08llx", 0ULL);
163
+ char *flags = config_get("global", "debug flags", buffer);
164
+ debug_flags = strtoull(flags, NULL, 0);
165
+ debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
166
+
167
+ // --------------------------------------------------------------------
168
+
169
+ output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
170
+ if(strcmp(output_log_file, "syslog") == 0) {
171
+ output_log_syslog = 1;
172
+ output_log_file = NULL;
173
+ }
174
+ else if(strcmp(output_log_file, "none") == 0) {
175
+ output_log_syslog = 0;
176
+ output_log_file = NULL;
177
+ }
178
+ else output_log_syslog = 0;
179
+
180
+ // --------------------------------------------------------------------
181
+
182
+ silent = 0;
183
+ error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
184
+ if(strcmp(error_log_file, "syslog") == 0) {
185
+ error_log_syslog = 1;
186
+ error_log_file = NULL;
187
+ }
188
+ else if(strcmp(error_log_file, "none") == 0) {
189
+ error_log_syslog = 0;
190
+ error_log_file = NULL;
191
+ silent = 1; // optimization - do not even generate debug log entries
192
+ }
193
+ else error_log_syslog = 0;
194
+
195
+ // --------------------------------------------------------------------
196
+
197
+ access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
198
+ if(strcmp(access_log_file, "syslog") == 0) {
199
+ access_log_syslog = 1;
200
+ access_log_file = NULL;
201
+ }
202
+ else if(strcmp(access_log_file, "none") == 0) {
203
+ access_log_syslog = 0;
204
+ access_log_file = NULL;
205
+ }
206
+ else access_log_syslog = 0;
207
+
208
+ // --------------------------------------------------------------------
209
+
210
+ memory_mode = memory_mode_id(config_get("global", "memory mode", memory_mode_name(memory_mode)));
211
+
212
+ // --------------------------------------------------------------------
213
+
214
+ if(gethostname(buffer, HOSTNAME_MAX) == -1)
215
+ error("WARNING: Cannot get machine hostname.");
216
+ hostname = config_get("global", "hostname", buffer);
217
+ debug(D_OPTIONS, "hostname set to '%s'", hostname);
218
+
219
+ // --------------------------------------------------------------------
220
+
221
+ save_history = config_get_number("global", "history", HISTORY);
222
+ if(save_history < 5 || save_history > HISTORY_MAX) {
223
+ fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", save_history, HISTORY);
224
+ save_history = HISTORY;
225
+ }
226
+ else {
227
+ debug(D_OPTIONS, "save lines set to %d.", save_history);
228
+ }
229
+
230
+ // --------------------------------------------------------------------
231
+
232
+ prepare_rundir();
233
+ char *user = config_get("global", "run as user", (getuid() == 0)?"nobody":"");
234
+ if(*user) {
235
+ if(become_user(user) != 0) {
236
+ fprintf(stderr, "Cannot become user %s.\n", user);
237
+ exit(1);
238
+ }
239
+ else debug(D_OPTIONS, "Successfully became user %s.", user);
240
+ }
241
+
242
+ // --------------------------------------------------------------------
243
+
244
+ update_every = config_get_number("global", "update every", UPDATE_EVERY);
245
+ if(update_every < 1 || update_every > 600) {
246
+ fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY_MAX);
247
+ update_every = UPDATE_EVERY;
248
+ }
249
+ else debug(D_OPTIONS, "update timer set to %d.", update_every);
250
+
251
+ // --------------------------------------------------------------------
252
+
253
+ listen_port = config_get_number("global", "port", LISTEN_PORT);
254
+ if(listen_port < 1 || listen_port > 65535) {
255
+ fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT);
256
+ listen_port = LISTEN_PORT;
257
+ }
258
+ else debug(D_OPTIONS, "listen port set to %d.", listen_port);
259
+
260
+ listen_fd = create_listen_socket(listen_port);
261
+ }
262
+
263
+ // never become a problem
264
+ if(nice(20) == -1) {
265
+ fprintf(stderr, "Cannot lower my CPU priority. Error: %s.\n", strerror(errno));
266
+ }
267
+
268
+ if(become_daemon(0, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1) {
269
+ fprintf(stderr, "Cannot demonize myself (%s).", strerror(errno));
270
+ exit(1);
271
+ }
272
+
273
+
274
+ if(output_log_syslog || error_log_syslog || access_log_syslog)
275
+ openlog("netdata", LOG_PID, LOG_DAEMON);
276
+
277
+ info("NetData started on pid %d", getpid());
278
+
279
+
280
+ // catch all signals
281
+ for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV && i != SIGFPE) signal(i, sig_handler);
282
+
283
+ for (i = 0; static_threads[i].name != NULL ; i++) {
284
+ struct netdata_static_thread *st = &static_threads[i];
285
+ int doit = 1;
286
+
287
+ if(st->config_name) doit = config_get_boolean(st->config_section, st->config_name, doit);
288
+
289
+ if(doit) {
290
+ st->thread = malloc(sizeof(pthread_t));
291
+
292
+ if(pthread_create(st->thread, NULL, st->start_routine, NULL))
293
+ error("failed to create new thread for %s.", st->name);
294
+
295
+ else if(pthread_detach(*st->thread))
296
+ error("Cannot request detach of newly created %s thread.", st->name);
297
+ }
298
+ }
299
+
300
+ // the main process - the web server listener
301
+ // this never ends
302
+ // socket_listen_main(NULL);
303
+ while(1) process_childs(1);
304
+
305
+ exit(0);
306
+}
src/main.h
new
+6
@@ -0,0 +1,6 @@
1
+#ifndef NETDATA_MAIN_H
2
+#define NETDATA_MAIN_H 1
3
+
4
+extern void kill_childs(void);
5
+
6
+#endif /* NETDATA_MAIN_H */
src/plugin_checks.c
new
+91
@@ -0,0 +1,91 @@
1
+#include <pthread.h>
2
+#include <sys/time.h>
3
+#include <sys/resource.h>
4
+#include <strings.h>
5
+#include <unistd.h>
6
+
7
+#include "common.h"
8
+#include "config.h"
9
+#include "log.h"
10
+#include "rrd.h"
11
+#include "plugin_checks.h"
12
+
13
+void *checks_main(void *ptr)
14
+{
15
+ if(ptr) { ; }
16
+
17
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
18
+ error("Cannot set pthread cancel type to DEFERRED.");
19
+
20
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
21
+ error("Cannot set pthread cancel state to ENABLE.");
22
+
23
+ unsigned long long usec = 0, susec = update_every * 1000000ULL, loop_usec = 0, total_susec = 0;
24
+ struct timeval now, last, loop;
25
+
26
+ RRD_STATS *check1, *check2, *check3, *apps_cpu = NULL;
27
+
28
+ check1 = rrd_stats_create("netdata", "check1", NULL, "netdata", "Caller gives microseconds", "a million !", 99999, update_every, CHART_TYPE_LINE);
29
+ rrd_stats_dimension_add(check1, "absolute", NULL, -1, 1, RRD_DIMENSION_ABSOLUTE);
30
+ rrd_stats_dimension_add(check1, "incremental", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
31
+
32
+ check2 = rrd_stats_create("netdata", "check2", NULL, "netdata", "Netdata calcs microseconds", "a million !", 99999, update_every, CHART_TYPE_LINE);
33
+ rrd_stats_dimension_add(check2, "absolute", NULL, -1, 1, RRD_DIMENSION_ABSOLUTE);
34
+ rrd_stats_dimension_add(check2, "incremental", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
35
+
36
+ check3 = rrd_stats_create("netdata", "checkdt", NULL, "netdata", "Clock difference", "microseconds diff", 99999, update_every, CHART_TYPE_LINE);
37
+ rrd_stats_dimension_add(check3, "caller", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
38
+ rrd_stats_dimension_add(check3, "netdata", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
39
+ rrd_stats_dimension_add(check3, "apps.plugin", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
40
+
41
+ gettimeofday(&last, NULL);
42
+ while(1) {
43
+ usleep(susec);
44
+
45
+ // find the time to sleep in order to wait exactly update_every seconds
46
+ gettimeofday(&now, NULL);
47
+ loop_usec = usecdiff(&now, &last);
48
+ usec = loop_usec - susec;
49
+ debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
50
+
51
+ if(usec < (update_every * 1000000ULL / 2ULL)) susec = (update_every * 1000000ULL) - usec;
52
+ else susec = update_every * 1000000ULL / 2ULL;
53
+
54
+ // --------------------------------------------------------------------
55
+ // Calculate loop time
56
+
57
+ last.tv_sec = now.tv_sec;
58
+ last.tv_usec = now.tv_usec;
59
+ total_susec += loop_usec;
60
+
61
+ // --------------------------------------------------------------------
62
+ // check chart 1
63
+
64
+ if(check1->counter_done) rrd_stats_next_usec(check1, loop_usec);
65
+ rrd_stats_dimension_set(check1, "absolute", 1000000);
66
+ rrd_stats_dimension_set(check1, "incremental", total_susec);
67
+ rrd_stats_done(check1);
68
+
69
+ // --------------------------------------------------------------------
70
+ // check chart 2
71
+
72
+ if(check2->counter_done) rrd_stats_next(check2);
73
+ rrd_stats_dimension_set(check2, "absolute", 1000000);
74
+ rrd_stats_dimension_set(check2, "incremental", total_susec);
75
+ rrd_stats_done(check2);
76
+
77
+ // --------------------------------------------------------------------
78
+ // check chart 3
79
+
80
+ if(!apps_cpu) apps_cpu = rrd_stats_find("apps.cpu");
81
+ if(check3->counter_done) rrd_stats_next_usec(check3, loop_usec);
82
+ gettimeofday(&loop, NULL);
83
+ rrd_stats_dimension_set(check3, "caller", (long long)usecdiff(&loop, &check1->last_collected_time));
84
+ rrd_stats_dimension_set(check3, "netdata", (long long)usecdiff(&loop, &check2->last_collected_time));
85
+ if(apps_cpu) rrd_stats_dimension_set(check3, "apps.plugin", (long long)usecdiff(&loop, &apps_cpu->last_collected_time));
86
+ rrd_stats_done(check3);
87
+ }
88
+
89
+ return NULL;
90
+}
91
+
src/plugin_checks.h
new
+6
@@ -0,0 +1,6 @@
1
+#ifndef NETDATA_PLUGIN_CHECKS_H
2
+#define NETDATA_PLUGIN_CHECKS_H 1
3
+
4
+void *checks_main(void *ptr);
5
+
6
+#endif /* NETDATA_PLUGIN_PROC_H */
src/plugin_idlejitter.c
new
+62
@@ -0,0 +1,62 @@
1
+#include <pthread.h>
2
+#include <sys/time.h>
3
+#include <sys/resource.h>
4
+#include <strings.h>
5
+#include <unistd.h>
6
+
7
+#include "global_statistics.h"
8
+#include "common.h"
9
+#include "config.h"
10
+#include "log.h"
11
+#include "rrd.h"
12
+#include "plugin_idlejitter.h"
13
+
14
+#define CPU_IDLEJITTER_SLEEP_TIME_MS 20
15
+
16
+void *cpuidlejitter_main(void *ptr)
17
+{
18
+ if(ptr) { ; }
19
+
20
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
21
+ error("Cannot set pthread cancel type to DEFERRED.");
22
+
23
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
24
+ error("Cannot set pthread cancel state to ENABLE.");
25
+
26
+ int sleep_ms = config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
27
+ if(sleep_ms <= 0) {
28
+ config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
29
+ sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
30
+ }
31
+
32
+ RRD_STATS *st = rrd_stats_find("system.idlejitter");
33
+ if(!st) {
34
+ st = rrd_stats_create("system", "idlejitter", NULL, "cpu", "CPU Idle Jitter", "microseconds lost/s", 9999, update_every, CHART_TYPE_LINE);
35
+ rrd_stats_dimension_add(st, "jitter", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
36
+ }
37
+
38
+ struct timeval before, after;
39
+ unsigned long long counter;
40
+ for(counter = 0; 1 ;counter++) {
41
+ unsigned long long usec = 0, susec = 0;
42
+
43
+ while(susec < (update_every * 1000000ULL)) {
44
+
45
+ gettimeofday(&before, NULL);
46
+ usleep(sleep_ms * 1000);
47
+ gettimeofday(&after, NULL);
48
+
49
+ // calculate the time it took for a full loop
50
+ usec = usecdiff(&after, &before);
51
+ susec += usec;
52
+ }
53
+ usec -= (sleep_ms * 1000);
54
+
55
+ if(counter) rrd_stats_next_usec(st, susec);
56
+ rrd_stats_dimension_set(st, "jitter", usec);
57
+ rrd_stats_done(st);
58
+ }
59
+
60
+ return NULL;
61
+}
62
+
src/plugin_idlejitter.h
new
+6
@@ -0,0 +1,6 @@
1
+#ifndef NETDATA_PLUGIN_IDLEJITTER_H
2
+#define NETDATA_PLUGIN_IDLEJITTER_H 1
3
+
4
+extern void *cpuidlejitter_main(void *ptr);
5
+
6
+#endif /* NETDATA_PLUGIN_IDLEJITTER_H */
src/plugin_proc.c
new
+145
@@ -0,0 +1,145 @@
1
+#include <pthread.h>
2
+#include <sys/time.h>
3
+#include <sys/resource.h>
4
+#include <strings.h>
5
+#include <unistd.h>
6
+
7
+#include "global_statistics.h"
8
+#include "common.h"
9
+#include "config.h"
10
+#include "log.h"
11
+#include "rrd.h"
12
+#include "plugin_proc.h"
13
+
14
+void *proc_main(void *ptr)
15
+{
16
+ if(ptr) { ; }
17
+
18
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
19
+ error("Cannot set pthread cancel type to DEFERRED.");
20
+
21
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
22
+ error("Cannot set pthread cancel state to ENABLE.");
23
+
24
+ struct rusage me, me_last;
25
+ struct timeval last, now;
26
+
27
+ gettimeofday(&last, NULL);
28
+ last.tv_sec -= update_every;
29
+
30
+ // disable (by default) various interface that are not needed
31
+ config_get_boolean("plugin:proc:/proc/net/dev", "interface lo", 0);
32
+ config_get_boolean("plugin:proc:/proc/net/dev", "interface fireqos_monitor", 0);
33
+
34
+ // when ZERO, attempt to do it
35
+ int vdo_proc_net_dev = !config_get_boolean("plugin:proc", "/proc/net/dev", 1);
36
+ int vdo_proc_diskstats = !config_get_boolean("plugin:proc", "/proc/diskstats", 1);
37
+ int vdo_proc_net_snmp = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1);
38
+ int vdo_proc_net_netstat = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1);
39
+ int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1);
40
+ int vdo_proc_net_ip_vs_stats = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1);
41
+ int vdo_proc_stat = !config_get_boolean("plugin:proc", "/proc/stat", 1);
42
+ int vdo_proc_meminfo = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
43
+ int vdo_proc_vmstat = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
44
+ int vdo_cpu_netdata = !config_get_boolean("plugin:proc", "netdata server resources", 1);
45
+
46
+ RRD_STATS *stcpu = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL;
47
+
48
+ gettimeofday(&last, NULL);
49
+ getrusage(RUSAGE_SELF, &me_last);
50
+
51
+ unsigned long long usec = 0, susec = 0;
52
+ for(;1;) {
53
+
54
+ // BEGIN -- the job to be done
55
+ if(!vdo_proc_net_dev) vdo_proc_net_dev = do_proc_net_dev(update_every, usec+susec);
56
+ if(!vdo_proc_diskstats) vdo_proc_diskstats = do_proc_diskstats(update_every, usec+susec);
57
+ if(!vdo_proc_net_snmp) vdo_proc_net_snmp = do_proc_net_snmp(update_every, usec+susec);
58
+ if(!vdo_proc_net_netstat) vdo_proc_net_netstat = do_proc_net_netstat(update_every, usec+susec);
59
+ if(!vdo_proc_net_stat_conntrack) vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(update_every, usec+susec);
60
+ if(!vdo_proc_net_ip_vs_stats) vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(update_every, usec+susec);
61
+ if(!vdo_proc_stat) vdo_proc_stat = do_proc_stat(update_every, usec+susec);
62
+ if(!vdo_proc_meminfo) vdo_proc_meminfo = do_proc_meminfo(update_every, usec+susec);
63
+ if(!vdo_proc_vmstat) vdo_proc_vmstat = do_proc_vmstat(update_every, usec+susec);
64
+ // END -- the job is done
65
+
66
+ // find the time to sleep in order to wait exactly update_every seconds
67
+ gettimeofday(&now, NULL);
68
+ usec = usecdiff(&now, &last) - susec;
69
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
70
+
71
+ if(usec < (update_every * 1000000ULL / 2ULL)) susec = (update_every * 1000000ULL) - usec;
72
+ else susec = update_every * 1000000ULL / 2ULL;
73
+
74
+ // --------------------------------------------------------------------
75
+
76
+ if(!vdo_cpu_netdata && getrusage(RUSAGE_SELF, &me) == 0) {
77
+
78
+ unsigned long long cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
79
+ unsigned long long cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
80
+
81
+ if(!stcpu) stcpu = rrd_stats_find("netdata.server_cpu");
82
+ if(!stcpu) {
83
+ stcpu = rrd_stats_create("netdata", "server_cpu", NULL, "netdata", "NetData CPU usage", "milliseconds/s", 9999, update_every, CHART_TYPE_STACKED);
84
+
85
+ rrd_stats_dimension_add(stcpu, "user", NULL, 1, 1000 * update_every, RRD_DIMENSION_INCREMENTAL);
86
+ rrd_stats_dimension_add(stcpu, "system", NULL, 1, 1000 * update_every, RRD_DIMENSION_INCREMENTAL);
87
+ }
88
+ else rrd_stats_next(stcpu);
89
+
90
+ rrd_stats_dimension_set(stcpu, "user", cpuuser);
91
+ rrd_stats_dimension_set(stcpu, "system", cpusyst);
92
+ rrd_stats_done(stcpu);
93
+
94
+ bcopy(&me, &me_last, sizeof(struct rusage));
95
+
96
+ // ----------------------------------------------------------------
97
+
98
+ if(!stclients) stclients = rrd_stats_find("netdata.clients");
99
+ if(!stclients) {
100
+ stclients = rrd_stats_create("netdata", "clients", NULL, "netdata", "NetData Web Clients", "connected clients", 11000, update_every, CHART_TYPE_LINE);
101
+
102
+ rrd_stats_dimension_add(stclients, "clients", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
103
+ }
104
+ else rrd_stats_next(stclients);
105
+
106
+ rrd_stats_dimension_set(stclients, "clients", global_statistics.connected_clients);
107
+ rrd_stats_done(stclients);
108
+
109
+ // ----------------------------------------------------------------
110
+
111
+ if(!streqs) streqs = rrd_stats_find("netdata.requests");
112
+ if(!streqs) {
113
+ streqs = rrd_stats_create("netdata", "requests", NULL, "netdata", "NetData Web Requests", "requests/s", 12000, update_every, CHART_TYPE_LINE);
114
+
115
+ rrd_stats_dimension_add(streqs, "requests", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
116
+ }
117
+ else rrd_stats_next(streqs);
118
+
119
+ rrd_stats_dimension_set(streqs, "requests", global_statistics.web_requests);
120
+ rrd_stats_done(streqs);
121
+
122
+ // ----------------------------------------------------------------
123
+
124
+ if(!stbytes) stbytes = rrd_stats_find("netdata.net");
125
+ if(!stbytes) {
126
+ stbytes = rrd_stats_create("netdata", "net", NULL, "netdata", "NetData Network Traffic", "kilobits/s", 13000, update_every, CHART_TYPE_AREA);
127
+
128
+ rrd_stats_dimension_add(stbytes, "in", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
129
+ rrd_stats_dimension_add(stbytes, "out", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
130
+ }
131
+ else rrd_stats_next(stbytes);
132
+
133
+ rrd_stats_dimension_set(stbytes, "in", global_statistics.bytes_received);
134
+ rrd_stats_dimension_set(stbytes, "out", global_statistics.bytes_sent);
135
+ rrd_stats_done(stbytes);
136
+ }
137
+
138
+ usleep(susec);
139
+
140
+ // copy current to last
141
+ bcopy(&now, &last, sizeof(struct timeval));
142
+ }
143
+
144
+ return NULL;
145
+}
src/plugin_proc.h
new
+16
@@ -0,0 +1,16 @@
1
+#ifndef NETDATA_PLUGIN_PROC_H
2
+#define NETDATA_PLUGIN_PROC_H 1
3
+
4
+void *proc_main(void *ptr);
5
+
6
+extern int do_proc_net_dev(int update_every, unsigned long long dt);
7
+extern int do_proc_diskstats(int update_every, unsigned long long dt);
8
+extern int do_proc_net_snmp(int update_every, unsigned long long dt);
9
+extern int do_proc_net_netstat(int update_every, unsigned long long dt);
10
+extern int do_proc_net_stat_conntrack(int update_every, unsigned long long dt);
11
+extern int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt);
12
+extern int do_proc_stat(int update_every, unsigned long long dt);
13
+extern int do_proc_meminfo(int update_every, unsigned long long dt);
14
+extern int do_proc_vmstat(int update_every, unsigned long long dt);
15
+
16
+#endif /* NETDATA_PLUGIN_PROC_H */
src/plugin_tc.c
new
+339
@@ -0,0 +1,339 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "rrd.h"
9
+#include "popen.h"
10
+#include "plugin_tc.h"
11
+
12
+#define RRD_TYPE_TC "tc"
13
+#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
14
+
15
+// ----------------------------------------------------------------------------
16
+// /sbin/tc processor
17
+// this requires the script plugins.d/tc-qos-helper.sh
18
+
19
+#define TC_LINE_MAX 1024
20
+
21
+struct tc_class {
22
+ char id[RRD_STATS_NAME_MAX + 1];
23
+ char name[RRD_STATS_NAME_MAX + 1];
24
+
25
+ char leafid[RRD_STATS_NAME_MAX + 1];
26
+ char parentid[RRD_STATS_NAME_MAX + 1];
27
+
28
+ int hasparent;
29
+ int isleaf;
30
+ unsigned long long bytes;
31
+
32
+ struct tc_class *next;
33
+};
34
+
35
+struct tc_device {
36
+ char id[RRD_STATS_NAME_MAX + 1];
37
+ char name[RRD_STATS_NAME_MAX + 1];
38
+ char family[RRD_STATS_NAME_MAX + 1];
39
+
40
+ struct tc_class *classes;
41
+};
42
+
43
+void tc_device_commit(struct tc_device *d)
44
+{
45
+ static int enable_new_interfaces = -1;
46
+
47
+ if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1);
48
+
49
+ // we only need to add leaf classes
50
+ struct tc_class *c, *x;
51
+
52
+ for ( c = d->classes ; c ; c = c->next)
53
+ c->isleaf = 1;
54
+
55
+ for ( c = d->classes ; c ; c = c->next) {
56
+ for ( x = d->classes ; x ; x = x->next) {
57
+ if(x->parentid[0] && (strcmp(c->id, x->parentid) == 0 || strcmp(c->leafid, x->parentid) == 0)) {
58
+ // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has leaf the class '%s' (parentid: '%s').", d->name, c->name, c->leafid, x->name, x->parentid);
59
+ c->isleaf = 0;
60
+ x->hasparent = 1;
61
+ }
62
+ }
63
+ }
64
+
65
+ // debugging:
66
+ /*
67
+ for ( c = d->classes ; c ; c = c->next) {
68
+ if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
69
+ else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
70
+ }
71
+ */
72
+
73
+ for ( c = d->classes ; c ; c = c->next) {
74
+ if(c->isleaf && c->hasparent) break;
75
+ }
76
+ if(!c) {
77
+ debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name);
78
+ return;
79
+ }
80
+
81
+ char var_name[4096 + 1];
82
+ snprintf(var_name, 4096, "qos for %s", d->id);
83
+ if(config_get_boolean("plugin:tc", var_name, enable_new_interfaces)) {
84
+ RRD_STATS *st = rrd_stats_find_bytype(RRD_TYPE_TC, d->id);
85
+ if(!st) {
86
+ debug(D_TC_LOOP, "TC: Committing new TC device '%s'", d->name);
87
+
88
+ st = rrd_stats_create(RRD_TYPE_TC, d->id, d->name, d->family, "Class Usage", "kilobits/s", 1000, update_every, CHART_TYPE_STACKED);
89
+
90
+ for ( c = d->classes ; c ; c = c->next) {
91
+ if(c->isleaf && c->hasparent)
92
+ rrd_stats_dimension_add(st, c->id, c->name, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
93
+ }
94
+ }
95
+ else {
96
+ rrd_stats_next_plugins(st);
97
+
98
+ if(strcmp(d->id, d->name) != 0) rrd_stats_set_name(st, d->name);
99
+ }
100
+
101
+ for ( c = d->classes ; c ; c = c->next) {
102
+ if(c->isleaf && c->hasparent) {
103
+ if(rrd_stats_dimension_set(st, c->id, c->bytes) != 0) {
104
+
105
+ // new class, we have to add it
106
+ rrd_stats_dimension_add(st, c->id, c->name, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
107
+ rrd_stats_dimension_set(st, c->id, c->bytes);
108
+ }
109
+
110
+ // if it has a name, different to the id
111
+ if(strcmp(c->id, c->name) != 0) {
112
+ // update the rrd dimension with the new name
113
+ RRD_DIMENSION *rd;
114
+ for(rd = st->dimensions ; rd ; rd = rd->next) {
115
+ if(strcmp(rd->id, c->id) == 0) { rrd_stats_dimension_set_name(st, rd, c->name); break; }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ rrd_stats_done(st);
121
+ }
122
+}
123
+
124
+void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
125
+{
126
+ struct tc_class *c;
127
+ for ( c = d->classes ; c ; c = c->next) {
128
+ if(strcmp(c->id, id) == 0) {
129
+ strncpy(c->name, name, RRD_STATS_NAME_MAX);
130
+ // no need for null termination - it is already null
131
+ break;
132
+ }
133
+ }
134
+}
135
+
136
+void tc_device_set_device_name(struct tc_device *d, char *name)
137
+{
138
+ strncpy(d->name, name, RRD_STATS_NAME_MAX);
139
+ // no need for null termination - it is already null
140
+}
141
+
142
+void tc_device_set_device_family(struct tc_device *d, char *name)
143
+{
144
+ strncpy(d->family, name, RRD_STATS_NAME_MAX);
145
+ // no need for null termination - it is already null
146
+}
147
+
148
+struct tc_device *tc_device_create(char *name)
149
+{
150
+ struct tc_device *d;
151
+
152
+ d = calloc(1, sizeof(struct tc_device));
153
+ if(!d) {
154
+ fatal("Cannot allocate memory for tc_device %s", name);
155
+ return NULL;
156
+ }
157
+
158
+ strncpy(d->id, name, RRD_STATS_NAME_MAX);
159
+ strcpy(d->name, d->id);
160
+ strcpy(d->family, d->id);
161
+
162
+ // no need for null termination on the strings, because of calloc()
163
+
164
+ return(d);
165
+}
166
+
167
+struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid)
168
+{
169
+ struct tc_class *c;
170
+
171
+ c = calloc(1, sizeof(struct tc_class));
172
+ if(!c) {
173
+ fatal("Cannot allocate memory for tc class");
174
+ return NULL;
175
+ }
176
+
177
+ c->next = n->classes;
178
+ n->classes = c;
179
+
180
+ strncpy(c->id, id, RRD_STATS_NAME_MAX);
181
+ strcpy(c->name, c->id);
182
+ if(parentid) strncpy(c->parentid, parentid, RRD_STATS_NAME_MAX);
183
+ if(leafid) strncpy(c->leafid, leafid, RRD_STATS_NAME_MAX);
184
+
185
+ // no need for null termination on the strings, because of calloc()
186
+
187
+ return(c);
188
+}
189
+
190
+void tc_class_free(struct tc_class *c)
191
+{
192
+ if(c->next) tc_class_free(c->next);
193
+ free(c);
194
+}
195
+
196
+void tc_device_free(struct tc_device *n)
197
+{
198
+ if(n->classes) tc_class_free(n->classes);
199
+ free(n);
200
+}
201
+
202
+pid_t tc_child_pid = 0;
203
+void *tc_main(void *ptr)
204
+{
205
+ if(ptr) { ; }
206
+
207
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
208
+ error("Cannot set pthread cancel type to DEFERRED.");
209
+
210
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
211
+ error("Cannot set pthread cancel state to ENABLE.");
212
+
213
+ char buffer[TC_LINE_MAX+1] = "";
214
+
215
+ for(;1;) {
216
+ FILE *fp;
217
+ struct tc_device *device = NULL;
218
+ struct tc_class *class = NULL;
219
+
220
+ snprintf(buffer, TC_LINE_MAX, "exec %s %d", config_get("plugin:tc", "script to run to get tc values", PLUGINS_DIR "/tc-qos-helper.sh"), update_every);
221
+ debug(D_TC_LOOP, "executing '%s'", buffer);
222
+ // fp = popen(buffer, "r");
223
+ fp = mypopen(buffer, &tc_child_pid);
224
+ if(!fp) {
225
+ error("TC: Cannot popen(\"%s\", \"r\").", buffer);
226
+ return NULL;
227
+ }
228
+
229
+ while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
230
+ buffer[TC_LINE_MAX] = '\0';
231
+ char *b = buffer, *p;
232
+ // debug(D_TC_LOOP, "TC: read '%s'", buffer);
233
+
234
+ p = strsep(&b, " \n");
235
+ while (p && (*p == ' ' || *p == '\0')) p = strsep(&b, " \n");
236
+ if(!p) continue;
237
+
238
+ if(strcmp(p, "END") == 0) {
239
+ if(device) {
240
+ if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
241
+ error("Cannot set pthread cancel state to DISABLE.");
242
+
243
+ tc_device_commit(device);
244
+ tc_device_free(device);
245
+ device = NULL;
246
+ class = NULL;
247
+
248
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
249
+ error("Cannot set pthread cancel state to ENABLE.");
250
+ }
251
+ }
252
+ else if(strcmp(p, "BEGIN") == 0) {
253
+ if(device) {
254
+ tc_device_free(device);
255
+ device = NULL;
256
+ class = NULL;
257
+ }
258
+
259
+ p = strsep(&b, " \n");
260
+ if(p && *p) {
261
+ device = tc_device_create(p);
262
+ class = NULL;
263
+ }
264
+ }
265
+ else if(device && (strcmp(p, "class") == 0)) {
266
+ p = strsep(&b, " \n"); // the class: htb, fq_codel, etc
267
+ char *id = strsep(&b, " \n"); // the class major:minor
268
+ char *parent = strsep(&b, " \n"); // 'parent' or 'root'
269
+ char *parentid = strsep(&b, " \n"); // the parent's id
270
+ char *leaf = strsep(&b, " \n"); // 'leaf'
271
+ char *leafid = strsep(&b, " \n"); // leafid
272
+
273
+ if(id && *id
274
+ && parent && *parent
275
+ && parentid && *parentid
276
+ && (
277
+ (strcmp(parent, "parent") == 0 && parentid && *parentid)
278
+ || strcmp(parent, "root") == 0
279
+ )) {
280
+
281
+ if(strcmp(parent, "root") == 0) {
282
+ parentid = NULL;
283
+ leafid = NULL;
284
+ }
285
+ else if(!leaf || strcmp(leaf, "leaf") != 0)
286
+ leafid = NULL;
287
+
288
+ char leafbuf[20 + 1] = "";
289
+ if(leafid && leafid[strlen(leafid) - 1] == ':') {
290
+ strncpy(leafbuf, leafid, 20 - 1);
291
+ strcat(leafbuf, "1");
292
+ leafid = leafbuf;
293
+ }
294
+
295
+ class = tc_class_add(device, id, parentid, leafid);
296
+ }
297
+ }
298
+ else if(device && class && (strcmp(p, "Sent") == 0)) {
299
+ p = strsep(&b, " \n");
300
+ if(p && *p) class->bytes = atoll(p);
301
+ }
302
+ else if(device && (strcmp(p, "SETDEVICENAME") == 0)) {
303
+ char *name = strsep(&b, " \n");
304
+ if(name && *name) tc_device_set_device_name(device, name);
305
+ }
306
+ else if(device && (strcmp(p, "SETDEVICEGROUP") == 0)) {
307
+ char *name = strsep(&b, " \n");
308
+ if(name && *name) tc_device_set_device_family(device, name);
309
+ }
310
+ else if(device && (strcmp(p, "SETCLASSNAME") == 0)) {
311
+ char *id = strsep(&b, " \n");
312
+ char *path = strsep(&b, " \n");
313
+ if(id && *id && path && *path) tc_device_set_class_name(device, id, path);
314
+ }
315
+#ifdef DETACH_PLUGINS_FROM_NETDATA
316
+ else if((strcmp(p, "MYPID") == 0)) {
317
+ char *id = strsep(&b, " \n");
318
+ pid_t pid = atol(id);
319
+
320
+ if(pid) tc_child_pid = pid;
321
+
322
+ debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
323
+ }
324
+#endif
325
+ }
326
+ mypclose(fp);
327
+
328
+ if(device) {
329
+ tc_device_free(device);
330
+ device = NULL;
331
+ class = NULL;
332
+ }
333
+
334
+ sleep(update_every);
335
+ }
336
+
337
+ return NULL;
338
+}
339
+
src/plugin_tc.h
new
+8
@@ -0,0 +1,8 @@
1
+#ifndef NETDATA_PLUGIN_TC_H
2
+#define NETDATA_PLUGIN_TC_H 1
3
+
4
+extern pid_t tc_child_pid;
5
+extern void *tc_main(void *ptr);
6
+
7
+#endif /* NETDATA_PLUGIN_TC_H */
8
+
src/plugins_d.c
new
+384
@@ -0,0 +1,384 @@
1
+#include <sys/types.h>
2
+#include <dirent.h>
3
+#include <pthread.h>
4
+#include <string.h>
5
+#include <stdio.h>
6
+#include <stdlib.h>
7
+#include <signal.h>
8
+
9
+#include "common.h"
10
+#include "config.h"
11
+#include "log.h"
12
+#include "rrd.h"
13
+#include "popen.h"
14
+#include "plugins_d.h"
15
+
16
+struct plugind *pluginsd_root = NULL;
17
+
18
+void *pluginsd_worker_thread(void *arg)
19
+{
20
+ struct plugind *cd = (struct plugind *)arg;
21
+ char line[PLUGINSD_LINE_MAX + 1];
22
+
23
+#ifdef DETACH_PLUGINS_FROM_NETDATA
24
+ unsigned long long usec = 0, susec = 0;
25
+ struct timeval last = {0, 0} , now = {0, 0};
26
+#endif
27
+
28
+ while(1) {
29
+ FILE *fp = mypopen(cd->cmd, &cd->pid);
30
+ if(!fp) {
31
+ error("Cannot popen(\"%s\", \"r\").", cd->cmd);
32
+ break;
33
+ }
34
+
35
+ RRD_STATS *st = NULL;
36
+
37
+ unsigned long long count = 0;
38
+ while(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL) {
39
+ char *p = trim(line);
40
+ debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
41
+
42
+ char *s = qstrsep(&p);
43
+
44
+ if(!s || !*s) continue;
45
+ else if(!strcmp(s, "SET")) {
46
+ char *t;
47
+ while((t = strchr(p, '='))) *t = ' ';
48
+
49
+ char *dimension = qstrsep(&p);
50
+ char *value = qstrsep(&p);
51
+
52
+ if(!dimension || !*dimension || !value) {
53
+ error("PLUGINSD: '%s' is requesting a SET on chart '%s', like this: 'SET %s = %s'. Disabling it.", cd->fullfilename, st->id, dimension?dimension:"", value?value:"");
54
+ cd->enabled = 0;
55
+ kill(cd->pid, SIGTERM);
56
+ break;
57
+ }
58
+
59
+ if(!st) {
60
+ error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value);
61
+ cd->enabled = 0;
62
+ kill(cd->pid, SIGTERM);
63
+ break;
64
+ }
65
+
66
+ if(st->debug) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value);
67
+ rrd_stats_dimension_set(st, dimension, atoll(value));
68
+
69
+ count++;
70
+ }
71
+ else if(!strcmp(s, "BEGIN")) {
72
+ char *id = qstrsep(&p);
73
+ char *microseconds_txt = qstrsep(&p);
74
+
75
+ if(!id) {
76
+ error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
77
+ cd->enabled = 0;
78
+ kill(cd->pid, SIGTERM);
79
+ break;
80
+ }
81
+
82
+ st = rrd_stats_find(id);
83
+ if(!st) {
84
+ error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
85
+ cd->enabled = 0;
86
+ kill(cd->pid, SIGTERM);
87
+ break;
88
+ }
89
+
90
+ if(st->counter_done) {
91
+ unsigned long long microseconds = 0;
92
+ if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10);
93
+ if(microseconds) rrd_stats_next_usec(st, microseconds);
94
+ else rrd_stats_next_plugins(st);
95
+ }
96
+ }
97
+ else if(!strcmp(s, "END")) {
98
+ if(!st) {
99
+ error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
100
+ cd->enabled = 0;
101
+ kill(cd->pid, SIGTERM);
102
+ break;
103
+ }
104
+
105
+ if(st->debug) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a END on chart %s", cd->fullfilename, st->id);
106
+
107
+ rrd_stats_done(st);
108
+ st = NULL;
109
+ }
110
+ else if(!strcmp(s, "FLUSH")) {
111
+ debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
112
+ st = NULL;
113
+ }
114
+ else if(!strcmp(s, "CHART")) {
115
+ st = NULL;
116
+
117
+ char *type = qstrsep(&p);
118
+ char *id = NULL;
119
+ if(type) {
120
+ id = strchr(type, '.');
121
+ if(id) { *id = '\0'; id++; }
122
+ }
123
+ char *name = qstrsep(&p);
124
+ char *title = qstrsep(&p);
125
+ char *units = qstrsep(&p);
126
+ char *family = qstrsep(&p);
127
+ char *category = qstrsep(&p);
128
+ char *chart = qstrsep(&p);
129
+ char *priority_s = qstrsep(&p);
130
+ char *update_every_s = qstrsep(&p);
131
+
132
+ if(!type || !*type || !id || !*id) {
133
+ error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
134
+ cd->enabled = 0;
135
+ kill(cd->pid, SIGTERM);
136
+ break;
137
+ }
138
+
139
+ int priority = 1000;
140
+ if(priority_s) priority = atoi(priority_s);
141
+
142
+ int update_every = cd->update_every;
143
+ if(update_every_s) update_every = atoi(update_every_s);
144
+ if(!update_every) update_every = cd->update_every;
145
+
146
+ int chart_type = CHART_TYPE_LINE;
147
+ if(chart) chart_type = chart_type_id(chart);
148
+
149
+ if(!name || !*name) name = NULL;
150
+ if(!family || !*family) family = id;
151
+ if(!category || !*category) category = type;
152
+
153
+ st = rrd_stats_find_bytype(type, id);
154
+ if(!st) {
155
+ debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', category='%s', chart='%s', priority=%d, update_every=%d"
156
+ , type, id
157
+ , name?name:""
158
+ , family?family:""
159
+ , category?category:""
160
+ , chart_type_name(chart_type)
161
+ , priority
162
+ , update_every
163
+ );
164
+
165
+ st = rrd_stats_create(type, id, name, family, title, units, priority, update_every, chart_type);
166
+ cd->update_every = update_every;
167
+
168
+ if(strcmp(category, "none") == 0) st->isdetail = 1;
169
+ }
170
+ else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
171
+ }
172
+ else if(!strcmp(s, "DIMENSION")) {
173
+ char *id = qstrsep(&p);
174
+ char *name = qstrsep(&p);
175
+ char *algorithm = qstrsep(&p);
176
+ char *multiplier_s = qstrsep(&p);
177
+ char *divisor_s = qstrsep(&p);
178
+ char *hidden = qstrsep(&p);
179
+
180
+ if(!id || !*id) {
181
+ error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
182
+ cd->enabled = 0;
183
+ kill(cd->pid, SIGTERM);
184
+ break;
185
+ }
186
+
187
+ if(!st) {
188
+ error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
189
+ cd->enabled = 0;
190
+ kill(cd->pid, SIGTERM);
191
+ break;
192
+ }
193
+
194
+ long multiplier = 1;
195
+ if(multiplier_s && *multiplier_s) multiplier = atol(multiplier_s);
196
+ if(!multiplier) multiplier = 1;
197
+
198
+ long divisor = 1;
199
+ if(divisor_s && *divisor_s) divisor = atol(divisor_s);
200
+ if(!divisor) divisor = 1;
201
+
202
+ if(!algorithm || !*algorithm) algorithm = "absolute";
203
+
204
+ if(st->debug) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
205
+ , st->id
206
+ , id
207
+ , name?name:""
208
+ , algorithm_name(algorithm_id(algorithm))
209
+ , multiplier
210
+ , divisor
211
+ , hidden?hidden:""
212
+ );
213
+
214
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
215
+ if(!rd) {
216
+ rd = rrd_stats_dimension_add(st, id, name, multiplier, divisor, algorithm_id(algorithm));
217
+ if(hidden && strcmp(hidden, "hidden") == 0) rd->hidden = 1;
218
+ }
219
+ else if(st->debug) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
220
+ }
221
+ else if(!strcmp(s, "DISABLE")) {
222
+ error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
223
+ cd->enabled = 0;
224
+ kill(cd->pid, SIGTERM);
225
+ break;
226
+ }
227
+#ifdef DETACH_PLUGINS_FROM_NETDATA
228
+ else if(!strcmp(s, "MYPID")) {
229
+ char *pid_s = qstrsep(&p);
230
+ pid_t pid = atol(pid_s);
231
+
232
+ if(pid) cd->pid = pid;
233
+ debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
234
+ }
235
+ else if(!strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE")) {
236
+ error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
237
+
238
+ gettimeofday(&now, NULL);
239
+ if(!usec && !susec) {
240
+ // our first run
241
+ susec = cd->update_every * 1000000ULL;
242
+ }
243
+ else {
244
+ // second+ run
245
+ usec = usecdiff(&now, &last) - susec;
246
+ error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
247
+ if(usec < (update_every * 1000000ULL / 2ULL)) susec = (update_every * 1000000ULL) - usec;
248
+ else susec = update_every * 1000000ULL / 2ULL;
249
+ }
250
+
251
+ error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
252
+ usleep(susec);
253
+ kill(cd->pid, SIGCONT);
254
+ bcopy(&now, &last, sizeof(struct timeval));
255
+ break;
256
+ }
257
+#endif
258
+ else {
259
+ error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
260
+ cd->enabled = 0;
261
+ kill(cd->pid, SIGTERM);
262
+ break;
263
+ }
264
+ }
265
+
266
+ // fgets() failed or loop broke
267
+ mypclose(fp);
268
+
269
+ if(!count && cd->enabled) {
270
+ error("PLUGINSD: '%s' does not generate usefull output. Disabling it.", cd->fullfilename);
271
+ cd->enabled = 0;
272
+ kill(cd->pid, SIGTERM);
273
+ }
274
+
275
+ if(cd->enabled) sleep(cd->update_every);
276
+ else break;
277
+ }
278
+
279
+ cd->obsolete = 1;
280
+ return NULL;
281
+}
282
+
283
+void *pluginsd_main(void *ptr)
284
+{
285
+ if(ptr) { ; }
286
+
287
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
288
+ error("Cannot set pthread cancel type to DEFERRED.");
289
+
290
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
291
+ error("Cannot set pthread cancel state to ENABLE.");
292
+
293
+ char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
294
+ int automatic_run = config_get_boolean("plugins", "enable running new plugins", 0);
295
+ int scan_frequency = config_get_number("plugins", "check for new plugins every", 60);
296
+ DIR *dir = NULL;
297
+ struct dirent *file = NULL;
298
+ struct plugind *cd;
299
+
300
+ // enable the apps plugin by default
301
+ config_get_boolean("plugins", "apps", 1);
302
+
303
+ if(scan_frequency < 1) scan_frequency = 1;
304
+
305
+ while(1) {
306
+ dir = opendir(dir_name);
307
+ if(!dir) {
308
+ error("Cannot open directory '%s'.", dir_name);
309
+ return NULL;
310
+ }
311
+
312
+ while((file = readdir(dir))) {
313
+ debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
314
+
315
+ if(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0) continue;
316
+
317
+ int len = strlen(file->d_name);
318
+ if(len <= (int)PLUGINSD_FILE_SUFFIX_LEN) continue;
319
+ if(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0) {
320
+ debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
321
+ continue;
322
+ }
323
+
324
+ char pluginname[CONFIG_MAX_NAME + 1];
325
+ snprintf(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
326
+ int enabled = config_get_boolean("plugins", pluginname, automatic_run);
327
+
328
+ if(!enabled) {
329
+ debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
330
+ continue;
331
+ }
332
+
333
+ // check if it runs already
334
+ for(cd = pluginsd_root ; cd ; cd = cd->next) {
335
+ if(strcmp(cd->filename, file->d_name) == 0) break;
336
+ }
337
+ if(cd && !cd->obsolete) {
338
+ debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
339
+ continue;
340
+ }
341
+
342
+ // it is not running
343
+ // allocate a new one, or use the obsolete one
344
+ if(!cd) {
345
+ cd = calloc(sizeof(struct plugind), 1);
346
+ if(!cd) fatal("Cannot allocate memory for plugin.");
347
+
348
+ snprintf(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
349
+
350
+ strncpy(cd->filename, file->d_name, FILENAME_MAX);
351
+ snprintf(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
352
+
353
+ cd->enabled = enabled;
354
+ cd->update_every = config_get_number(cd->id, "update every", update_every);
355
+ cd->started_t = time(NULL);
356
+
357
+ char *def = "";
358
+ snprintf(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
359
+
360
+ // link it
361
+ if(pluginsd_root) cd->next = pluginsd_root;
362
+ pluginsd_root = cd;
363
+ }
364
+ cd->obsolete = 0;
365
+
366
+ if(!cd->enabled) continue;
367
+
368
+ // spawn a new thread for it
369
+ if(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0) {
370
+ error("CHARTS.D: failed to create new thread for chart.d %s.", cd->filename);
371
+ cd->obsolete = 1;
372
+ }
373
+ else if(pthread_detach(cd->thread) != 0)
374
+ error("CHARTS.D: Cannot request detach of newly created thread for chart.d %s.", cd->filename);
375
+ }
376
+
377
+ closedir(dir);
378
+ sleep(scan_frequency);
379
+ }
380
+
381
+ return NULL;
382
+}
383
+
384
+
src/plugins_d.h
new
+36
@@ -0,0 +1,36 @@
1
+#include <sys/types.h>
2
+#include <unistd.h>
3
+
4
+
5
+#ifndef NETDATA_PLUGINS_D_H
6
+#define NETDATA_PLUGINS_D_H 1
7
+
8
+#define PLUGINSD_FILE_SUFFIX ".plugin"
9
+#define PLUGINSD_FILE_SUFFIX_LEN strlen(PLUGINSD_FILE_SUFFIX)
10
+#define PLUGINSD_CMD_MAX (FILENAME_MAX*2)
11
+#define PLUGINSD_LINE_MAX 1024
12
+
13
+struct plugind {
14
+ char id[CONFIG_MAX_NAME+1]; // config node id
15
+
16
+ char filename[FILENAME_MAX+1]; // just the filename
17
+ char fullfilename[FILENAME_MAX+1]; // with path
18
+ char cmd[PLUGINSD_CMD_MAX+1]; // the command that is executes
19
+
20
+ pid_t pid;
21
+ pthread_t thread;
22
+
23
+ int update_every;
24
+ int obsolete;
25
+ int enabled;
26
+
27
+ time_t started_t;
28
+
29
+ struct plugind *next;
30
+};
31
+
32
+extern struct plugind *pluginsd_root;
33
+
34
+extern void *pluginsd_main(void *ptr);
35
+
36
+#endif /* NETDATA_PLUGINS_D_H */
src/popen.c
new
+130
@@ -0,0 +1,130 @@
1
+#include <signal.h>
2
+#include <unistd.h>
3
+#include <stdlib.h>
4
+#include <sys/types.h>
5
+#include <sys/wait.h>
6
+
7
+#include "log.h"
8
+#include "popen.h"
9
+
10
+#define PIPE_READ 0
11
+#define PIPE_WRITE 1
12
+
13
+FILE *mypopen(const char *command, pid_t *pidptr)
14
+{
15
+ int pipefd[2];
16
+
17
+ if(pipe(pipefd) == -1) return NULL;
18
+
19
+ int pid = fork();
20
+ if(pid == -1) {
21
+ close(pipefd[PIPE_READ]);
22
+ close(pipefd[PIPE_WRITE]);
23
+ return NULL;
24
+ }
25
+ if(pid != 0) {
26
+ // the parent
27
+ *pidptr = pid;
28
+ close(pipefd[PIPE_WRITE]);
29
+ FILE *fp = fdopen(pipefd[PIPE_READ], "r");
30
+ return(fp);
31
+ }
32
+ // the child
33
+
34
+ // close all files
35
+ int i;
36
+ for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
37
+ if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
38
+
39
+ // move the pipe to stdout
40
+ if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
41
+ dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
42
+ close(pipefd[PIPE_WRITE]);
43
+ }
44
+
45
+#ifdef DETACH_PLUGINS_FROM_NETDATA
46
+ // this was an attempt to detach the child and use the suspend mode charts.d
47
+ // unfortunatelly it does not work as expected.
48
+
49
+ // fork again to become session leader
50
+ pid = fork();
51
+ if(pid == -1) fprintf(stderr, "Cannot fork again on pid %d\n", getpid());
52
+ if(pid != 0) {
53
+ // the parent
54
+ exit(0);
55
+ }
56
+
57
+ // set a new process group id for just this child
58
+ if( setpgid(0, 0) != 0 )
59
+ fprintf(stderr, "Cannot set a new process group for pid %d (%s)\n", getpid(), strerror(errno));
60
+
61
+ if( getpgid(0) != getpid() )
62
+ fprintf(stderr, "Process group set is incorrect. Expected %d, found %d\n", getpid(), getpgid(0));
63
+
64
+ if( setsid() != 0 )
65
+ fprintf(stderr, "Cannot set session id for pid %d (%s)\n", getpid(), strerror(errno));
66
+
67
+ fprintf(stdout, "MYPID %d\n", getpid());
68
+ fflush(NULL);
69
+#endif
70
+
71
+ // ignore all signals
72
+ for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
73
+
74
+ fprintf(stderr, "executing command: '%s' on pid %d.\n", command, getpid());
75
+ execl("/bin/sh", "sh", "-c", command, NULL);
76
+ exit(1);
77
+}
78
+
79
+void mypclose(FILE *fp)
80
+{
81
+ // this is a very poor implementation of pclose()
82
+ // the caller should catch SIGCHLD and waitpid() on the exited child
83
+ // otherwise the child will be a zombie forever
84
+
85
+ fclose(fp);
86
+}
87
+
88
+void process_childs(int wait)
89
+{
90
+ siginfo_t info;
91
+ int options = WEXITED;
92
+ if(!wait) options |= WNOHANG;
93
+
94
+ info.si_pid = 0;
95
+ while(waitid(P_ALL, 0, &info, options) == 0) {
96
+ if(!info.si_pid) break;
97
+ switch(info.si_code) {
98
+ case CLD_EXITED:
99
+ error("pid %d exited with code %d.", info.si_pid, info.si_status);
100
+ break;
101
+
102
+ case CLD_KILLED:
103
+ error("pid %d killed by signal %d.", info.si_pid, info.si_status);
104
+ break;
105
+
106
+ case CLD_DUMPED:
107
+ error("pid %d core dumped by signal %d.", info.si_pid, info.si_status);
108
+ break;
109
+
110
+ case CLD_STOPPED:
111
+ error("pid %d stopped by signal %d.", info.si_pid, info.si_status);
112
+ break;
113
+
114
+ case CLD_TRAPPED:
115
+ error("pid %d trapped by signal %d.", info.si_pid, info.si_status);
116
+ break;
117
+
118
+ case CLD_CONTINUED:
119
+ error("pid %d continued by signal %d.", info.si_pid, info.si_status);
120
+ break;
121
+
122
+ default:
123
+ error("pid %d gave us a SIGCHLD with code %d and status %d.", info.si_pid, info.si_code, info.si_status);
124
+ break;
125
+ }
126
+
127
+ // prevent an infinite loop
128
+ info.si_pid = 0;
129
+ }
130
+}
src/popen.h
new
+15
@@ -0,0 +1,15 @@
1
+#include <stdio.h>
2
+#include <unistd.h>
3
+#include <sys/types.h>
4
+
5
+#ifndef NETDATA_POPEN_H
6
+#define NETDATA_POPEN_H 1
7
+
8
+#define PIPE_READ 0
9
+#define PIPE_WRITE 1
10
+
11
+extern FILE *mypopen(const char *command, pid_t *pidptr);
12
+extern void mypclose(FILE *fp);
13
+extern void process_childs(int wait);
14
+
15
+#endif /* NETDATA_POPEN_H */
src/proc_diskstats.c
new
+313
@@ -0,0 +1,313 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+#define RRD_TYPE_DISK "disk"
13
+#define RRD_TYPE_DISK_LEN strlen(RRD_TYPE_DISK)
14
+
15
+#define MAX_PROC_DISKSTATS_LINE 4096
16
+
17
+int do_proc_diskstats(int update_every, unsigned long long dt) {
18
+ static procfile *ff = NULL;
19
+
20
+ static int enable_new_disks = -1;
21
+ static int do_io = -1, do_ops = -1, do_merged_ops = -1, do_iotime = -1, do_cur_ops = -1;
22
+
23
+ if(enable_new_disks == -1) enable_new_disks = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", 1);
24
+
25
+ if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/diskstats", "bandwidth for all disks", 1);
26
+ if(do_ops == -1) do_ops = config_get_boolean("plugin:proc:/proc/diskstats", "operations for all disks", 1);
27
+ if(do_merged_ops == -1) do_merged_ops = config_get_boolean("plugin:proc:/proc/diskstats", "merged operations for all disks", 1);
28
+ if(do_iotime == -1) do_iotime = config_get_boolean("plugin:proc:/proc/diskstats", "i/o time for all disks", 1);
29
+ if(do_cur_ops == -1) do_cur_ops = config_get_boolean("plugin:proc:/proc/diskstats", "current operations for all disks", 1);
30
+
31
+ if(dt) {};
32
+
33
+ if(!ff) ff = procfile_open("/proc/diskstats", " \t");
34
+ if(!ff) return 1;
35
+
36
+ ff = procfile_readall(ff);
37
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
38
+
39
+ uint32_t lines = procfile_lines(ff), l;
40
+ uint32_t words;
41
+
42
+ for(l = 0; l < lines ;l++) {
43
+ char *disk;
44
+ unsigned long long major = 0, minor = 0,
45
+ reads = 0, reads_merged = 0, readsectors = 0, readms = 0,
46
+ writes = 0, writes_merged = 0, writesectors = 0, writems = 0,
47
+ currentios = 0, iosms = 0, wiosms = 0;
48
+
49
+ words = procfile_linewords(ff, l);
50
+ if(words < 14) continue;
51
+
52
+ major = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
53
+ minor = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
54
+ disk = procfile_lineword(ff, l, 2);
55
+ reads = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
56
+ reads_merged = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
57
+ readsectors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
58
+ readms = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
59
+ writes = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
60
+ writes_merged = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
61
+ writesectors = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
62
+ writems = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
63
+ currentios = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
64
+ iosms = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
65
+ wiosms = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
66
+
67
+ int def_enabled = 0;
68
+
69
+ switch(major) {
70
+ case 9: // MDs
71
+ case 43: // network block
72
+ case 144: // nfs
73
+ case 145: // nfs
74
+ case 146: // nfs
75
+ case 199: // veritas
76
+ case 201: // veritas
77
+ case 251: // dm
78
+ def_enabled = enable_new_disks;
79
+ break;
80
+
81
+ case 48: // RAID
82
+ case 49: // RAID
83
+ case 50: // RAID
84
+ case 51: // RAID
85
+ case 52: // RAID
86
+ case 53: // RAID
87
+ case 54: // RAID
88
+ case 55: // RAID
89
+ case 112: // RAID
90
+ case 136: // RAID
91
+ case 137: // RAID
92
+ case 138: // RAID
93
+ case 139: // RAID
94
+ case 140: // RAID
95
+ case 141: // RAID
96
+ case 142: // RAID
97
+ case 143: // RAID
98
+ case 179: // MMC
99
+ case 180: // USB
100
+ if(minor % 8) def_enabled = 0; // partitions
101
+ else def_enabled = enable_new_disks;
102
+ break;
103
+
104
+ case 8: // scsi disks
105
+ case 65: // scsi disks
106
+ case 66: // scsi disks
107
+ case 67: // scsi disks
108
+ case 68: // scsi disks
109
+ case 69: // scsi disks
110
+ case 70: // scsi disks
111
+ case 71: // scsi disks
112
+ case 72: // scsi disks
113
+ case 73: // scsi disks
114
+ case 74: // scsi disks
115
+ case 75: // scsi disks
116
+ case 76: // scsi disks
117
+ case 77: // scsi disks
118
+ case 78: // scsi disks
119
+ case 79: // scsi disks
120
+ case 80: // i2o
121
+ case 81: // i2o
122
+ case 82: // i2o
123
+ case 83: // i2o
124
+ case 84: // i2o
125
+ case 85: // i2o
126
+ case 86: // i2o
127
+ case 87: // i2o
128
+ case 101: // hyperdisk
129
+ case 102: // compressed
130
+ case 104: // scsi
131
+ case 105: // scsi
132
+ case 106: // scsi
133
+ case 107: // scsi
134
+ case 108: // scsi
135
+ case 109: // scsi
136
+ case 110: // scsi
137
+ case 111: // scsi
138
+ case 114: // bios raid
139
+ case 116: // ram board
140
+ case 128: // scsi
141
+ case 129: // scsi
142
+ case 130: // scsi
143
+ case 131: // scsi
144
+ case 132: // scsi
145
+ case 133: // scsi
146
+ case 134: // scsi
147
+ case 135: // scsi
148
+ case 153: // raid
149
+ case 202: // xen
150
+ case 256: // flash
151
+ case 257: // flash
152
+ if(minor % 16) def_enabled = 0; // partitions
153
+ else def_enabled = enable_new_disks;
154
+ break;
155
+
156
+ case 160: // raid
157
+ case 161: // raid
158
+ if(minor % 32) def_enabled = 0; // partitions
159
+ else def_enabled = enable_new_disks;
160
+ break;
161
+
162
+ case 3: // ide
163
+ case 13: // 8bit ide
164
+ case 22: // ide
165
+ case 33: // ide
166
+ case 34: // ide
167
+ case 56: // ide
168
+ case 57: // ide
169
+ case 88: // ide
170
+ case 89: // ide
171
+ case 90: // ide
172
+ case 91: // ide
173
+ if(minor % 64) def_enabled = 0; // partitions
174
+ else def_enabled = enable_new_disks;
175
+ break;
176
+
177
+ default:
178
+ def_enabled = 0;
179
+ break;
180
+ }
181
+
182
+ // check if it is enabled
183
+ {
184
+ char var_name[4096 + 1];
185
+ snprintf(var_name, 4096, "disk %s", disk);
186
+ if(!config_get_boolean("plugin:proc:/proc/diskstats", var_name, def_enabled)) continue;
187
+ }
188
+
189
+ RRD_STATS *st;
190
+
191
+ // --------------------------------------------------------------------
192
+
193
+ if(do_io) {
194
+ st = rrd_stats_find_bytype(RRD_TYPE_DISK, disk);
195
+ if(!st) {
196
+ char tf[FILENAME_MAX + 1], *t;
197
+ char ssfilename[FILENAME_MAX + 1];
198
+ int sector_size = 512;
199
+
200
+ strncpy(tf, disk, FILENAME_MAX);
201
+ tf[FILENAME_MAX] = '\0';
202
+
203
+ // replace all / with !
204
+ while((t = strchr(tf, '/'))) *t = '!';
205
+
206
+ snprintf(ssfilename, FILENAME_MAX, "/sys/block/%s/queue/hw_sector_size", tf);
207
+ FILE *fpss = fopen(ssfilename, "r");
208
+ if(fpss) {
209
+ char ssbuffer[1025];
210
+ char *tmp = fgets(ssbuffer, 1024, fpss);
211
+
212
+ if(tmp) {
213
+ sector_size = atoi(tmp);
214
+ if(sector_size <= 0) {
215
+ error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, disk, ssfilename);
216
+ sector_size = 512;
217
+ }
218
+ }
219
+ else error("Cannot read data for sector size for device %s from %s. Assuming 512.", disk, ssfilename);
220
+
221
+ fclose(fpss);
222
+ }
223
+ else error("Cannot read sector size for device %s from %s. Assuming 512.", disk, ssfilename);
224
+
225
+ st = rrd_stats_create(RRD_TYPE_DISK, disk, NULL, disk, "Disk I/O", "kilobytes/s", 2000, update_every, CHART_TYPE_AREA);
226
+
227
+ rrd_stats_dimension_add(st, "reads", NULL, sector_size, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
228
+ rrd_stats_dimension_add(st, "writes", NULL, sector_size * -1, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
229
+ }
230
+ else rrd_stats_next(st);
231
+
232
+ rrd_stats_dimension_set(st, "reads", readsectors);
233
+ rrd_stats_dimension_set(st, "writes", writesectors);
234
+ rrd_stats_done(st);
235
+ }
236
+
237
+ // --------------------------------------------------------------------
238
+
239
+ if(do_ops) {
240
+ st = rrd_stats_find_bytype("disk_ops", disk);
241
+ if(!st) {
242
+ st = rrd_stats_create("disk_ops", disk, NULL, disk, "Disk Operations", "operations/s", 2001, update_every, CHART_TYPE_LINE);
243
+ st->isdetail = 1;
244
+
245
+ rrd_stats_dimension_add(st, "reads", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
246
+ rrd_stats_dimension_add(st, "writes", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
247
+ }
248
+ else rrd_stats_next(st);
249
+
250
+ rrd_stats_dimension_set(st, "reads", reads);
251
+ rrd_stats_dimension_set(st, "writes", writes);
252
+ rrd_stats_done(st);
253
+ }
254
+
255
+ // --------------------------------------------------------------------
256
+
257
+ if(do_merged_ops) {
258
+ st = rrd_stats_find_bytype("disk_merged_ops", disk);
259
+ if(!st) {
260
+ st = rrd_stats_create("disk_merged_ops", disk, NULL, disk, "Merged Disk Operations", "operations/s", 2010, update_every, CHART_TYPE_LINE);
261
+ st->isdetail = 1;
262
+
263
+ rrd_stats_dimension_add(st, "reads", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
264
+ rrd_stats_dimension_add(st, "writes", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
265
+ }
266
+ else rrd_stats_next(st);
267
+
268
+ rrd_stats_dimension_set(st, "reads", reads_merged);
269
+ rrd_stats_dimension_set(st, "writes", writes_merged);
270
+ rrd_stats_done(st);
271
+ }
272
+
273
+ // --------------------------------------------------------------------
274
+
275
+ if(do_iotime) {
276
+ st = rrd_stats_find_bytype("disk_iotime", disk);
277
+ if(!st) {
278
+ st = rrd_stats_create("disk_iotime", disk, NULL, disk, "Disk I/O Time", "milliseconds/s", 2005, update_every, CHART_TYPE_LINE);
279
+ st->isdetail = 1;
280
+
281
+ rrd_stats_dimension_add(st, "reads", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
282
+ rrd_stats_dimension_add(st, "writes", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
283
+ rrd_stats_dimension_add(st, "latency", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
284
+ rrd_stats_dimension_add(st, "weighted", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
285
+ }
286
+ else rrd_stats_next(st);
287
+
288
+ rrd_stats_dimension_set(st, "reads", readms);
289
+ rrd_stats_dimension_set(st, "writes", writems);
290
+ rrd_stats_dimension_set(st, "latency", iosms);
291
+ rrd_stats_dimension_set(st, "weighted", wiosms);
292
+ rrd_stats_done(st);
293
+ }
294
+
295
+ // --------------------------------------------------------------------
296
+
297
+ if(do_cur_ops) {
298
+ st = rrd_stats_find_bytype("disk_cur_ops", disk);
299
+ if(!st) {
300
+ st = rrd_stats_create("disk_cur_ops", disk, NULL, disk, "Current Disk I/O operations", "operations", 2004, update_every, CHART_TYPE_LINE);
301
+ st->isdetail = 1;
302
+
303
+ rrd_stats_dimension_add(st, "operations", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
304
+ }
305
+ else rrd_stats_next(st);
306
+
307
+ rrd_stats_dimension_set(st, "operations", currentios);
308
+ rrd_stats_done(st);
309
+ }
310
+ }
311
+
312
+ return 0;
313
+}
src/proc_meminfo.c
new
+245
@@ -0,0 +1,245 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+#define MAX_PROC_MEMINFO_LINE 4096
13
+#define MAX_PROC_MEMINFO_NAME 1024
14
+
15
+int do_proc_meminfo(int update_every, unsigned long long dt) {
16
+ static procfile *ff = NULL;
17
+
18
+ static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
19
+
20
+ if(do_ram == -1) do_ram = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
21
+ if(do_swap == -1) do_swap = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
22
+ if(do_hwcorrupt == -1) do_hwcorrupt = config_get_boolean("plugin:proc:/proc/meminfo", "hardware corrupted ECC", 1);
23
+ if(do_committed == -1) do_committed = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
24
+ if(do_writeback == -1) do_writeback = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
25
+ if(do_kernel == -1) do_kernel = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
26
+ if(do_slab == -1) do_slab = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
27
+
28
+ if(dt) {};
29
+
30
+ if(!ff) ff = procfile_open("/proc/meminfo", " \t:");
31
+ if(!ff) return 1;
32
+
33
+ ff = procfile_readall(ff);
34
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
35
+
36
+ uint32_t lines = procfile_lines(ff), l;
37
+ uint32_t words;
38
+
39
+ int hwcorrupted = 0;
40
+
41
+ unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0,
42
+ Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0,
43
+ Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0,
44
+ Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0,
45
+ NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0,
46
+ VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0,
47
+ AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0,
48
+ DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0;
49
+
50
+ for(l = 0; l < lines ;l++) {
51
+ words = procfile_linewords(ff, l);
52
+ if(words < 2) continue;
53
+
54
+ char *name = procfile_lineword(ff, l, 0);
55
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
56
+
57
+ if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value;
58
+ else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value;
59
+ else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value;
60
+ else if(!Cached && strcmp(name, "Cached") == 0) Cached = value;
61
+ else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value;
62
+ else if(!Active && strcmp(name, "Active") == 0) Active = value;
63
+ else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value;
64
+ else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value;
65
+ else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value;
66
+ else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value;
67
+ else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value;
68
+ else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value;
69
+ else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value;
70
+ else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value;
71
+ else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value;
72
+ else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value;
73
+ else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value;
74
+ else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value;
75
+ else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value;
76
+ else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value;
77
+ else if(!Slab && strcmp(name, "Slab") == 0) Slab = value;
78
+ else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value;
79
+ else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value;
80
+ else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value;
81
+ else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value;
82
+ else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value;
83
+ else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value;
84
+ else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value;
85
+ else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value;
86
+ else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value;
87
+ else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value;
88
+ else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value;
89
+ else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value;
90
+ else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; }
91
+ else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value;
92
+ else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value;
93
+ else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value;
94
+ else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value;
95
+ else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value;
96
+ else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value;
97
+ else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value;
98
+ else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value;
99
+ }
100
+
101
+ RRD_STATS *st;
102
+
103
+ // --------------------------------------------------------------------
104
+
105
+ // http://stackoverflow.com/questions/3019748/how-to-reliably-measure-available-memory-in-linux
106
+ unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
107
+
108
+ if(do_ram) {
109
+ st = rrd_stats_find("system.ram");
110
+ if(!st) {
111
+ st = rrd_stats_create("system", "ram", NULL, "mem", "System RAM", "MB", 200, update_every, CHART_TYPE_STACKED);
112
+
113
+ rrd_stats_dimension_add(st, "buffers", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
114
+ rrd_stats_dimension_add(st, "used", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
115
+ rrd_stats_dimension_add(st, "cached", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
116
+ rrd_stats_dimension_add(st, "free", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
117
+ }
118
+ else rrd_stats_next(st);
119
+
120
+ rrd_stats_dimension_set(st, "used", MemUsed);
121
+ rrd_stats_dimension_set(st, "free", MemFree);
122
+ rrd_stats_dimension_set(st, "cached", Cached);
123
+ rrd_stats_dimension_set(st, "buffers", Buffers);
124
+ rrd_stats_done(st);
125
+ }
126
+
127
+ // --------------------------------------------------------------------
128
+
129
+ unsigned long long SwapUsed = SwapTotal - SwapFree;
130
+
131
+ if(do_swap) {
132
+ st = rrd_stats_find("system.swap");
133
+ if(!st) {
134
+ st = rrd_stats_create("system", "swap", NULL, "mem", "System Swap", "MB", 201, update_every, CHART_TYPE_STACKED);
135
+ st->isdetail = 1;
136
+
137
+ rrd_stats_dimension_add(st, "free", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
138
+ rrd_stats_dimension_add(st, "used", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
139
+ }
140
+ else rrd_stats_next(st);
141
+
142
+ rrd_stats_dimension_set(st, "used", SwapUsed);
143
+ rrd_stats_dimension_set(st, "free", SwapFree);
144
+ rrd_stats_done(st);
145
+ }
146
+
147
+ // --------------------------------------------------------------------
148
+
149
+ if(hwcorrupted && do_hwcorrupt) {
150
+ st = rrd_stats_find("mem.hwcorrupt");
151
+ if(!st) {
152
+ st = rrd_stats_create("mem", "hwcorrupt", NULL, "mem", "Hardware Corrupted ECC", "MB", 9000, update_every, CHART_TYPE_LINE);
153
+ st->isdetail = 1;
154
+
155
+ rrd_stats_dimension_add(st, "HardwareCorrupted", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
156
+ }
157
+ else rrd_stats_next(st);
158
+
159
+ rrd_stats_dimension_set(st, "HardwareCorrupted", HardwareCorrupted);
160
+ rrd_stats_done(st);
161
+ }
162
+
163
+ // --------------------------------------------------------------------
164
+
165
+ if(do_committed) {
166
+ st = rrd_stats_find("mem.committed");
167
+ if(!st) {
168
+ st = rrd_stats_create("mem", "committed", NULL, "mem", "Committed (Allocated) Memory", "MB", 5000, update_every, CHART_TYPE_AREA);
169
+ st->isdetail = 1;
170
+
171
+ rrd_stats_dimension_add(st, "Committed_AS", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
172
+ }
173
+ else rrd_stats_next(st);
174
+
175
+ rrd_stats_dimension_set(st, "Committed_AS", Committed_AS);
176
+ rrd_stats_done(st);
177
+ }
178
+
179
+ // --------------------------------------------------------------------
180
+
181
+ if(do_writeback) {
182
+ st = rrd_stats_find("mem.writeback");
183
+ if(!st) {
184
+ st = rrd_stats_create("mem", "writeback", NULL, "mem", "Writeback Memory", "MB", 4000, update_every, CHART_TYPE_LINE);
185
+ st->isdetail = 1;
186
+
187
+ rrd_stats_dimension_add(st, "Dirty", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
188
+ rrd_stats_dimension_add(st, "Writeback", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
189
+ rrd_stats_dimension_add(st, "FuseWriteback", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
190
+ rrd_stats_dimension_add(st, "NfsWriteback", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
191
+ rrd_stats_dimension_add(st, "Bounce", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
192
+ }
193
+ else rrd_stats_next(st);
194
+
195
+ rrd_stats_dimension_set(st, "Dirty", Dirty);
196
+ rrd_stats_dimension_set(st, "Writeback", Writeback);
197
+ rrd_stats_dimension_set(st, "FuseWriteback", WritebackTmp);
198
+ rrd_stats_dimension_set(st, "NfsWriteback", NFS_Unstable);
199
+ rrd_stats_dimension_set(st, "Bounce", Bounce);
200
+ rrd_stats_done(st);
201
+ }
202
+
203
+ // --------------------------------------------------------------------
204
+
205
+ if(do_kernel) {
206
+ st = rrd_stats_find("mem.kernel");
207
+ if(!st) {
208
+ st = rrd_stats_create("mem", "kernel", NULL, "mem", "Memory Used by Kernel", "MB", 6000, update_every, CHART_TYPE_STACKED);
209
+ st->isdetail = 1;
210
+
211
+ rrd_stats_dimension_add(st, "Slab", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
212
+ rrd_stats_dimension_add(st, "KernelStack", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
213
+ rrd_stats_dimension_add(st, "PageTables", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
214
+ rrd_stats_dimension_add(st, "VmallocUsed", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
215
+ }
216
+ else rrd_stats_next(st);
217
+
218
+ rrd_stats_dimension_set(st, "KernelStack", KernelStack);
219
+ rrd_stats_dimension_set(st, "Slab", Slab);
220
+ rrd_stats_dimension_set(st, "PageTables", PageTables);
221
+ rrd_stats_dimension_set(st, "VmallocUsed", VmallocUsed);
222
+ rrd_stats_done(st);
223
+ }
224
+
225
+ // --------------------------------------------------------------------
226
+
227
+ if(do_slab) {
228
+ st = rrd_stats_find("mem.slab");
229
+ if(!st) {
230
+ st = rrd_stats_create("mem", "slab", NULL, "mem", "Reclaimable Kernel Memory", "MB", 6500, update_every, CHART_TYPE_STACKED);
231
+ st->isdetail = 1;
232
+
233
+ rrd_stats_dimension_add(st, "reclaimable", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
234
+ rrd_stats_dimension_add(st, "unreclaimable", NULL, 1, 1024, RRD_DIMENSION_ABSOLUTE);
235
+ }
236
+ else rrd_stats_next(st);
237
+
238
+ rrd_stats_dimension_set(st, "reclaimable", SReclaimable);
239
+ rrd_stats_dimension_set(st, "unreclaimable", SUnreclaim);
240
+ rrd_stats_done(st);
241
+ }
242
+
243
+ return 0;
244
+}
245
+
src/proc_net_dev.c
new
+203
@@ -0,0 +1,203 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+
5
+#include "config.h"
6
+#include "procfile.h"
7
+#include "rrd.h"
8
+#include "plugin_proc.h"
9
+
10
+int do_proc_net_dev(int update_every, unsigned long long dt) {
11
+ static procfile *ff = NULL;
12
+ static int enable_new_interfaces = -1;
13
+ static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
14
+
15
+ if(dt) {};
16
+
17
+ if(!ff) ff = procfile_open("/proc/net/dev", " \t,:|");
18
+ if(!ff) return 1;
19
+
20
+ ff = procfile_readall(ff);
21
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
22
+
23
+ if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", 1);
24
+
25
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", 1);
26
+ if(do_packets == -1) do_packets = config_get_boolean("plugin:proc:/proc/net/dev", "packets for all interfaces", 1);
27
+ if(do_errors == -1) do_errors = config_get_boolean("plugin:proc:/proc/net/dev", "errors for all interfaces", 1);
28
+ if(do_drops == -1) do_drops = config_get_boolean("plugin:proc:/proc/net/dev", "drops for all interfaces", 1);
29
+ if(do_fifo == -1) do_fifo = config_get_boolean("plugin:proc:/proc/net/dev", "fifo for all interfaces", 1);
30
+ if(do_compressed == -1) do_compressed = config_get_boolean("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", 1);
31
+ if(do_events == -1) do_events = config_get_boolean("plugin:proc:/proc/net/dev", "frames, collisions, carrier coutners for all interfaces", 1);
32
+
33
+ uint32_t lines = procfile_lines(ff), l;
34
+ uint32_t words;
35
+
36
+ char *iface;
37
+ unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
38
+ unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
39
+
40
+ for(l = 2; l < lines ;l++) {
41
+ words = procfile_linewords(ff, l);
42
+ if(words < 17) continue;
43
+
44
+ iface = procfile_lineword(ff, l, 0);
45
+
46
+ rbytes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
47
+ rpackets = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
48
+ rerrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
49
+ rdrops = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
50
+ rfifo = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
51
+ rframe = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
52
+ rcompressed = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
53
+ rmulticast = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
54
+
55
+ tbytes = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
56
+ tpackets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
57
+ terrors = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
58
+ tdrops = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
59
+ tfifo = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
60
+ tcollisions = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
61
+ tcarrier = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
62
+ tcompressed = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
63
+
64
+ {
65
+ char var_name[4096 + 1];
66
+ snprintf(var_name, 4096, "interface %s", iface);
67
+ if(!config_get_boolean("plugin:proc:/proc/net/dev", var_name, enable_new_interfaces)) continue;
68
+ }
69
+
70
+ RRD_STATS *st;
71
+
72
+ // --------------------------------------------------------------------
73
+
74
+ if(do_bandwidth) {
75
+ st = rrd_stats_find_bytype("net", iface);
76
+ if(!st) {
77
+ st = rrd_stats_create("net", iface, NULL, iface, "Bandwidth", "kilobits/s", 1000, update_every, CHART_TYPE_AREA);
78
+
79
+ rrd_stats_dimension_add(st, "received", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
80
+ rrd_stats_dimension_add(st, "sent", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
81
+ }
82
+ else rrd_stats_next(st);
83
+
84
+ rrd_stats_dimension_set(st, "received", rbytes);
85
+ rrd_stats_dimension_set(st, "sent", tbytes);
86
+ rrd_stats_done(st);
87
+ }
88
+
89
+ // --------------------------------------------------------------------
90
+
91
+ if(do_packets) {
92
+ st = rrd_stats_find_bytype("net_packets", iface);
93
+ if(!st) {
94
+ st = rrd_stats_create("net_packets", iface, NULL, iface, "Packets", "packets/s", 1001, update_every, CHART_TYPE_LINE);
95
+ st->isdetail = 1;
96
+
97
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
98
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
99
+ rrd_stats_dimension_add(st, "multicast", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
100
+ }
101
+ else rrd_stats_next(st);
102
+
103
+ rrd_stats_dimension_set(st, "received", rpackets);
104
+ rrd_stats_dimension_set(st, "sent", tpackets);
105
+ rrd_stats_dimension_set(st, "multicast", rmulticast);
106
+ rrd_stats_done(st);
107
+ }
108
+
109
+ // --------------------------------------------------------------------
110
+
111
+ if(do_errors) {
112
+ st = rrd_stats_find_bytype("net_errors", iface);
113
+ if(!st) {
114
+ st = rrd_stats_create("net_errors", iface, NULL, iface, "Interface Errors", "errors/s", 1002, update_every, CHART_TYPE_LINE);
115
+ st->isdetail = 1;
116
+
117
+ rrd_stats_dimension_add(st, "inbound", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
118
+ rrd_stats_dimension_add(st, "outbound", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
119
+ }
120
+ else rrd_stats_next(st);
121
+
122
+ rrd_stats_dimension_set(st, "inbound", rerrors);
123
+ rrd_stats_dimension_set(st, "outbound", terrors);
124
+ rrd_stats_done(st);
125
+ }
126
+
127
+ // --------------------------------------------------------------------
128
+
129
+ if(do_drops) {
130
+ st = rrd_stats_find_bytype("net_drops", iface);
131
+ if(!st) {
132
+ st = rrd_stats_create("net_drops", iface, NULL, iface, "Interface Drops", "drops/s", 1003, update_every, CHART_TYPE_LINE);
133
+ st->isdetail = 1;
134
+
135
+ rrd_stats_dimension_add(st, "inbound", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
136
+ rrd_stats_dimension_add(st, "outbound", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
137
+ }
138
+ else rrd_stats_next(st);
139
+
140
+ rrd_stats_dimension_set(st, "inbound", rdrops);
141
+ rrd_stats_dimension_set(st, "outbound", tdrops);
142
+ rrd_stats_done(st);
143
+ }
144
+
145
+ // --------------------------------------------------------------------
146
+
147
+ if(do_fifo) {
148
+ st = rrd_stats_find_bytype("net_fifo", iface);
149
+ if(!st) {
150
+ st = rrd_stats_create("net_fifo", iface, NULL, iface, "Interface Queue", "packets", 1100, update_every, CHART_TYPE_LINE);
151
+ st->isdetail = 1;
152
+
153
+ rrd_stats_dimension_add(st, "receive", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
154
+ rrd_stats_dimension_add(st, "transmit", NULL, -1, 1, RRD_DIMENSION_ABSOLUTE);
155
+ }
156
+ else rrd_stats_next(st);
157
+
158
+ rrd_stats_dimension_set(st, "receive", rfifo);
159
+ rrd_stats_dimension_set(st, "transmit", tfifo);
160
+ rrd_stats_done(st);
161
+ }
162
+
163
+ // --------------------------------------------------------------------
164
+
165
+ if(do_compressed) {
166
+ st = rrd_stats_find_bytype("net_compressed", iface);
167
+ if(!st) {
168
+ st = rrd_stats_create("net_compressed", iface, NULL, iface, "Compressed Packets", "packets/s", 1200, update_every, CHART_TYPE_LINE);
169
+ st->isdetail = 1;
170
+
171
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
172
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
173
+ }
174
+ else rrd_stats_next(st);
175
+
176
+ rrd_stats_dimension_set(st, "received", rcompressed);
177
+ rrd_stats_dimension_set(st, "sent", tcompressed);
178
+ rrd_stats_done(st);
179
+ }
180
+
181
+ // --------------------------------------------------------------------
182
+
183
+ if(do_events) {
184
+ st = rrd_stats_find_bytype("net_events", iface);
185
+ if(!st) {
186
+ st = rrd_stats_create("net_events", iface, NULL, iface, "Network Interface Events", "events/s", 1200, update_every, CHART_TYPE_LINE);
187
+ st->isdetail = 1;
188
+
189
+ rrd_stats_dimension_add(st, "frames", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
190
+ rrd_stats_dimension_add(st, "collisions", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
191
+ rrd_stats_dimension_add(st, "carrier", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
192
+ }
193
+ else rrd_stats_next(st);
194
+
195
+ rrd_stats_dimension_set(st, "frames", rframe);
196
+ rrd_stats_dimension_set(st, "collisions", tcollisions);
197
+ rrd_stats_dimension_set(st, "carrier", tcarrier);
198
+ rrd_stats_done(st);
199
+ }
200
+ }
201
+
202
+ return 0;
203
+}
src/proc_net_ip_vs_stats.c
new
+95
@@ -0,0 +1,95 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+
5
+#include "config.h"
6
+#include "procfile.h"
7
+#include "rrd.h"
8
+#include "plugin_proc.h"
9
+
10
+#define RRD_TYPE_NET_IPVS "ipvs"
11
+#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
12
+
13
+int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
14
+ static int do_bandwidth = -1, do_sockets = -1, do_packets = -1;
15
+ static procfile *ff = NULL;
16
+
17
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS bandwidth", 1);
18
+ if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS connections", 1);
19
+ if(do_packets == -1) do_packets = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS packets", 1);
20
+
21
+ if(dt) {};
22
+
23
+ if(!ff) ff = procfile_open("/proc/net/ip_vs_stats", " \t,:|");
24
+ if(!ff) return 1;
25
+
26
+ ff = procfile_readall(ff);
27
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
28
+
29
+ // make sure we have 3 lines
30
+ if(procfile_lines(ff) < 3) return 1;
31
+
32
+ // make sure we have 5 words on the 3rd line
33
+ if(procfile_linewords(ff, 2) < 5) return 1;
34
+
35
+ unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
36
+
37
+ entries = strtoull(procfile_lineword(ff, 2, 0), NULL, 10);
38
+ InPackets = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
39
+ OutPackets = strtoull(procfile_lineword(ff, 2, 2), NULL, 10);
40
+ InBytes = strtoull(procfile_lineword(ff, 2, 3), NULL, 10);
41
+ OutBytes = strtoull(procfile_lineword(ff, 2, 4), NULL, 10);
42
+
43
+ RRD_STATS *st;
44
+
45
+ // --------------------------------------------------------------------
46
+
47
+ if(do_sockets) {
48
+ st = rrd_stats_find(RRD_TYPE_NET_IPVS ".sockets");
49
+ if(!st) {
50
+ st = rrd_stats_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, "IPVS New Connections", "connections/s", 1001, update_every, CHART_TYPE_LINE);
51
+
52
+ rrd_stats_dimension_add(st, "connections", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
53
+ }
54
+ else rrd_stats_next(st);
55
+
56
+ rrd_stats_dimension_set(st, "connections", entries);
57
+ rrd_stats_done(st);
58
+ }
59
+
60
+ // --------------------------------------------------------------------
61
+
62
+ if(do_packets) {
63
+ st = rrd_stats_find(RRD_TYPE_NET_IPVS ".packets");
64
+ if(!st) {
65
+ st = rrd_stats_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, "IPVS Packets", "packets/s", 1002, update_every, CHART_TYPE_LINE);
66
+
67
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
68
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
69
+ }
70
+ else rrd_stats_next(st);
71
+
72
+ rrd_stats_dimension_set(st, "received", InPackets);
73
+ rrd_stats_dimension_set(st, "sent", OutPackets);
74
+ rrd_stats_done(st);
75
+ }
76
+
77
+ // --------------------------------------------------------------------
78
+
79
+ if(do_bandwidth) {
80
+ st = rrd_stats_find(RRD_TYPE_NET_IPVS ".net");
81
+ if(!st) {
82
+ st = rrd_stats_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, "IPVS Bandwidth", "kilobits/s", 1000, update_every, CHART_TYPE_AREA);
83
+
84
+ rrd_stats_dimension_add(st, "received", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
85
+ rrd_stats_dimension_add(st, "sent", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
86
+ }
87
+ else rrd_stats_next(st);
88
+
89
+ rrd_stats_dimension_set(st, "received", InBytes);
90
+ rrd_stats_dimension_set(st, "sent", OutBytes);
91
+ rrd_stats_done(st);
92
+ }
93
+
94
+ return 0;
95
+}
src/proc_net_netstat.c
new
+178
@@ -0,0 +1,178 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+int do_proc_net_netstat(int update_every, unsigned long long dt) {
13
+ static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
14
+ static procfile *ff = NULL;
15
+
16
+ if(do_bandwidth == -1) do_bandwidth = config_get_boolean("plugin:proc:/proc/net/netstat", "bandwidth", 1);
17
+ if(do_inerrors == -1) do_inerrors = config_get_boolean("plugin:proc:/proc/net/netstat", "input errors", 1);
18
+ if(do_mcast == -1) do_mcast = config_get_boolean("plugin:proc:/proc/net/netstat", "multicast bandwidth", 1);
19
+ if(do_bcast == -1) do_bcast = config_get_boolean("plugin:proc:/proc/net/netstat", "broadcast bandwidth", 1);
20
+ if(do_mcast_p == -1) do_mcast_p = config_get_boolean("plugin:proc:/proc/net/netstat", "multicast packets", 1);
21
+ if(do_bcast_p == -1) do_bcast_p = config_get_boolean("plugin:proc:/proc/net/netstat", "broadcast packets", 1);
22
+
23
+ if(dt) {};
24
+
25
+ if(!ff) ff = procfile_open("/proc/net/netstat", " \t:");
26
+ if(!ff) return 1;
27
+
28
+ ff = procfile_readall(ff);
29
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
30
+
31
+ uint32_t lines = procfile_lines(ff), l;
32
+ uint32_t words;
33
+
34
+ for(l = 0; l < lines ;l++) {
35
+ if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
36
+ l++; // we need the next line
37
+
38
+ if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
39
+ error("Cannot read IpExt line from /proc/net/netstat.");
40
+ break;
41
+ }
42
+ words = procfile_linewords(ff, l);
43
+ if(words < 12) {
44
+ error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %d.", words);
45
+ continue;
46
+ }
47
+
48
+ unsigned long long
49
+ InNoRoutes = 0, InTruncatedPkts = 0,
50
+ InOctets = 0, InMcastPkts = 0, InBcastPkts = 0, InMcastOctets = 0, InBcastOctets = 0,
51
+ OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
52
+
53
+ InNoRoutes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
54
+ InTruncatedPkts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
55
+ InMcastPkts = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
56
+ OutMcastPkts = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
57
+ InBcastPkts = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
58
+ OutBcastPkts = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
59
+ InOctets = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
60
+ OutOctets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
61
+ InMcastOctets = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
62
+ OutMcastOctets = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
63
+ InBcastOctets = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
64
+ OutBcastOctets = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
65
+
66
+ RRD_STATS *st;
67
+
68
+ // --------------------------------------------------------------------
69
+
70
+ if(do_bandwidth) {
71
+ st = rrd_stats_find("system.ipv4");
72
+ if(!st) {
73
+ st = rrd_stats_create("system", "ipv4", NULL, "ipv4", "IPv4 Bandwidth", "kilobits/s", 2000, update_every, CHART_TYPE_AREA);
74
+
75
+ rrd_stats_dimension_add(st, "received", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
76
+ rrd_stats_dimension_add(st, "sent", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
77
+ }
78
+ else rrd_stats_next(st);
79
+
80
+ rrd_stats_dimension_set(st, "sent", OutOctets);
81
+ rrd_stats_dimension_set(st, "received", InOctets);
82
+ rrd_stats_done(st);
83
+ }
84
+
85
+ // --------------------------------------------------------------------
86
+
87
+ if(do_inerrors) {
88
+ st = rrd_stats_find("ipv4.inerrors");
89
+ if(!st) {
90
+ st = rrd_stats_create("ipv4", "inerrors", NULL, "ipv4", "IPv4 Input Errors", "packets/s", 4000, update_every, CHART_TYPE_LINE);
91
+ st->isdetail = 1;
92
+
93
+ rrd_stats_dimension_add(st, "noroutes", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
94
+ rrd_stats_dimension_add(st, "trunkated", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
95
+ }
96
+ else rrd_stats_next(st);
97
+
98
+ rrd_stats_dimension_set(st, "noroutes", InNoRoutes);
99
+ rrd_stats_dimension_set(st, "trunkated", InTruncatedPkts);
100
+ rrd_stats_done(st);
101
+ }
102
+
103
+ // --------------------------------------------------------------------
104
+
105
+ if(do_mcast) {
106
+ st = rrd_stats_find("ipv4.mcast");
107
+ if(!st) {
108
+ st = rrd_stats_create("ipv4", "mcast", NULL, "ipv4", "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, CHART_TYPE_AREA);
109
+ st->isdetail = 1;
110
+
111
+ rrd_stats_dimension_add(st, "received", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
112
+ rrd_stats_dimension_add(st, "sent", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
113
+ }
114
+ else rrd_stats_next(st);
115
+
116
+ rrd_stats_dimension_set(st, "sent", OutMcastOctets);
117
+ rrd_stats_dimension_set(st, "received", InMcastOctets);
118
+ rrd_stats_done(st);
119
+ }
120
+
121
+ // --------------------------------------------------------------------
122
+
123
+ if(do_bcast) {
124
+ st = rrd_stats_find("ipv4.bcast");
125
+ if(!st) {
126
+ st = rrd_stats_create("ipv4", "bcast", NULL, "ipv4", "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, CHART_TYPE_AREA);
127
+ st->isdetail = 1;
128
+
129
+ rrd_stats_dimension_add(st, "received", NULL, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
130
+ rrd_stats_dimension_add(st, "sent", NULL, -8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
131
+ }
132
+ else rrd_stats_next(st);
133
+
134
+ rrd_stats_dimension_set(st, "sent", OutBcastOctets);
135
+ rrd_stats_dimension_set(st, "received", InBcastOctets);
136
+ rrd_stats_done(st);
137
+ }
138
+
139
+ // --------------------------------------------------------------------
140
+
141
+ if(do_mcast_p) {
142
+ st = rrd_stats_find("ipv4.mcastpkts");
143
+ if(!st) {
144
+ st = rrd_stats_create("ipv4", "mcastpkts", NULL, "ipv4", "IPv4 Multicast Packets", "packets/s", 9500, update_every, CHART_TYPE_LINE);
145
+ st->isdetail = 1;
146
+
147
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
148
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
149
+ }
150
+ else rrd_stats_next(st);
151
+
152
+ rrd_stats_dimension_set(st, "sent", OutMcastPkts);
153
+ rrd_stats_dimension_set(st, "received", InMcastPkts);
154
+ rrd_stats_done(st);
155
+ }
156
+
157
+ // --------------------------------------------------------------------
158
+
159
+ if(do_bcast_p) {
160
+ st = rrd_stats_find("ipv4.bcastpkts");
161
+ if(!st) {
162
+ st = rrd_stats_create("ipv4", "bcastpkts", NULL, "ipv4", "IPv4 Broadcast Packets", "packets/s", 8500, update_every, CHART_TYPE_LINE);
163
+ st->isdetail = 1;
164
+
165
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
166
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
167
+ }
168
+ else rrd_stats_next(st);
169
+
170
+ rrd_stats_dimension_set(st, "sent", OutBcastPkts);
171
+ rrd_stats_dimension_set(st, "received", InBcastPkts);
172
+ rrd_stats_done(st);
173
+ }
174
+ }
175
+ }
176
+
177
+ return 0;
178
+}
src/proc_net_snmp.c
new
+359
@@ -0,0 +1,359 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+#define RRD_TYPE_NET_SNMP "ipv4"
13
+#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
14
+
15
+int do_proc_net_snmp(int update_every, unsigned long long dt) {
16
+ static procfile *ff = NULL;
17
+ static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
18
+ do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
19
+ do_udp_packets = -1, do_udp_errors = -1;
20
+
21
+ if(do_ip_packets == -1) do_ip_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
22
+ if(do_ip_fragsout == -1) do_ip_fragsout = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragrments sent", 1);
23
+ if(do_ip_fragsin == -1) do_ip_fragsin = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
24
+ if(do_ip_errors == -1) do_ip_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
25
+ if(do_tcp_sockets == -1) do_tcp_sockets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
26
+ if(do_tcp_packets == -1) do_tcp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
27
+ if(do_tcp_errors == -1) do_tcp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
28
+ if(do_tcp_handshake == -1) do_tcp_handshake = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
29
+ if(do_udp_packets == -1) do_udp_packets = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
30
+ if(do_udp_errors == -1) do_udp_errors = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
31
+
32
+ if(dt) {};
33
+
34
+ if(!ff) ff = procfile_open("/proc/net/snmp", " \t:");
35
+ if(!ff) return 1;
36
+
37
+ ff = procfile_readall(ff);
38
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
39
+
40
+ uint32_t lines = procfile_lines(ff), l;
41
+ uint32_t words;
42
+
43
+ RRD_STATS *st;
44
+
45
+ for(l = 0; l < lines ;l++) {
46
+ if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
47
+ l++;
48
+
49
+ if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
50
+ error("Cannot read Ip line from /proc/net/snmp.");
51
+ break;
52
+ }
53
+
54
+ words = procfile_linewords(ff, l);
55
+ if(words < 20) {
56
+ error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %d.", words);
57
+ continue;
58
+ }
59
+
60
+ // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
61
+ unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
62
+ OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
63
+
64
+ Forwarding = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
65
+ DefaultTTL = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
66
+ InReceives = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
67
+ InHdrErrors = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
68
+ InAddrErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
69
+ ForwDatagrams = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
70
+ InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
71
+ InDiscards = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
72
+ InDelivers = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
73
+ OutRequests = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
74
+ OutDiscards = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
75
+ OutNoRoutes = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
76
+ ReasmTimeout = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
77
+ ReasmReqds = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
78
+ ReasmOKs = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
79
+ ReasmFails = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
80
+ FragOKs = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
81
+ FragFails = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
82
+ FragCreates = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
83
+
84
+ // these are not counters
85
+ if(Forwarding) {}; // is forwarding enabled?
86
+ if(DefaultTTL) {}; // the default ttl on packets
87
+ if(ReasmTimeout) {}; // reassemply timeout
88
+
89
+ // this counter is not used
90
+ if(InDelivers) {}; // total number of packets delivered to IP user-protcols
91
+
92
+ // --------------------------------------------------------------------
93
+
94
+ if(do_ip_packets) {
95
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".packets");
96
+ if(!st) {
97
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "packets", NULL, RRD_TYPE_NET_SNMP, "IPv4 Packets", "packets/s", 3000, update_every, CHART_TYPE_LINE);
98
+
99
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
100
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
101
+ rrd_stats_dimension_add(st, "forwarded", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
102
+ }
103
+ else rrd_stats_next(st);
104
+
105
+ rrd_stats_dimension_set(st, "sent", OutRequests);
106
+ rrd_stats_dimension_set(st, "received", InReceives);
107
+ rrd_stats_dimension_set(st, "forwarded", ForwDatagrams);
108
+ rrd_stats_done(st);
109
+ }
110
+
111
+ // --------------------------------------------------------------------
112
+
113
+ if(do_ip_fragsout) {
114
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".fragsout");
115
+ if(!st) {
116
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, RRD_TYPE_NET_SNMP, "IPv4 Fragments Sent", "packets/s", 3010, update_every, CHART_TYPE_LINE);
117
+ st->isdetail = 1;
118
+
119
+ rrd_stats_dimension_add(st, "ok", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
120
+ rrd_stats_dimension_add(st, "failed", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
121
+ rrd_stats_dimension_add(st, "all", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
122
+ }
123
+ else rrd_stats_next(st);
124
+
125
+ rrd_stats_dimension_set(st, "ok", FragOKs);
126
+ rrd_stats_dimension_set(st, "failed", FragFails);
127
+ rrd_stats_dimension_set(st, "all", FragCreates);
128
+ rrd_stats_done(st);
129
+ }
130
+
131
+ // --------------------------------------------------------------------
132
+
133
+ if(do_ip_fragsin) {
134
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".fragsin");
135
+ if(!st) {
136
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, RRD_TYPE_NET_SNMP, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, CHART_TYPE_LINE);
137
+ st->isdetail = 1;
138
+
139
+ rrd_stats_dimension_add(st, "ok", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
140
+ rrd_stats_dimension_add(st, "failed", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
141
+ rrd_stats_dimension_add(st, "all", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
142
+ }
143
+ else rrd_stats_next(st);
144
+
145
+ rrd_stats_dimension_set(st, "ok", ReasmOKs);
146
+ rrd_stats_dimension_set(st, "failed", ReasmFails);
147
+ rrd_stats_dimension_set(st, "all", ReasmReqds);
148
+ rrd_stats_done(st);
149
+ }
150
+
151
+ // --------------------------------------------------------------------
152
+
153
+ if(do_ip_errors) {
154
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".errors");
155
+ if(!st) {
156
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "errors", NULL, RRD_TYPE_NET_SNMP, "IPv4 Errors", "packets/s", 3002, update_every, CHART_TYPE_LINE);
157
+ st->isdetail = 1;
158
+
159
+ rrd_stats_dimension_add(st, "InDiscards", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
160
+ rrd_stats_dimension_add(st, "OutDiscards", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
161
+
162
+ rrd_stats_dimension_add(st, "InHdrErrors", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
163
+ rrd_stats_dimension_add(st, "InAddrErrors", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
164
+ rrd_stats_dimension_add(st, "InUnknownProtos", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
165
+
166
+ rrd_stats_dimension_add(st, "OutNoRoutes", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
167
+ }
168
+ else rrd_stats_next(st);
169
+
170
+ rrd_stats_dimension_set(st, "InDiscards", InDiscards);
171
+ rrd_stats_dimension_set(st, "OutDiscards", OutDiscards);
172
+ rrd_stats_dimension_set(st, "InHdrErrors", InHdrErrors);
173
+ rrd_stats_dimension_set(st, "InAddrErrors", InAddrErrors);
174
+ rrd_stats_dimension_set(st, "InUnknownProtos", InUnknownProtos);
175
+ rrd_stats_dimension_set(st, "OutNoRoutes", OutNoRoutes);
176
+ rrd_stats_done(st);
177
+ }
178
+ }
179
+ else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
180
+ l++;
181
+
182
+ if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
183
+ error("Cannot read Tcp line from /proc/net/snmp.");
184
+ break;
185
+ }
186
+
187
+ words = procfile_linewords(ff, l);
188
+ if(words < 15) {
189
+ error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %d.", words);
190
+ continue;
191
+ }
192
+
193
+ unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
194
+ CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
195
+
196
+ RtoAlgorithm = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
197
+ RtoMin = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
198
+ RtoMax = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
199
+ MaxConn = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
200
+ ActiveOpens = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
201
+ PassiveOpens = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
202
+ AttemptFails = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
203
+ EstabResets = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
204
+ CurrEstab = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
205
+ InSegs = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
206
+ OutSegs = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
207
+ RetransSegs = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
208
+ InErrs = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
209
+ OutRsts = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
210
+
211
+ // these are not counters
212
+ if(RtoAlgorithm) {};
213
+ if(RtoMin) {};
214
+ if(RtoMax) {};
215
+ if(MaxConn) {};
216
+
217
+ // --------------------------------------------------------------------
218
+
219
+ // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
220
+ if(do_tcp_sockets) {
221
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcpsock");
222
+ if(!st) {
223
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", "IPv4 TCP Connections", "active connections", 2500, update_every, CHART_TYPE_LINE);
224
+
225
+ rrd_stats_dimension_add(st, "connections", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
226
+ }
227
+ else rrd_stats_next(st);
228
+
229
+ rrd_stats_dimension_set(st, "connections", CurrEstab);
230
+ rrd_stats_done(st);
231
+ }
232
+
233
+ // --------------------------------------------------------------------
234
+
235
+ if(do_tcp_packets) {
236
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcppackets");
237
+ if(!st) {
238
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", "IPv4 TCP Packets", "packets/s", 2600, update_every, CHART_TYPE_LINE);
239
+
240
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
241
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
242
+ }
243
+ else rrd_stats_next(st);
244
+
245
+ rrd_stats_dimension_set(st, "received", InSegs);
246
+ rrd_stats_dimension_set(st, "sent", OutSegs);
247
+ rrd_stats_done(st);
248
+ }
249
+
250
+ // --------------------------------------------------------------------
251
+
252
+ if(do_tcp_errors) {
253
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcperrors");
254
+ if(!st) {
255
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", "IPv4 TCP Errors", "packets/s", 2700, update_every, CHART_TYPE_LINE);
256
+ st->isdetail = 1;
257
+
258
+ rrd_stats_dimension_add(st, "InErrs", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
259
+ rrd_stats_dimension_add(st, "RetransSegs", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
260
+ }
261
+ else rrd_stats_next(st);
262
+
263
+ rrd_stats_dimension_set(st, "InErrs", InErrs);
264
+ rrd_stats_dimension_set(st, "RetransSegs", RetransSegs);
265
+ rrd_stats_done(st);
266
+ }
267
+
268
+ // --------------------------------------------------------------------
269
+
270
+ if(do_tcp_handshake) {
271
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcphandshake");
272
+ if(!st) {
273
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, CHART_TYPE_LINE);
274
+ st->isdetail = 1;
275
+
276
+ rrd_stats_dimension_add(st, "EstabResets", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
277
+ rrd_stats_dimension_add(st, "OutRsts", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
278
+ rrd_stats_dimension_add(st, "ActiveOpens", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
279
+ rrd_stats_dimension_add(st, "PassiveOpens", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
280
+ rrd_stats_dimension_add(st, "AttemptFails", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
281
+ }
282
+ else rrd_stats_next(st);
283
+
284
+ rrd_stats_dimension_set(st, "EstabResets", EstabResets);
285
+ rrd_stats_dimension_set(st, "OutRsts", OutRsts);
286
+ rrd_stats_dimension_set(st, "ActiveOpens", ActiveOpens);
287
+ rrd_stats_dimension_set(st, "PassiveOpens", PassiveOpens);
288
+ rrd_stats_dimension_set(st, "AttemptFails", AttemptFails);
289
+ rrd_stats_done(st);
290
+ }
291
+ }
292
+ else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
293
+ l++;
294
+
295
+ if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
296
+ error("Cannot read Udp line from /proc/net/snmp.");
297
+ break;
298
+ }
299
+
300
+ words = procfile_linewords(ff, l);
301
+ if(words < 7) {
302
+ error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %d.", words);
303
+ continue;
304
+ }
305
+
306
+ unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
307
+
308
+ InDatagrams = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
309
+ NoPorts = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
310
+ InErrors = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
311
+ OutDatagrams = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
312
+ RcvbufErrors = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
313
+ SndbufErrors = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
314
+
315
+ // --------------------------------------------------------------------
316
+
317
+ // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
318
+ if(do_udp_packets) {
319
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".udppackets");
320
+ if(!st) {
321
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", "IPv4 UDP Packets", "packets/s", 2601, update_every, CHART_TYPE_LINE);
322
+
323
+ rrd_stats_dimension_add(st, "received", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
324
+ rrd_stats_dimension_add(st, "sent", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
325
+ }
326
+ else rrd_stats_next(st);
327
+
328
+ rrd_stats_dimension_set(st, "received", InDatagrams);
329
+ rrd_stats_dimension_set(st, "sent", OutDatagrams);
330
+ rrd_stats_done(st);
331
+ }
332
+
333
+ // --------------------------------------------------------------------
334
+
335
+ if(do_udp_errors) {
336
+ st = rrd_stats_find(RRD_TYPE_NET_SNMP ".udperrors");
337
+ if(!st) {
338
+ st = rrd_stats_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", "IPv4 UDP Errors", "events/s", 2701, update_every, CHART_TYPE_LINE);
339
+ st->isdetail = 1;
340
+
341
+ rrd_stats_dimension_add(st, "RcvbufErrors", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
342
+ rrd_stats_dimension_add(st, "SndbufErrors", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
343
+ rrd_stats_dimension_add(st, "InErrors", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
344
+ rrd_stats_dimension_add(st, "NoPorts", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
345
+ }
346
+ else rrd_stats_next(st);
347
+
348
+ rrd_stats_dimension_set(st, "InErrors", InErrors);
349
+ rrd_stats_dimension_set(st, "NoPorts", NoPorts);
350
+ rrd_stats_dimension_set(st, "RcvbufErrors", RcvbufErrors);
351
+ rrd_stats_dimension_set(st, "SndbufErrors", SndbufErrors);
352
+ rrd_stats_done(st);
353
+ }
354
+ }
355
+ }
356
+
357
+ return 0;
358
+}
359
+
src/proc_net_stat_conntrack.c
new
+207
@@ -0,0 +1,207 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
13
+#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
14
+
15
+int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
16
+ static procfile *ff = NULL;
17
+ static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
18
+
19
+ if(do_sockets == -1) do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1);
20
+ if(do_new == -1) do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1);
21
+ if(do_changes == -1) do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1);
22
+ if(do_expect == -1) do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1);
23
+ if(do_search == -1) do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1);
24
+ if(do_errors == -1) do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1);
25
+
26
+ if(dt) {};
27
+
28
+ if(!ff) ff = procfile_open("/proc/net/stat/nf_conntrack", " \t:");
29
+ if(!ff) return 1;
30
+
31
+ ff = procfile_readall(ff);
32
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
33
+
34
+ uint32_t lines = procfile_lines(ff), l;
35
+ uint32_t words;
36
+
37
+ unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
38
+ ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
39
+
40
+ for(l = 1; l < lines ;l++) {
41
+ words = procfile_linewords(ff, l);
42
+ if(words < 17) {
43
+ error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %d.", words);
44
+ continue;
45
+ }
46
+
47
+ unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0;
48
+
49
+ tentries = strtoull(procfile_lineword(ff, l, 0), NULL, 16);
50
+ tsearched = strtoull(procfile_lineword(ff, l, 1), NULL, 16);
51
+ tfound = strtoull(procfile_lineword(ff, l, 2), NULL, 16);
52
+ tnew = strtoull(procfile_lineword(ff, l, 3), NULL, 16);
53
+ tinvalid = strtoull(procfile_lineword(ff, l, 4), NULL, 16);
54
+ tignore = strtoull(procfile_lineword(ff, l, 5), NULL, 16);
55
+ tdelete = strtoull(procfile_lineword(ff, l, 6), NULL, 16);
56
+ tdelete_list = strtoull(procfile_lineword(ff, l, 7), NULL, 16);
57
+ tinsert = strtoull(procfile_lineword(ff, l, 8), NULL, 16);
58
+ tinsert_failed = strtoull(procfile_lineword(ff, l, 9), NULL, 16);
59
+ tdrop = strtoull(procfile_lineword(ff, l, 10), NULL, 16);
60
+ tearly_drop = strtoull(procfile_lineword(ff, l, 11), NULL, 16);
61
+ ticmp_error = strtoull(procfile_lineword(ff, l, 12), NULL, 16);
62
+ texpect_new = strtoull(procfile_lineword(ff, l, 13), NULL, 16);
63
+ texpect_create = strtoull(procfile_lineword(ff, l, 14), NULL, 16);
64
+ texpect_delete = strtoull(procfile_lineword(ff, l, 15), NULL, 16);
65
+ tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16);
66
+
67
+ if(!aentries) aentries = tentries;
68
+
69
+ // sum all the cpus together
70
+ asearched += tsearched; // conntrack.search
71
+ afound += tfound; // conntrack.search
72
+ anew += tnew; // conntrack.new
73
+ ainvalid += tinvalid; // conntrack.new
74
+ aignore += tignore; // conntrack.new
75
+ adelete += tdelete; // conntrack.changes
76
+ adelete_list += tdelete_list; // conntrack.changes
77
+ ainsert += tinsert; // conntrack.changes
78
+ ainsert_failed += tinsert_failed; // conntrack.errors
79
+ adrop += tdrop; // conntrack.errors
80
+ aearly_drop += tearly_drop; // conntrack.errors
81
+ aicmp_error += ticmp_error; // conntrack.errors
82
+ aexpect_new += texpect_new; // conntrack.expect
83
+ aexpect_create += texpect_create; // conntrack.expect
84
+ aexpect_delete += texpect_delete; // conntrack.expect
85
+ asearch_restart += tsearch_restart; // conntrack.search
86
+ }
87
+
88
+ RRD_STATS *st;
89
+
90
+ // --------------------------------------------------------------------
91
+
92
+ if(do_sockets) {
93
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".sockets");
94
+ if(!st) {
95
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter Connections", "active connections", 1000, update_every, CHART_TYPE_LINE);
96
+
97
+ rrd_stats_dimension_add(st, "connections", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
98
+ }
99
+ else rrd_stats_next(st);
100
+
101
+ rrd_stats_dimension_set(st, "connections", aentries);
102
+ rrd_stats_done(st);
103
+ }
104
+
105
+ // --------------------------------------------------------------------
106
+
107
+ if(do_new) {
108
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".new");
109
+ if(!st) {
110
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter New Connections", "connections/s", 1001, update_every, CHART_TYPE_LINE);
111
+
112
+ rrd_stats_dimension_add(st, "new", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
113
+ rrd_stats_dimension_add(st, "ignore", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
114
+ rrd_stats_dimension_add(st, "invalid", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
115
+ }
116
+ else rrd_stats_next(st);
117
+
118
+ rrd_stats_dimension_set(st, "new", anew);
119
+ rrd_stats_dimension_set(st, "ignore", aignore);
120
+ rrd_stats_dimension_set(st, "invalid", ainvalid);
121
+ rrd_stats_done(st);
122
+ }
123
+
124
+ // --------------------------------------------------------------------
125
+
126
+ if(do_changes) {
127
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".changes");
128
+ if(!st) {
129
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter Connection Changes", "changes/s", 1002, update_every, CHART_TYPE_LINE);
130
+ st->isdetail = 1;
131
+
132
+ rrd_stats_dimension_add(st, "inserted", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
133
+ rrd_stats_dimension_add(st, "deleted", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
134
+ rrd_stats_dimension_add(st, "delete_list", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
135
+ }
136
+ else rrd_stats_next(st);
137
+
138
+ rrd_stats_dimension_set(st, "inserted", ainsert);
139
+ rrd_stats_dimension_set(st, "deleted", adelete);
140
+ rrd_stats_dimension_set(st, "delete_list", adelete_list);
141
+ rrd_stats_done(st);
142
+ }
143
+
144
+ // --------------------------------------------------------------------
145
+
146
+ if(do_expect) {
147
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".expect");
148
+ if(!st) {
149
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter Connection Expectations", "expectations/s", 1003, update_every, CHART_TYPE_LINE);
150
+ st->isdetail = 1;
151
+
152
+ rrd_stats_dimension_add(st, "created", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
153
+ rrd_stats_dimension_add(st, "deleted", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
154
+ rrd_stats_dimension_add(st, "new", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
155
+ }
156
+ else rrd_stats_next(st);
157
+
158
+ rrd_stats_dimension_set(st, "created", aexpect_create);
159
+ rrd_stats_dimension_set(st, "deleted", aexpect_delete);
160
+ rrd_stats_dimension_set(st, "new", aexpect_new);
161
+ rrd_stats_done(st);
162
+ }
163
+
164
+ // --------------------------------------------------------------------
165
+
166
+ if(do_search) {
167
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".search");
168
+ if(!st) {
169
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter Connection Searches", "searches/s", 1010, update_every, CHART_TYPE_LINE);
170
+ st->isdetail = 1;
171
+
172
+ rrd_stats_dimension_add(st, "searched", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
173
+ rrd_stats_dimension_add(st, "restarted", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
174
+ rrd_stats_dimension_add(st, "found", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
175
+ }
176
+ else rrd_stats_next(st);
177
+
178
+ rrd_stats_dimension_set(st, "searched", asearched);
179
+ rrd_stats_dimension_set(st, "restarted", asearch_restart);
180
+ rrd_stats_dimension_set(st, "found", afound);
181
+ rrd_stats_done(st);
182
+ }
183
+
184
+ // --------------------------------------------------------------------
185
+
186
+ if(do_errors) {
187
+ st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".errors");
188
+ if(!st) {
189
+ st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK, "errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, "Netfilter Errors", "events/s", 1005, update_every, CHART_TYPE_LINE);
190
+ st->isdetail = 1;
191
+
192
+ rrd_stats_dimension_add(st, "icmp_error", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
193
+ rrd_stats_dimension_add(st, "insert_failed", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
194
+ rrd_stats_dimension_add(st, "drop", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
195
+ rrd_stats_dimension_add(st, "early_drop", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
196
+ }
197
+ else rrd_stats_next(st);
198
+
199
+ rrd_stats_dimension_set(st, "icmp_error", aicmp_error);
200
+ rrd_stats_dimension_set(st, "insert_failed", ainsert_failed);
201
+ rrd_stats_dimension_set(st, "drop", adrop);
202
+ rrd_stats_dimension_set(st, "early_drop", aearly_drop);
203
+ rrd_stats_done(st);
204
+ }
205
+
206
+ return 0;
207
+}
src/proc_stat.c
new
+192
@@ -0,0 +1,192 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "log.h"
7
+#include "config.h"
8
+#include "procfile.h"
9
+#include "rrd.h"
10
+#include "plugin_proc.h"
11
+
12
+#define RRD_TYPE_STAT "cpu"
13
+#define RRD_TYPE_STAT_LEN strlen(RRD_TYPE_STAT)
14
+
15
+int do_proc_stat(int update_every, unsigned long long dt) {
16
+ static procfile *ff = NULL;
17
+ static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1;
18
+
19
+ if(do_cpu == -1) do_cpu = config_get_boolean("plugin:proc:/proc/stat", "cpu utilization", 1);
20
+ if(do_cpu_cores == -1) do_cpu_cores = config_get_boolean("plugin:proc:/proc/stat", "per cpu core utilization", 1);
21
+ if(do_interrupts == -1) do_interrupts = config_get_boolean("plugin:proc:/proc/stat", "cpu interrupts", 1);
22
+ if(do_context == -1) do_context = config_get_boolean("plugin:proc:/proc/stat", "context switches", 1);
23
+ if(do_forks == -1) do_forks = config_get_boolean("plugin:proc:/proc/stat", "processes started", 1);
24
+ if(do_processes == -1) do_processes = config_get_boolean("plugin:proc:/proc/stat", "processes running", 1);
25
+
26
+ if(dt) {};
27
+
28
+ if(!ff) ff = procfile_open("/proc/stat", " \t:");
29
+ if(!ff) return 1;
30
+
31
+ ff = procfile_readall(ff);
32
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
33
+
34
+ uint32_t lines = procfile_lines(ff), l;
35
+ uint32_t words;
36
+
37
+ unsigned long long processes = 0, running = 0 , blocked = 0;
38
+ RRD_STATS *st;
39
+
40
+ for(l = 0; l < lines ;l++) {
41
+ if(strncmp(procfile_lineword(ff, l, 0), "cpu", 3) == 0) {
42
+ words = procfile_linewords(ff, l);
43
+ if(words < 11) {
44
+ error("Cannot read /proc/stat cpu line. Expected 11 params, read %d.", words);
45
+ continue;
46
+ }
47
+
48
+ char *id;
49
+ unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
50
+
51
+ id = procfile_lineword(ff, l, 0);
52
+ user = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
53
+ nice = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
54
+ system = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
55
+ idle = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
56
+ iowait = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
57
+ irq = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
58
+ softirq = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
59
+ steal = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
60
+ guest = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
61
+ guest_nice = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
62
+
63
+ char *title = "Core utilization";
64
+ char *type = RRD_TYPE_STAT;
65
+ long priority = 1000;
66
+ int isthistotal = 0;
67
+ if(strcmp(id, "cpu") == 0) {
68
+ isthistotal = 1;
69
+ title = "Total CPU utilization";
70
+ type = "system";
71
+ priority = 100;
72
+ }
73
+
74
+ if((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores)) {
75
+ st = rrd_stats_find_bytype(type, id);
76
+ if(!st) {
77
+ st = rrd_stats_create(type, id, NULL, "cpu", title, "percentage", priority, update_every, CHART_TYPE_STACKED);
78
+
79
+ long multiplier = 1;
80
+ long divisor = 1; // sysconf(_SC_CLK_TCK);
81
+
82
+ rrd_stats_dimension_add(st, "guest_nice", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
83
+ rrd_stats_dimension_add(st, "guest", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
84
+ rrd_stats_dimension_add(st, "steal", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
85
+ rrd_stats_dimension_add(st, "softirq", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
86
+ rrd_stats_dimension_add(st, "irq", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
87
+ rrd_stats_dimension_add(st, "user", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
88
+ rrd_stats_dimension_add(st, "system", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
89
+ rrd_stats_dimension_add(st, "nice", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
90
+ rrd_stats_dimension_add(st, "iowait", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
91
+
92
+ rrd_stats_dimension_add(st, "idle", NULL, multiplier, divisor, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
93
+ rrd_stats_dimension_hide(st, "idle");
94
+ }
95
+ else rrd_stats_next(st);
96
+
97
+ rrd_stats_dimension_set(st, "user", user);
98
+ rrd_stats_dimension_set(st, "nice", nice);
99
+ rrd_stats_dimension_set(st, "system", system);
100
+ rrd_stats_dimension_set(st, "idle", idle);
101
+ rrd_stats_dimension_set(st, "iowait", iowait);
102
+ rrd_stats_dimension_set(st, "irq", irq);
103
+ rrd_stats_dimension_set(st, "softirq", softirq);
104
+ rrd_stats_dimension_set(st, "steal", steal);
105
+ rrd_stats_dimension_set(st, "guest", guest);
106
+ rrd_stats_dimension_set(st, "guest_nice", guest_nice);
107
+ rrd_stats_done(st);
108
+ }
109
+ }
110
+ else if(strcmp(procfile_lineword(ff, l, 0), "intr") == 0) {
111
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
112
+
113
+ // --------------------------------------------------------------------
114
+
115
+ if(do_interrupts) {
116
+ st = rrd_stats_find_bytype("system", "intr");
117
+ if(!st) {
118
+ st = rrd_stats_create("system", "intr", NULL, "cpu", "CPU Interrupts", "interrupts/s", 900, update_every, CHART_TYPE_LINE);
119
+ st->isdetail = 1;
120
+
121
+ rrd_stats_dimension_add(st, "interrupts", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
122
+ }
123
+ else rrd_stats_next(st);
124
+
125
+ rrd_stats_dimension_set(st, "interrupts", value);
126
+ rrd_stats_done(st);
127
+ }
128
+ }
129
+ else if(strcmp(procfile_lineword(ff, l, 0), "ctxt") == 0) {
130
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
131
+
132
+ // --------------------------------------------------------------------
133
+
134
+ if(do_context) {
135
+ st = rrd_stats_find_bytype("system", "ctxt");
136
+ if(!st) {
137
+ st = rrd_stats_create("system", "ctxt", NULL, "cpu", "CPU Context Switches", "context switches/s", 800, update_every, CHART_TYPE_LINE);
138
+
139
+ rrd_stats_dimension_add(st, "switches", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
140
+ }
141
+ else rrd_stats_next(st);
142
+
143
+ rrd_stats_dimension_set(st, "switches", value);
144
+ rrd_stats_done(st);
145
+ }
146
+ }
147
+ else if(!processes && strcmp(procfile_lineword(ff, l, 0), "processes") == 0) {
148
+ processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
149
+ }
150
+ else if(!running && strcmp(procfile_lineword(ff, l, 0), "procs_running") == 0) {
151
+ running = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
152
+ }
153
+ else if(!blocked && strcmp(procfile_lineword(ff, l, 0), "procs_blocked") == 0) {
154
+ blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
155
+ }
156
+ }
157
+
158
+ // --------------------------------------------------------------------
159
+
160
+ if(do_forks) {
161
+ st = rrd_stats_find_bytype("system", "forks");
162
+ if(!st) {
163
+ st = rrd_stats_create("system", "forks", NULL, "cpu", "New Processes", "processes/s", 700, update_every, CHART_TYPE_LINE);
164
+ st->isdetail = 1;
165
+
166
+ rrd_stats_dimension_add(st, "started", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
167
+ }
168
+ else rrd_stats_next(st);
169
+
170
+ rrd_stats_dimension_set(st, "started", processes);
171
+ rrd_stats_done(st);
172
+ }
173
+
174
+ // --------------------------------------------------------------------
175
+
176
+ if(do_processes) {
177
+ st = rrd_stats_find_bytype("system", "processes");
178
+ if(!st) {
179
+ st = rrd_stats_create("system", "processes", NULL, "cpu", "Processes", "processes", 600, update_every, CHART_TYPE_LINE);
180
+
181
+ rrd_stats_dimension_add(st, "running", NULL, 1, 1, RRD_DIMENSION_ABSOLUTE);
182
+ rrd_stats_dimension_add(st, "blocked", NULL, -1, 1, RRD_DIMENSION_ABSOLUTE);
183
+ }
184
+ else rrd_stats_next(st);
185
+
186
+ rrd_stats_dimension_set(st, "running", running);
187
+ rrd_stats_dimension_set(st, "blocked", blocked);
188
+ rrd_stats_done(st);
189
+ }
190
+
191
+ return 0;
192
+}
src/proc_vmstat.c
new
+209
@@ -0,0 +1,209 @@
1
+#include <inttypes.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+#include <unistd.h>
6
+
7
+#include "log.h"
8
+#include "config.h"
9
+#include "procfile.h"
10
+#include "rrd.h"
11
+#include "plugin_proc.h"
12
+
13
+#define MAX_PROC_VMSTAT_LINE 4096
14
+#define MAX_PROC_VMSTAT_NAME 1024
15
+
16
+int do_proc_vmstat(int update_every, unsigned long long dt) {
17
+ static procfile *ff = NULL;
18
+ static int do_swapio = -1, do_io = -1, do_pgfaults = -1;
19
+
20
+ if(do_swapio == -1) do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
21
+ if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
22
+ if(do_pgfaults == -1) do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
23
+
24
+ if(dt) {};
25
+
26
+ if(!ff) ff = procfile_open("/proc/vmstat", " \t:");
27
+ if(!ff) return 1;
28
+
29
+ ff = procfile_readall(ff);
30
+ if(!ff) return 0; // we return 0, so that we will retry to open it next time
31
+
32
+ uint32_t lines = procfile_lines(ff), l;
33
+ uint32_t words;
34
+
35
+ unsigned long long nr_free_pages = 0, nr_inactive_anon = 0, nr_active_anon = 0, nr_inactive_file = 0, nr_active_file = 0, nr_unevictable = 0, nr_mlock = 0,
36
+ nr_anon_pages = 0, nr_mapped = 0, nr_file_pages = 0, nr_dirty = 0, nr_writeback = 0, nr_slab_reclaimable = 0, nr_slab_unreclaimable = 0, nr_page_table_pages = 0,
37
+ nr_kernel_stack = 0, nr_unstable = 0, nr_bounce = 0, nr_vmscan_write = 0, nr_vmscan_immediate_reclaim = 0, nr_writeback_temp = 0, nr_isolated_anon = 0, nr_isolated_file = 0,
38
+ nr_shmem = 0, nr_dirtied = 0, nr_written = 0, nr_anon_transparent_hugepages = 0, nr_dirty_threshold = 0, nr_dirty_background_threshold = 0,
39
+ pgpgin = 0, pgpgout = 0, pswpin = 0, pswpout = 0, pgalloc_dma = 0, pgalloc_dma32 = 0, pgalloc_normal = 0, pgalloc_movable = 0, pgfree = 0, pgactivate = 0, pgdeactivate = 0,
40
+ pgfault = 0, pgmajfault = 0, pgrefill_dma = 0, pgrefill_dma32 = 0, pgrefill_normal = 0, pgrefill_movable = 0, pgsteal_kswapd_dma = 0, pgsteal_kswapd_dma32 = 0,
41
+ pgsteal_kswapd_normal = 0, pgsteal_kswapd_movable = 0, pgsteal_direct_dma = 0, pgsteal_direct_dma32 = 0, pgsteal_direct_normal = 0, pgsteal_direct_movable = 0,
42
+ pgscan_kswapd_dma = 0, pgscan_kswapd_dma32 = 0, pgscan_kswapd_normal = 0, pgscan_kswapd_movable = 0, pgscan_direct_dma = 0, pgscan_direct_dma32 = 0, pgscan_direct_normal = 0,
43
+ pgscan_direct_movable = 0, pginodesteal = 0, slabs_scanned = 0, kswapd_inodesteal = 0, kswapd_low_wmark_hit_quickly = 0, kswapd_high_wmark_hit_quickly = 0,
44
+ kswapd_skip_congestion_wait = 0, pageoutrun = 0, allocstall = 0, pgrotated = 0, compact_blocks_moved = 0, compact_pages_moved = 0, compact_pagemigrate_failed = 0,
45
+ compact_stall = 0, compact_fail = 0, compact_success = 0, htlb_buddy_alloc_success = 0, htlb_buddy_alloc_fail = 0, unevictable_pgs_culled = 0, unevictable_pgs_scanned = 0,
46
+ unevictable_pgs_rescued = 0, unevictable_pgs_mlocked = 0, unevictable_pgs_munlocked = 0, unevictable_pgs_cleared = 0, unevictable_pgs_stranded = 0, unevictable_pgs_mlockfreed = 0,
47
+ thp_fault_alloc = 0, thp_fault_fallback = 0, thp_collapse_alloc = 0, thp_collapse_alloc_failed = 0, thp_split = 0;
48
+
49
+ for(l = 0; l < lines ;l++) {
50
+ words = procfile_linewords(ff, l);
51
+ if(words < 2) {
52
+ error("Cannot read /proc/vmstat line %d. Expected 2 params, read %d.", l, words);
53
+ continue;
54
+ }
55
+
56
+ char *name = procfile_lineword(ff, l, 0);
57
+ unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
58
+
59
+ if(!nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = value;
60
+ else if(!nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = value;
61
+ else if(!nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = value;
62
+ else if(!nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = value;
63
+ else if(!nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = value;
64
+ else if(!nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = value;
65
+ else if(!nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = value;
66
+ else if(!nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = value;
67
+ else if(!nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = value;
68
+ else if(!nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = value;
69
+ else if(!nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = value;
70
+ else if(!nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = value;
71
+ else if(!nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = value;
72
+ else if(!nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = value;
73
+ else if(!nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = value;
74
+ else if(!nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = value;
75
+ else if(!nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = value;
76
+ else if(!nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = value;
77
+ else if(!nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = value;
78
+ else if(!nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = value;
79
+ else if(!nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = value;
80
+ else if(!nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = value;
81
+ else if(!nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = value;
82
+ else if(!nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = value;
83
+ else if(!nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = value;
84
+ else if(!nr_written && strcmp(name, "nr_written") == 0) nr_written = value;
85
+ else if(!nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = value;
86
+ else if(!nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = value;
87
+ else if(!nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = value;
88
+ else if(!pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = value;
89
+ else if(!pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = value;
90
+ else if(!pswpin && strcmp(name, "pswpin") == 0) pswpin = value;
91
+ else if(!pswpout && strcmp(name, "pswpout") == 0) pswpout = value;
92
+ else if(!pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = value;
93
+ else if(!pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = value;
94
+ else if(!pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = value;
95
+ else if(!pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = value;
96
+ else if(!pgfree && strcmp(name, "pgfree") == 0) pgfree = value;
97
+ else if(!pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = value;
98
+ else if(!pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = value;
99
+ else if(!pgfault && strcmp(name, "pgfault") == 0) pgfault = value;
100
+ else if(!pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = value;
101
+ else if(!pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = value;
102
+ else if(!pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = value;
103
+ else if(!pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = value;
104
+ else if(!pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = value;
105
+ else if(!pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = value;
106
+ else if(!pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = value;
107
+ else if(!pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = value;
108
+ else if(!pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = value;
109
+ else if(!pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = value;
110
+ else if(!pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = value;
111
+ else if(!pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = value;
112
+ else if(!pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = value;
113
+ else if(!pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = value;
114
+ else if(!pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = value;
115
+ else if(!pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = value;
116
+ else if(!pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = value;
117
+ else if(!pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = value;
118
+ else if(!pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = value;
119
+ else if(!pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = value;
120
+ else if(!pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = value;
121
+ else if(!pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = value;
122
+ else if(!slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = value;
123
+ else if(!kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = value;
124
+ else if(!kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = value;
125
+ else if(!kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = value;
126
+ else if(!kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = value;
127
+ else if(!pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = value;
128
+ else if(!allocstall && strcmp(name, "allocstall") == 0) allocstall = value;
129
+ else if(!pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = value;
130
+ else if(!compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = value;
131
+ else if(!compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = value;
132
+ else if(!compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = value;
133
+ else if(!compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = value;
134
+ else if(!compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = value;
135
+ else if(!compact_success && strcmp(name, "compact_success") == 0) compact_success = value;
136
+ else if(!htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = value;
137
+ else if(!htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = value;
138
+ else if(!unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = value;
139
+ else if(!unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = value;
140
+ else if(!unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = value;
141
+ else if(!unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = value;
142
+ else if(!unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = value;
143
+ else if(!unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = value;
144
+ else if(!unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = value;
145
+ else if(!unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = value;
146
+ else if(!thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = value;
147
+ else if(!thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = value;
148
+ else if(!thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = value;
149
+ else if(!thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = value;
150
+ else if(!thp_split && strcmp(name, "thp_split") == 0) thp_split = value;
151
+ }
152
+
153
+ RRD_STATS *st;
154
+
155
+ // --------------------------------------------------------------------
156
+
157
+ if(do_swapio) {
158
+ st = rrd_stats_find("system.swapio");
159
+ if(!st) {
160
+ st = rrd_stats_create("system", "swapio", NULL, "mem", "Swap I/O", "kilobytes/s", 250, update_every, CHART_TYPE_AREA);
161
+
162
+ rrd_stats_dimension_add(st, "in", NULL, sysconf(_SC_PAGESIZE), 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
163
+ rrd_stats_dimension_add(st, "out", NULL, -sysconf(_SC_PAGESIZE), 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
164
+ }
165
+ else rrd_stats_next(st);
166
+
167
+ rrd_stats_dimension_set(st, "in", pswpin);
168
+ rrd_stats_dimension_set(st, "out", pswpout);
169
+ rrd_stats_done(st);
170
+ }
171
+
172
+ // --------------------------------------------------------------------
173
+
174
+ if(do_io) {
175
+ st = rrd_stats_find("system.io");
176
+ if(!st) {
177
+ st = rrd_stats_create("system", "io", NULL, "disk", "Disk I/O", "kilobytes/s", 150, update_every, CHART_TYPE_AREA);
178
+
179
+ rrd_stats_dimension_add(st, "in", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
180
+ rrd_stats_dimension_add(st, "out", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
181
+ }
182
+ else rrd_stats_next(st);
183
+
184
+ rrd_stats_dimension_set(st, "in", pgpgin);
185
+ rrd_stats_dimension_set(st, "out", pgpgout);
186
+ rrd_stats_done(st);
187
+ }
188
+
189
+ // --------------------------------------------------------------------
190
+
191
+ if(do_pgfaults) {
192
+ st = rrd_stats_find("system.pgfaults");
193
+ if(!st) {
194
+ st = rrd_stats_create("system", "pgfaults", NULL, "mem", "Memory Page Faults", "page faults/s", 500, update_every, CHART_TYPE_LINE);
195
+ st->isdetail = 1;
196
+
197
+ rrd_stats_dimension_add(st, "minor", NULL, 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
198
+ rrd_stats_dimension_add(st, "major", NULL, -1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
199
+ }
200
+ else rrd_stats_next(st);
201
+
202
+ rrd_stats_dimension_set(st, "minor", pgfault);
203
+ rrd_stats_dimension_set(st, "major", pgmajfault);
204
+ rrd_stats_done(st);
205
+ }
206
+
207
+ return 0;
208
+}
209
+
src/procfile.c
new
+337
@@ -0,0 +1,337 @@
1
+#define _GNU_SOURCE
2
+
3
+#include <stdio.h>
4
+#include <stdlib.h>
5
+#include <unistd.h>
6
+#include <errno.h>
7
+#include <fcntl.h>
8
+#include <string.h>
9
+#include <malloc.h>
10
+#include <inttypes.h>
11
+#include <ctype.h>
12
+#include <time.h>
13
+#include <sys/time.h>
14
+#include <sys/wait.h>
15
+
16
+#include <sys/types.h>
17
+#include <sys/stat.h>
18
+#include <sys/mman.h>
19
+
20
+#include "procfile.h"
21
+
22
+#define PF_PREFIX "PROCFILE"
23
+int pfdebug = 0;
24
+
25
+// ----------------------------------------------------------------------------
26
+// An array of words
27
+
28
+#define PFWORDS_INCREASE_STEP 200
29
+
30
+pfwords *pfwords_add(pfwords *fw, char *str) {
31
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": adding word No %d: '%s'\n", fw->len, str);
32
+
33
+ if(fw->len == fw->size) {
34
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": expanding words\n");
35
+
36
+ pfwords *new = realloc(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
37
+ if(!new) {
38
+ fprintf(stderr, PF_PREFIX ": failed to expand words\n");
39
+ free(fw);
40
+ return NULL;
41
+ }
42
+ fw = new;
43
+ fw->size += PFWORDS_INCREASE_STEP;
44
+ }
45
+
46
+ fw->words[fw->len++] = str;
47
+
48
+ return fw;
49
+}
50
+
51
+pfwords *pfwords_new(void) {
52
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": initializing words\n");
53
+
54
+ pfwords *new = malloc(sizeof(pfwords) + PFWORDS_INCREASE_STEP * sizeof(char *));
55
+ if(!new) return NULL;
56
+
57
+ new->len = 0;
58
+ new->size = PFWORDS_INCREASE_STEP;
59
+ return new;
60
+}
61
+
62
+void pfwords_reset(pfwords *fw) {
63
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": reseting words\n");
64
+ fw->len = 0;
65
+}
66
+
67
+void pfwords_free(pfwords *fw) {
68
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": freeing words\n");
69
+
70
+ free(fw);
71
+}
72
+
73
+
74
+// ----------------------------------------------------------------------------
75
+// An array of lines
76
+
77
+#define PFLINES_INCREASE_STEP 10
78
+
79
+pflines *pflines_add(pflines *fl, uint32_t first_word) {
80
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": adding line %d at word %d\n", fl->len, first_word);
81
+
82
+ if(fl->len == fl->size) {
83
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": expanding lines\n");
84
+
85
+ pflines *new = realloc(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
86
+ if(!new) {
87
+ fprintf(stderr, PF_PREFIX ": failed to expand lines\n");
88
+ free(fl);
89
+ return NULL;
90
+ }
91
+ fl = new;
92
+ fl->size += PFLINES_INCREASE_STEP;
93
+ }
94
+
95
+ fl->lines[fl->len].words = 0;
96
+ fl->lines[fl->len++].first = first_word;
97
+
98
+ return fl;
99
+}
100
+
101
+pflines *pflines_new(void) {
102
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": initializing lines\n");
103
+
104
+ pflines *new = malloc(sizeof(pflines) + PFLINES_INCREASE_STEP * sizeof(ffline));
105
+ if(!new) return NULL;
106
+
107
+ new->len = 0;
108
+ new->size = PFLINES_INCREASE_STEP;
109
+ return new;
110
+}
111
+
112
+void pflines_reset(pflines *fl) {
113
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": reseting lines\n");
114
+
115
+ fl->len = 0;
116
+}
117
+
118
+void pflines_free(pflines *fl) {
119
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": freeing lines\n");
120
+
121
+ free(fl);
122
+}
123
+
124
+
125
+// ----------------------------------------------------------------------------
126
+// The procfile
127
+
128
+#define PROCFILE_INITIAL_BUFFER 256
129
+#define PROCFILE_INCREMENT_BUFFER 512
130
+
131
+#define PF_CHAR_IS_SEPARATOR 0
132
+#define PF_CHAR_IS_NEWLINE 1
133
+#define PF_CHAR_IS_WORD 2
134
+
135
+void procfile_close(procfile *ff) {
136
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": Closing file '%s'. Reason: %s\n", ff->filename, strerror(errno));
137
+
138
+ if(ff->lines) pflines_free(ff->lines);
139
+ if(ff->words) pfwords_free(ff->words);
140
+
141
+ if(ff->fd != -1) close(ff->fd);
142
+ free(ff);
143
+}
144
+
145
+procfile *procfile_parser(procfile *ff) {
146
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": Parsing file '%s'\n", ff->filename);
147
+
148
+ char *s = ff->data, *e = ff->data, *t = ff->data;
149
+ uint32_t l = 0, w = 0;
150
+ e += ff->len;
151
+
152
+ ff->lines = pflines_add(ff->lines, w);
153
+ if(!ff->lines) goto cleanup;
154
+
155
+ while(s < e) {
156
+ switch(ff->separators[(int)(*s)]) {
157
+ case PF_CHAR_IS_SEPARATOR:
158
+ if(s == t) {
159
+ // skip all leading white spaces
160
+ t = ++s;
161
+ continue;
162
+ }
163
+
164
+ // end of word
165
+ *s = '\0';
166
+
167
+ ff->words = pfwords_add(ff->words, t);
168
+ if(!ff->words) goto cleanup;
169
+
170
+ ff->lines->lines[l].words++;
171
+ w++;
172
+
173
+ t = ++s;
174
+ continue;
175
+
176
+ case PF_CHAR_IS_NEWLINE:
177
+ // end of line
178
+ *s = '\0';
179
+
180
+ ff->words = pfwords_add(ff->words, t);
181
+ if(!ff->words) goto cleanup;
182
+
183
+ ff->lines->lines[l].words++;
184
+ w++;
185
+
186
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": ended line %d with %d words\n", l, ff->lines->lines[l].words);
187
+
188
+ ff->lines = pflines_add(ff->lines, w);
189
+ if(!ff->lines) goto cleanup;
190
+ l++;
191
+
192
+ t = ++s;
193
+ continue;
194
+
195
+ default:
196
+ s++;
197
+ continue;
198
+ }
199
+ }
200
+
201
+ if(s != t) {
202
+ // the last word
203
+ if(ff->len < ff->size) *s = '\0';
204
+ else {
205
+ // we are going to loose the last byte
206
+ ff->data[ff->size - 1] = '\0';
207
+ }
208
+
209
+ ff->words = pfwords_add(ff->words, t);
210
+ if(!ff->words) goto cleanup;
211
+
212
+ ff->lines->lines[l].words++;
213
+ w++;
214
+ }
215
+
216
+ return ff;
217
+
218
+cleanup:
219
+ fprintf(stderr, PF_PREFIX ": Failed to parse file '%s'. Reason: %s\n", ff->filename, strerror(errno));
220
+ procfile_close(ff);
221
+ return NULL;
222
+}
223
+
224
+procfile *procfile_readall(procfile *ff) {
225
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": Reading file '%s'.\n", ff->filename);
226
+
227
+ ssize_t s = 0, r = ff->size, x = ff->size;
228
+ ff->len = 0;
229
+
230
+ while(r == x) {
231
+ if(s) {
232
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": Expanding data buffer for file '%s'.\n", ff->filename);
233
+
234
+ procfile *new = realloc(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
235
+ if(!new) {
236
+ fprintf(stderr, PF_PREFIX ": Cannot allocate memory for file '%s'. Reason: %s\n", ff->filename, strerror(errno));
237
+ procfile_close(ff);
238
+ return NULL;
239
+ }
240
+ ff = new;
241
+ ff->size += PROCFILE_INCREMENT_BUFFER;
242
+ x = PROCFILE_INCREMENT_BUFFER;
243
+ }
244
+
245
+ r = read(ff->fd, &ff->data[s], ff->size - s);
246
+ if(r == -1) {
247
+ fprintf(stderr, PF_PREFIX ": Cannot read from file '%s'. Reason: %s\n", ff->filename, strerror(errno));
248
+ procfile_close(ff);
249
+ return NULL;
250
+ }
251
+
252
+ ff->len += r;
253
+ s = ff->len;
254
+ }
255
+
256
+ if(lseek(ff->fd, 0, SEEK_SET) == -1) {
257
+ fprintf(stderr, PF_PREFIX ": Cannot rewind on file '%s'.\n", ff->filename);
258
+ procfile_close(ff);
259
+ return NULL;
260
+ }
261
+
262
+ pflines_reset(ff->lines);
263
+ pfwords_reset(ff->words);
264
+
265
+ ff = procfile_parser(ff);
266
+
267
+ return ff;
268
+}
269
+
270
+procfile *procfile_open(const char *filename, const char *separators) {
271
+ if(pfdebug) fprintf(stderr, PF_PREFIX ": Opening file '%s'\n", filename);
272
+
273
+ int fd = open(filename, O_RDONLY|O_NOATIME, 0666);
274
+ if(fd == -1) {
275
+ fprintf(stderr, PF_PREFIX ": Cannot open file '%s'. Reason: %s\n", filename, strerror(errno));
276
+ return NULL;
277
+ }
278
+
279
+ procfile *ff = malloc(sizeof(procfile) + PROCFILE_INITIAL_BUFFER);
280
+ if(!ff) {
281
+ fprintf(stderr, PF_PREFIX ": Cannot allocate memory for file '%s'. Reason: %s\n", ff->filename, strerror(errno));
282
+ close(fd);
283
+ return NULL;
284
+ }
285
+
286
+ strncpy(ff->filename, filename, FILENAME_MAX);
287
+ ff->filename[FILENAME_MAX] = '\0';
288
+
289
+ ff->fd = fd;
290
+ ff->size = PROCFILE_INITIAL_BUFFER;
291
+ ff->len = 0;
292
+
293
+ ff->lines = pflines_new();
294
+ ff->words = pfwords_new();
295
+
296
+ if(!ff->lines || !ff->words) {
297
+ fprintf(stderr, PF_PREFIX ": Cannot initialize parser for file '%s'. Reason: %s\n", ff->filename, strerror(errno));
298
+ procfile_close(ff);
299
+ return NULL;
300
+ }
301
+
302
+ int i;
303
+ for(i = 0; i < 256 ;i++) {
304
+ if(i == '\n' || i == '\r') ff->separators[i] = PF_CHAR_IS_NEWLINE;
305
+ else if(isspace(i) || !isprint(i)) ff->separators[i] = PF_CHAR_IS_SEPARATOR;
306
+ else ff->separators[i] = PF_CHAR_IS_WORD;
307
+ }
308
+
309
+ if(!separators) separators = " \t=|";
310
+ const char *s = separators;
311
+ while(*s) ff->separators[(int)*s++] = PF_CHAR_IS_SEPARATOR;
312
+
313
+ return ff;
314
+}
315
+
316
+
317
+// ----------------------------------------------------------------------------
318
+// example parsing of procfile data
319
+
320
+void procfile_print(procfile *ff) {
321
+ uint32_t lines = procfile_lines(ff), l;
322
+ uint32_t words, w;
323
+ char *s;
324
+
325
+ fprintf(stderr, "File '%s' with %d lines and %d words\n", ff->filename, ff->lines->len, ff->words->len);
326
+
327
+ for(l = 0; l < lines ;l++) {
328
+ words = procfile_linewords(ff, l);
329
+
330
+ fprintf(stderr, " line %d starts at word %d and has %d words\n", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
331
+
332
+ for(w = 0; w < words ;w++) {
333
+ s = procfile_lineword(ff, l, w);
334
+ fprintf(stderr, " [%d.%d] '%s'\n", l, w, s);
335
+ }
336
+ }
337
+}
src/procfile.h
new
+101
@@ -0,0 +1,101 @@
1
+/*
2
+ * procfile is a library for reading kernel files from /proc
3
+ *
4
+ * The idea is this:
5
+ *
6
+ * - every file is opened once with procfile_open().
7
+ *
8
+ * - to read updated contents, we rewind it (lseek() to 0) and read again
9
+ * with procfile_readall().
10
+ *
11
+ * - for every file, we use a buffer that is adjusted to fit its entire
12
+ * contents in memory, allowing us to read it with a single read() call.
13
+ * (this provides atomicity / consistency on the data read from the kernel)
14
+ *
15
+ * - once the data are read, we update two arrays of pointers:
16
+ * - a words array, pointing to each word in the data read
17
+ * - a lines array, pointing to the first word for each line
18
+ *
19
+ * This is highly optimized. Both arrays are automatically adjusted to
20
+ * fit all contents and are updated in a single pass on the data:
21
+ * - a raspberry Pi can process 5.000+ files / sec.
22
+ * - a J1900 celeron processor can process 23.000+ files / sec.
23
+*/
24
+
25
+
26
+#ifndef NETDATA_PROCFILE_H
27
+#define NETDATA_PROCFILE_H 1
28
+
29
+// if enabled, procfile will output a lot of debug info
30
+extern int pfdebug;
31
+
32
+// ----------------------------------------------------------------------------
33
+// An array of words
34
+
35
+typedef struct {
36
+ uint32_t len; // used entries
37
+ uint32_t size; // capacity
38
+ char *words[]; // array of pointers
39
+} pfwords;
40
+
41
+
42
+// ----------------------------------------------------------------------------
43
+// An array of lines
44
+
45
+typedef struct {
46
+ uint32_t words; // how many words this line has
47
+ uint32_t first; // the id of the first word of this line
48
+ // in the words array
49
+} ffline;
50
+
51
+typedef struct {
52
+ uint32_t len; // used entries
53
+ uint32_t size; // capacity
54
+ ffline lines[]; // array of lines
55
+} pflines;
56
+
57
+
58
+// ----------------------------------------------------------------------------
59
+// The procfile
60
+
61
+typedef struct {
62
+ char filename[FILENAME_MAX + 1];
63
+ int fd; // the file desriptor
64
+ ssize_t len; // the bytes we have placed into data
65
+ ssize_t size; // the bytes we have allocated for data
66
+ pflines *lines;
67
+ pfwords *words;
68
+ char separators[256];
69
+ char data[]; // allocated buffer to keep file contents
70
+} procfile;
71
+
72
+// close the proc file and free all related memory
73
+extern void procfile_close(procfile *ff);
74
+
75
+// (re)read and parse the proc file
76
+extern procfile *procfile_readall(procfile *ff);
77
+
78
+// open a /proc or /sys file
79
+extern procfile *procfile_open(const char *filename, const char *separators);
80
+
81
+// example walk-through a procfile parsed file
82
+extern void procfile_print(procfile *ff);
83
+
84
+// ----------------------------------------------------------------------------
85
+
86
+// return the number of lines present
87
+#define procfile_lines(ff) (ff->lines->len)
88
+
89
+// return the number of words of the Nth line
90
+#define procfile_linewords(ff, line) (((line) < procfile_lines(ff)) ? (ff)->lines->lines[(line)].words : 0)
91
+
92
+// return the Nth word of the file, or empty string
93
+#define procfile_word(ff, word) (((word) < (ff)->words->len) ? (ff)->words->words[(word)] : "")
94
+
95
+// return the first word of the Nth line, or empty string
96
+#define procfile_line(ff, line) (((line) < procfile_lines(ff)) ? procfile_word((ff), (ff)->lines->lines[(line)].first) : "")
97
+
98
+// return the Nth word of the current line
99
+#define procfile_lineword(ff, line, word) (((line) < procfile_lines(ff) && (word) < procfile_linewords(ff, (line))) ? procfile_word((ff), (ff)->lines->lines[(line)].first + word) : "")
100
+
101
+#endif /* NETDATA_PROCFILE_H */
src/rrd.c
new
+961
@@ -0,0 +1,961 @@
1
+#include <string.h>
2
+#include <unistd.h>
3
+#include <time.h>
4
+#include <sys/time.h>
5
+#include <sys/mman.h>
6
+#include <pthread.h>
7
+#include <errno.h>
8
+#include <ctype.h>
9
+#include <sys/stat.h>
10
+#include <sys/types.h>
11
+#include <stdlib.h>
12
+
13
+#include "log.h"
14
+#include "config.h"
15
+#include "common.h"
16
+
17
+#include "rrd.h"
18
+
19
+int update_every = UPDATE_EVERY;
20
+int save_history = HISTORY;
21
+
22
+// ----------------------------------------------------------------------------
23
+// chart types
24
+
25
+int chart_type_id(const char *name)
26
+{
27
+ if(strcmp(name, CHART_TYPE_AREA_NAME) == 0) return CHART_TYPE_AREA;
28
+ if(strcmp(name, CHART_TYPE_STACKED_NAME) == 0) return CHART_TYPE_STACKED;
29
+ if(strcmp(name, CHART_TYPE_LINE_NAME) == 0) return CHART_TYPE_LINE;
30
+ return CHART_TYPE_LINE;
31
+}
32
+
33
+const char *chart_type_name(int chart_type)
34
+{
35
+ static char line[] = CHART_TYPE_LINE_NAME;
36
+ static char area[] = CHART_TYPE_AREA_NAME;
37
+ static char stacked[] = CHART_TYPE_STACKED_NAME;
38
+
39
+ switch(chart_type) {
40
+ case CHART_TYPE_LINE:
41
+ return line;
42
+
43
+ case CHART_TYPE_AREA:
44
+ return area;
45
+
46
+ case CHART_TYPE_STACKED:
47
+ return stacked;
48
+ }
49
+ return line;
50
+}
51
+
52
+// ----------------------------------------------------------------------------
53
+// mmap() wrapper
54
+
55
+int memory_mode = NETDATA_MEMORY_MODE_SAVE;
56
+
57
+const char *memory_mode_name(int id)
58
+{
59
+ static const char ram[] = NETDATA_MEMORY_MODE_RAM_NAME;
60
+ static const char map[] = NETDATA_MEMORY_MODE_MAP_NAME;
61
+ static const char save[] = NETDATA_MEMORY_MODE_SAVE_NAME;
62
+
63
+ switch(id) {
64
+ case NETDATA_MEMORY_MODE_RAM:
65
+ return ram;
66
+
67
+ case NETDATA_MEMORY_MODE_MAP:
68
+ return map;
69
+
70
+ case NETDATA_MEMORY_MODE_SAVE:
71
+ default:
72
+ return save;
73
+ }
74
+
75
+ return save;
76
+}
77
+
78
+int memory_mode_id(const char *name)
79
+{
80
+ if(!strcmp(name, NETDATA_MEMORY_MODE_RAM_NAME))
81
+ return NETDATA_MEMORY_MODE_RAM;
82
+ else if(!strcmp(name, NETDATA_MEMORY_MODE_MAP_NAME))
83
+ return NETDATA_MEMORY_MODE_MAP;
84
+
85
+ return NETDATA_MEMORY_MODE_SAVE;
86
+}
87
+
88
+// ----------------------------------------------------------------------------
89
+// algorithms types
90
+
91
+int algorithm_id(const char *name)
92
+{
93
+ if(strcmp(name, RRD_DIMENSION_ABSOLUTE_NAME) == 0) return RRD_DIMENSION_ABSOLUTE;
94
+ if(strcmp(name, RRD_DIMENSION_INCREMENTAL_NAME) == 0) return RRD_DIMENSION_INCREMENTAL;
95
+ if(strcmp(name, RRD_DIMENSION_PCENT_OVER_ROW_TOTAL_NAME) == 0) return RRD_DIMENSION_PCENT_OVER_ROW_TOTAL;
96
+ if(strcmp(name, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL_NAME) == 0) return RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL;
97
+ return RRD_DIMENSION_ABSOLUTE;
98
+}
99
+
100
+const char *algorithm_name(int chart_type)
101
+{
102
+ static char absolute[] = RRD_DIMENSION_ABSOLUTE_NAME;
103
+ static char incremental[] = RRD_DIMENSION_INCREMENTAL_NAME;
104
+ static char percentage_of_absolute_row[] = RRD_DIMENSION_PCENT_OVER_ROW_TOTAL_NAME;
105
+ static char percentage_of_incremental_row[] = RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL_NAME;
106
+
107
+ switch(chart_type) {
108
+ case RRD_DIMENSION_ABSOLUTE:
109
+ return absolute;
110
+
111
+ case RRD_DIMENSION_INCREMENTAL:
112
+ return incremental;
113
+
114
+ case RRD_DIMENSION_PCENT_OVER_ROW_TOTAL:
115
+ return percentage_of_absolute_row;
116
+
117
+ case RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL:
118
+ return percentage_of_incremental_row;
119
+ }
120
+ return absolute;
121
+}
122
+
123
+RRD_STATS *root = NULL;
124
+pthread_rwlock_t root_rwlock = PTHREAD_RWLOCK_INITIALIZER;
125
+
126
+char *rrd_stats_strncpy_name(char *to, const char *from, int length)
127
+{
128
+ int i;
129
+ for(i = 0; i < length && from[i] ;i++) {
130
+ if(from[i] == '.' || isalpha(from[i]) || isdigit(from[i])) to[i] = from[i];
131
+ else to[i] = '_';
132
+ }
133
+ if(i < length) to[i] = '\0';
134
+ to[length - 1] = '\0';
135
+
136
+ return to;
137
+}
138
+
139
+void rrd_stats_set_name(RRD_STATS *st, const char *name)
140
+{
141
+ char b[CONFIG_MAX_VALUE + 1];
142
+ char n[RRD_STATS_NAME_MAX + 1];
143
+
144
+ snprintf(n, RRD_STATS_NAME_MAX, "%s.%s", st->type, name);
145
+ rrd_stats_strncpy_name(b, n, CONFIG_MAX_VALUE);
146
+ st->name = config_get(st->id, "name", b);
147
+ st->hash_name = simple_hash(st->name);
148
+}
149
+
150
+char *rrd_stats_cache_dir(const char *id)
151
+{
152
+ char *ret = NULL;
153
+
154
+ static char *cache_dir = NULL;
155
+ if(!cache_dir) cache_dir = config_get("global", "database directory", "cache");
156
+
157
+ char b[FILENAME_MAX + 1];
158
+ char n[FILENAME_MAX + 1];
159
+ rrd_stats_strncpy_name(b, id, FILENAME_MAX);
160
+
161
+ snprintf(n, FILENAME_MAX, "%s/%s", cache_dir, b);
162
+ ret = config_get(id, "database directory", n);
163
+
164
+ if(memory_mode == NETDATA_MEMORY_MODE_MAP || memory_mode == NETDATA_MEMORY_MODE_SAVE) {
165
+ int r = mkdir(ret, 0775);
166
+ if(r != 0 && errno != EEXIST)
167
+ error("Cannot create directory '%s'", ret);
168
+ }
169
+
170
+ return ret;
171
+}
172
+
173
+void rrd_stats_reset(RRD_STATS *st)
174
+{
175
+ st->last_collected_time.tv_sec = 0;
176
+ st->last_collected_time.tv_usec = 0;
177
+ st->current_entry = 0;
178
+ st->counter_done = 0;
179
+
180
+ RRD_DIMENSION *rd;
181
+ for(rd = st->dimensions; rd ; rd = rd->next) {
182
+ rd->last_collected_time.tv_sec = 0;
183
+ rd->last_collected_time.tv_usec = 0;
184
+ rd->current_entry = 0;
185
+ }
186
+}
187
+
188
+RRD_STATS *rrd_stats_create(const char *type, const char *id, const char *name, const char *family, const char *title, const char *units, long priority, int update_every, int chart_type)
189
+{
190
+ if(!id || !id[0]) {
191
+ fatal("Cannot create rrd stats without an id.");
192
+ return NULL;
193
+ }
194
+
195
+ char fullid[RRD_STATS_NAME_MAX + 1];
196
+ char fullfilename[FILENAME_MAX + 1];
197
+ RRD_STATS *st = NULL;
198
+
199
+ snprintf(fullid, RRD_STATS_NAME_MAX, "%s.%s", type, id);
200
+
201
+ long entries = config_get_number(fullid, "history", save_history);
202
+ if(entries < 5) entries = config_set_number(fullid, "history", 5);
203
+ if(entries > HISTORY_MAX) entries = config_set_number(fullid, "history", HISTORY_MAX);
204
+
205
+ int enabled = config_get_boolean(fullid, "enabled", 1);
206
+ if(!enabled) entries = 5;
207
+
208
+ unsigned long size = sizeof(RRD_STATS);
209
+ char *cache_dir = rrd_stats_cache_dir(fullid);
210
+
211
+ debug(D_RRD_STATS, "Creating RRD_STATS for '%s.%s'.", type, id);
212
+
213
+ snprintf(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
214
+ if(memory_mode != NETDATA_MEMORY_MODE_RAM) st = (RRD_STATS *)mymmap(fullfilename, size, ((memory_mode == NETDATA_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE));
215
+ if(st) {
216
+ if(strcmp(st->magic, RRD_STATS_MAGIC) != 0) {
217
+ errno = 0;
218
+ error("File %s does not have our version. Clearing it.", fullfilename);
219
+ bzero(st, size);
220
+ }
221
+ else if(strcmp(st->id, fullid) != 0) {
222
+ errno = 0;
223
+ error("File %s does not have our id. Unmapping it.", fullfilename);
224
+ munmap(st, size);
225
+ st = NULL;
226
+ }
227
+ else if(st->memsize != size || st->entries != entries) {
228
+ errno = 0;
229
+ error("File %s does not have the desired size. Clearing it.", fullfilename);
230
+ bzero(st, size);
231
+ }
232
+ else if(st->update_every != update_every) {
233
+ errno = 0;
234
+ error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
235
+ bzero(st, size);
236
+ }
237
+ }
238
+
239
+ if(st) {
240
+ st->name = NULL;
241
+ st->type = NULL;
242
+ st->family = NULL;
243
+ st->title = NULL;
244
+ st->units = NULL;
245
+ st->dimensions = NULL;
246
+ st->next = NULL;
247
+ st->mapped = memory_mode;
248
+ }
249
+ else {
250
+ st = calloc(1, size);
251
+ if(!st) {
252
+ fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
253
+ return NULL;
254
+ }
255
+ st->mapped = NETDATA_MEMORY_MODE_RAM;
256
+ }
257
+ st->memsize = size;
258
+ st->entries = entries;
259
+ st->update_every = update_every;
260
+
261
+ strcpy(st->cache_file, fullfilename);
262
+ strcpy(st->magic, RRD_STATS_MAGIC);
263
+
264
+ strcpy(st->id, fullid);
265
+ st->hash = simple_hash(st->id);
266
+
267
+ st->cache_dir = cache_dir;
268
+
269
+ st->family = config_get(st->id, "family", family?family:st->id);
270
+ st->units = config_get(st->id, "units", units?units:"");
271
+ st->type = config_get(st->id, "type", type);
272
+ st->chart_type = chart_type_id(config_get(st->id, "chart type", chart_type_name(chart_type)));
273
+
274
+ if(name && *name) rrd_stats_set_name(st, name);
275
+ else rrd_stats_set_name(st, id);
276
+
277
+ {
278
+ char varvalue[CONFIG_MAX_VALUE + 1];
279
+ snprintf(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
280
+ st->title = config_get(st->id, "title", varvalue);
281
+ }
282
+
283
+ st->priority = config_get_number(st->id, "priority", priority);
284
+ st->enabled = enabled;
285
+
286
+ st->isdetail = 0;
287
+ st->debug = 0;
288
+
289
+ st->last_collected_time.tv_sec = 0;
290
+ st->last_collected_time.tv_usec = 0;
291
+ st->counter_done = 0;
292
+
293
+ pthread_rwlock_init(&st->rwlock, NULL);
294
+ pthread_rwlock_wrlock(&root_rwlock);
295
+
296
+ st->next = root;
297
+ root = st;
298
+
299
+ pthread_rwlock_unlock(&root_rwlock);
300
+
301
+ return(st);
302
+}
303
+
304
+RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
305
+{
306
+ char filename[FILENAME_MAX + 1];
307
+ char fullfilename[FILENAME_MAX + 1];
308
+
309
+ char varname[CONFIG_MAX_NAME + 1];
310
+ RRD_DIMENSION *rd = NULL;
311
+ unsigned long size = sizeof(RRD_DIMENSION) + (st->entries * sizeof(storage_number));
312
+
313
+ debug(D_RRD_STATS, "Adding dimension '%s/%s'.", st->id, id);
314
+
315
+ rrd_stats_strncpy_name(filename, id, FILENAME_MAX);
316
+ snprintf(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
317
+ if(memory_mode != NETDATA_MEMORY_MODE_RAM) rd = (RRD_DIMENSION *)mymmap(fullfilename, size, ((memory_mode == NETDATA_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE));
318
+ if(rd) {
319
+ struct timeval now;
320
+ gettimeofday(&now, NULL);
321
+
322
+ if(strcmp(rd->magic, RRD_DIMENSION_MAGIC) != 0) {
323
+ errno = 0;
324
+ error("File %s does not have our version. Clearing it.", fullfilename);
325
+ bzero(rd, size);
326
+ }
327
+ else if(rd->memsize != size) {
328
+ errno = 0;
329
+ error("File %s does not have the desired size. Clearing it.", fullfilename);
330
+ bzero(rd, size);
331
+ }
332
+ else if(rd->multiplier != multiplier) {
333
+ errno = 0;
334
+ error("File %s does not have the same multiplier. Clearing it.", fullfilename);
335
+ bzero(rd, size);
336
+ }
337
+ else if(rd->divisor != divisor) {
338
+ errno = 0;
339
+ error("File %s does not have the same divisor. Clearing it.", fullfilename);
340
+ bzero(rd, size);
341
+ }
342
+ else if(rd->algorithm != algorithm) {
343
+ errno = 0;
344
+ error("File %s does not have the same algorithm. Clearing it.", fullfilename);
345
+ bzero(rd, size);
346
+ }
347
+ else if(rd->update_every != st->update_every) {
348
+ errno = 0;
349
+ error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
350
+ bzero(rd, size);
351
+ }
352
+ else if(usecdiff(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
353
+ errno = 0;
354
+ error("File %s is too old. Clearing it.", fullfilename);
355
+ bzero(rd, size);
356
+ }
357
+ else if(strcmp(rd->id, id) != 0) {
358
+ errno = 0;
359
+ error("File %s does not have our dimension id. Unmapping it.", fullfilename);
360
+ munmap(rd, size);
361
+ rd = NULL;
362
+ }
363
+ }
364
+
365
+ if(rd) {
366
+ // we have a file mapped for rd
367
+ rd->mapped = memory_mode;
368
+ rd->hidden = 0;
369
+ rd->next = NULL;
370
+ rd->name = NULL;
371
+ }
372
+ else {
373
+ // if we didn't manage to get a mmap'd dimension, just create one
374
+
375
+ rd = calloc(1, size);
376
+ if(!rd) {
377
+ fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
378
+ return NULL;
379
+ }
380
+
381
+ rd->mapped = NETDATA_MEMORY_MODE_RAM;
382
+ }
383
+ rd->memsize = size;
384
+
385
+ strcpy(rd->magic, RRD_DIMENSION_MAGIC);
386
+ strcpy(rd->cache_file, fullfilename);
387
+ strncpy(rd->id, id, RRD_STATS_NAME_MAX);
388
+ rd->hash = simple_hash(rd->id);
389
+
390
+ snprintf(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
391
+ rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
392
+
393
+ snprintf(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
394
+ rd->algorithm = algorithm_id(config_get(st->id, varname, algorithm_name(algorithm)));
395
+
396
+ snprintf(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
397
+ rd->multiplier = config_get_number(st->id, varname, multiplier);
398
+
399
+ snprintf(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
400
+ rd->divisor = config_get_number(st->id, varname, divisor);
401
+ if(!rd->divisor) rd->divisor = 1;
402
+
403
+ rd->entries = st->entries;
404
+ rd->update_every = st->update_every;
405
+
406
+ // append this dimension
407
+ if(!st->dimensions)
408
+ st->dimensions = rd;
409
+ else {
410
+ RRD_DIMENSION *td = st->dimensions;
411
+ for(; td->next; td = td->next) ;
412
+ td->next = rd;
413
+ }
414
+
415
+ return(rd);
416
+}
417
+
418
+void rrd_stats_dimension_set_name(RRD_STATS *st, RRD_DIMENSION *rd, const char *name)
419
+{
420
+ char varname[CONFIG_MAX_NAME + 1];
421
+ snprintf(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
422
+ config_get(st->id, varname, name);
423
+}
424
+
425
+void rrd_stats_dimension_free(RRD_DIMENSION *rd)
426
+{
427
+ if(rd->next) rrd_stats_dimension_free(rd->next);
428
+ // free(rd->annotations);
429
+ if(rd->mapped == NETDATA_MEMORY_MODE_SAVE) {
430
+ debug(D_RRD_STATS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_file);
431
+ savememory(rd->cache_file, rd, rd->memsize);
432
+
433
+ debug(D_RRD_STATS, "Unmapping dimension '%s'.", rd->name);
434
+ munmap(rd, rd->memsize);
435
+ }
436
+ else if(rd->mapped == NETDATA_MEMORY_MODE_MAP) {
437
+ debug(D_RRD_STATS, "Unmapping dimension '%s'.", rd->name);
438
+ munmap(rd, rd->memsize);
439
+ }
440
+ else {
441
+ debug(D_RRD_STATS, "Removing dimension '%s'.", rd->name);
442
+ free(rd);
443
+ }
444
+}
445
+
446
+void rrd_stats_free_all(void)
447
+{
448
+ info("Freeing all memory...");
449
+
450
+ RRD_STATS *st;
451
+ for(st = root; st ;) {
452
+ RRD_STATS *next = st->next;
453
+
454
+ if(st->dimensions) rrd_stats_dimension_free(st->dimensions);
455
+ st->dimensions = NULL;
456
+
457
+ if(st->mapped == NETDATA_MEMORY_MODE_SAVE) {
458
+ debug(D_RRD_STATS, "Saving stats '%s' to '%s'.", st->name, st->cache_file);
459
+ savememory(st->cache_file, st, st->memsize);
460
+
461
+ debug(D_RRD_STATS, "Unmapping stats '%s'.", st->name);
462
+ munmap(st, st->memsize);
463
+ }
464
+ else if(st->mapped == NETDATA_MEMORY_MODE_MAP) {
465
+ debug(D_RRD_STATS, "Unmapping stats '%s'.", st->name);
466
+ munmap(st, st->memsize);
467
+ }
468
+ else
469
+ free(st);
470
+
471
+ st = next;
472
+ }
473
+ root = NULL;
474
+
475
+ info("Memory cleanup completed...");
476
+}
477
+
478
+void rrd_stats_save_all(void)
479
+{
480
+ RRD_STATS *st;
481
+ RRD_DIMENSION *rd;
482
+
483
+ pthread_rwlock_wrlock(&root_rwlock);
484
+ for(st = root; st ; st = st->next) {
485
+ pthread_rwlock_wrlock(&st->rwlock);
486
+
487
+ if(st->mapped == NETDATA_MEMORY_MODE_SAVE) {
488
+ debug(D_RRD_STATS, "Saving stats '%s' to '%s'.", st->name, st->cache_file);
489
+ savememory(st->cache_file, st, st->memsize);
490
+ }
491
+
492
+ for(rd = st->dimensions; rd ; rd = rd->next) {
493
+ if(rd->mapped == NETDATA_MEMORY_MODE_SAVE) {
494
+ debug(D_RRD_STATS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_file);
495
+ savememory(rd->cache_file, rd, rd->memsize);
496
+ }
497
+ }
498
+
499
+ pthread_rwlock_unlock(&st->rwlock);
500
+ }
501
+ pthread_rwlock_unlock(&root_rwlock);
502
+}
503
+
504
+
505
+RRD_STATS *rrd_stats_find(const char *id)
506
+{
507
+ unsigned long hash = simple_hash(id);
508
+
509
+ pthread_rwlock_rdlock(&root_rwlock);
510
+ RRD_STATS *st = root;
511
+ for ( ; st ; st = st->next )
512
+ if(hash == st->hash)
513
+ if(strcmp(st->id, id) == 0)
514
+ break;
515
+ pthread_rwlock_unlock(&root_rwlock);
516
+
517
+ return(st);
518
+}
519
+
520
+RRD_STATS *rrd_stats_find_bytype(const char *type, const char *id)
521
+{
522
+ char buf[RRD_STATS_NAME_MAX + 1];
523
+
524
+ strncpy(buf, type, RRD_STATS_NAME_MAX - 1);
525
+ buf[RRD_STATS_NAME_MAX - 1] = '\0';
526
+ strcat(buf, ".");
527
+ int len = strlen(buf);
528
+ strncpy(&buf[len], id, RRD_STATS_NAME_MAX - len);
529
+ buf[RRD_STATS_NAME_MAX] = '\0';
530
+
531
+ return(rrd_stats_find(buf));
532
+}
533
+
534
+RRD_STATS *rrd_stats_find_byname(const char *name)
535
+{
536
+ char b[CONFIG_MAX_VALUE + 1];
537
+
538
+ rrd_stats_strncpy_name(b, name, CONFIG_MAX_VALUE);
539
+ unsigned long hash = simple_hash(b);
540
+
541
+ pthread_rwlock_rdlock(&root_rwlock);
542
+ RRD_STATS *st = root;
543
+ for ( ; st ; st = st->next ) {
544
+ if(hash == st->hash_name && strcmp(st->name, b) == 0) break;
545
+ }
546
+ pthread_rwlock_unlock(&root_rwlock);
547
+
548
+ return(st);
549
+}
550
+
551
+RRD_DIMENSION *rrd_stats_dimension_find(RRD_STATS *st, const char *id)
552
+{
553
+ unsigned long hash = simple_hash(id);
554
+
555
+ RRD_DIMENSION *rd = st->dimensions;
556
+
557
+ for ( ; rd ; rd = rd->next )
558
+ if(hash == rd->hash)
559
+ if(strcmp(rd->id, id) == 0)
560
+ break;
561
+
562
+ return(rd);
563
+}
564
+
565
+int rrd_stats_dimension_hide(RRD_STATS *st, const char *id)
566
+{
567
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
568
+ if(!rd) {
569
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
570
+ return 1;
571
+ }
572
+
573
+ rd->hidden = 1;
574
+ return 0;
575
+}
576
+
577
+void rrd_stats_dimension_set_by_pointer(RRD_STATS *st, RRD_DIMENSION *rd, collected_number value)
578
+{
579
+ if(st) {;}
580
+
581
+ gettimeofday(&rd->last_collected_time, NULL);
582
+ rd->collected_value = value;
583
+}
584
+
585
+int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value)
586
+{
587
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
588
+ if(!rd) {
589
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
590
+ return 1;
591
+ }
592
+
593
+ rrd_stats_dimension_set_by_pointer(st, rd, value);
594
+ return 0;
595
+}
596
+
597
+void rrd_stats_next_usec(RRD_STATS *st, unsigned long long microseconds)
598
+{
599
+ if(st->debug) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
600
+ st->usec_since_last_update = microseconds;
601
+}
602
+
603
+void rrd_stats_next(RRD_STATS *st)
604
+{
605
+ unsigned long long microseconds = 0;
606
+
607
+ if(st->last_collected_time.tv_sec) {
608
+ struct timeval now;
609
+ gettimeofday(&now, NULL);
610
+ microseconds = usecdiff(&now, &st->last_collected_time);
611
+ }
612
+
613
+ rrd_stats_next_usec(st, microseconds);
614
+}
615
+
616
+void rrd_stats_next_plugins(RRD_STATS *st)
617
+{
618
+ rrd_stats_next(st);
619
+}
620
+
621
+unsigned long long rrd_stats_done(RRD_STATS *st)
622
+{
623
+ RRD_DIMENSION *rd, *last;
624
+ int oldstate;
625
+
626
+ if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0)
627
+ error("Cannot set pthread cancel state to DISABLE.");
628
+
629
+ // a read lock is OK here
630
+ pthread_rwlock_rdlock(&st->rwlock);
631
+
632
+ if(st->debug) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
633
+
634
+ if(!st->last_collected_time.tv_sec) gettimeofday(&st->last_collected_time, NULL);
635
+ else {
636
+ unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
637
+ st->last_collected_time.tv_sec = ut / 1000000ULL;
638
+ st->last_collected_time.tv_usec = ut % 1000000ULL;
639
+ }
640
+
641
+ if(!st->last_updated.tv_sec) {
642
+ unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
643
+ st->last_updated.tv_sec = ut / 1000000ULL;
644
+ st->last_updated.tv_usec = ut % 1000000ULL;
645
+
646
+ if(st->debug) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf)", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
647
+ }
648
+
649
+ unsigned long long last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
650
+ unsigned long long now_ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
651
+ unsigned long long next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
652
+
653
+ if(st->debug) debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
654
+ if(st->debug) debug(D_RRD_STATS, "%s: now ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
655
+ if(st->debug) debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
656
+
657
+ st->counter_done++;
658
+
659
+ // calculate totals and count the dimensions
660
+ int dimensions;
661
+ st->collected_total = 0;
662
+ for( rd = st->dimensions, dimensions = 0 ; rd ; rd = rd->next, dimensions++ )
663
+ st->collected_total += rd->collected_value;
664
+
665
+ // process all dimensions to calculate their values
666
+ // based on the collected figures only
667
+ // at this stage we do not interpolate anything
668
+ for( rd = st->dimensions ; rd ; rd = rd->next ) {
669
+ if(st->debug) debug(D_RRD_STATS, "%s/%s: "
670
+ " last_collected_value = " COLLECTED_NUMBER_FORMAT
671
+ " collected_value = " COLLECTED_NUMBER_FORMAT
672
+ " last_calculated_value = " CALCULATED_NUMBER_FORMAT
673
+ " calculated_value = " CALCULATED_NUMBER_FORMAT
674
+ , st->id, rd->name
675
+ , rd->last_collected_value
676
+ , rd->collected_value
677
+ , rd->last_calculated_value
678
+ , rd->calculated_value
679
+ );
680
+
681
+ switch(rd->algorithm) {
682
+ case RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL:
683
+ // the percentage of the current increment
684
+ // over the increment of all dimensions together
685
+ if(st->collected_total == st->last_collected_total) rd->calculated_value = 0;
686
+ else rd->calculated_value =
687
+ (calculated_number)100
688
+ * (calculated_number)(rd->collected_value - rd->last_collected_value)
689
+ / (calculated_number)(st->collected_total - st->last_collected_total);
690
+
691
+ if(st->debug)
692
+ debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
693
+ CALCULATED_NUMBER_FORMAT " = 100"
694
+ " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
695
+ " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
696
+ , st->id, rd->name
697
+ , rd->calculated_value
698
+ , rd->collected_value, rd->last_collected_value
699
+ , st->collected_total, st->last_collected_total
700
+ );
701
+ break;
702
+
703
+ case RRD_DIMENSION_PCENT_OVER_ROW_TOTAL:
704
+ if(!st->collected_total) rd->calculated_value = 0;
705
+ else
706
+ // the percentage of the current value
707
+ // over the total of all dimensions
708
+ rd->calculated_value =
709
+ (calculated_number)100
710
+ * (calculated_number)rd->collected_value
711
+ / (calculated_number)st->collected_total;
712
+
713
+ if(st->debug)
714
+ debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
715
+ CALCULATED_NUMBER_FORMAT " = 100"
716
+ " * " COLLECTED_NUMBER_FORMAT
717
+ " / " COLLECTED_NUMBER_FORMAT
718
+ , st->id, rd->name
719
+ , rd->calculated_value
720
+ , rd->collected_value
721
+ , st->collected_total
722
+ );
723
+ break;
724
+
725
+ case RRD_DIMENSION_INCREMENTAL:
726
+ // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
727
+ // to reset the calculation (it will give zero as the calculation for this second)
728
+ if(rd->last_collected_value > rd->collected_value) rd->last_collected_value = rd->collected_value;
729
+
730
+ rd->calculated_value += (calculated_number)(rd->collected_value - rd->last_collected_value);
731
+
732
+ if(st->debug)
733
+ debug(D_RRD_STATS, "%s/%s: CALC INC "
734
+ CALCULATED_NUMBER_FORMAT " += "
735
+ COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
736
+ , st->id, rd->name
737
+ , rd->calculated_value
738
+ , rd->collected_value, rd->last_collected_value
739
+ );
740
+ break;
741
+
742
+ case RRD_DIMENSION_ABSOLUTE:
743
+ rd->calculated_value = (calculated_number)rd->collected_value;
744
+
745
+ if(st->debug)
746
+ debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
747
+ CALCULATED_NUMBER_FORMAT " = "
748
+ COLLECTED_NUMBER_FORMAT
749
+ , st->id, rd->name
750
+ , rd->calculated_value
751
+ , rd->collected_value
752
+ );
753
+ break;
754
+
755
+ default:
756
+ // make the default zero, to make sure
757
+ // it gets noticed when we add new types
758
+ rd->calculated_value = 0;
759
+
760
+ if(st->debug)
761
+ debug(D_RRD_STATS, "%s/%s: CALC "
762
+ CALCULATED_NUMBER_FORMAT " = 0"
763
+ , st->id, rd->name
764
+ , rd->calculated_value
765
+ );
766
+ break;
767
+ }
768
+ }
769
+ // at this point we have all the calculated values ready
770
+
771
+ if(st->counter_done == 1 || next_ut > now_ut) {
772
+ // we don't have any usable data yet
773
+ if(st->debug) debug(D_RRD_STATS, "%s: Skipping collected values (usec since last update = %llu, counter_done = %lu)", st->name, st->usec_since_last_update, st->counter_done);
774
+
775
+ for( rd = st->dimensions; rd ; rd = rd->next ) {
776
+ rd->last_calculated_value = rd->calculated_value;
777
+ rd->last_collected_value = rd->collected_value;
778
+
779
+ switch(rd->algorithm) {
780
+ case RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL:
781
+ case RRD_DIMENSION_INCREMENTAL:
782
+ if(!st->usec_since_last_update) rd->calculated_value = 0;
783
+ // keep the previous values
784
+ // the next time, a new incremental total will be calculated
785
+ break;
786
+ }
787
+
788
+ rd->collected_value = 0;
789
+ }
790
+ st->last_collected_total = st->collected_total;
791
+
792
+ pthread_rwlock_unlock(&st->rwlock);
793
+ if(pthread_setcancelstate(oldstate, NULL) != 0)
794
+ error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
795
+
796
+ return(st->usec_since_last_update);
797
+ }
798
+
799
+ // it is now time to interpolate values on a second boundary
800
+ unsigned long long first_ut = last_ut;
801
+ for( ; next_ut <= now_ut ; next_ut += st->update_every * 1000000ULL ) {
802
+ if(st->debug) debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
803
+ if(st->debug) debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
804
+
805
+ st->last_updated.tv_sec = next_ut / 1000000ULL;
806
+ st->last_updated.tv_usec = 0;
807
+
808
+ for( rd = st->dimensions ; rd ; rd = rd->next ) {
809
+ calculated_number new_value;
810
+
811
+ switch(rd->algorithm) {
812
+ case RRD_DIMENSION_INCREMENTAL:
813
+ new_value = (calculated_number)
814
+ ( rd->calculated_value
815
+ * (calculated_number)(next_ut - last_ut)
816
+ / (calculated_number)(now_ut - last_ut)
817
+ );
818
+
819
+ if(st->debug)
820
+ debug(D_RRD_STATS, "%s/%s: CALC2 INC "
821
+ CALCULATED_NUMBER_FORMAT " = "
822
+ CALCULATED_NUMBER_FORMAT
823
+ " * %llu"
824
+ " / %llu"
825
+ , st->id, rd->name
826
+ , new_value
827
+ , rd->calculated_value
828
+ , (unsigned long long)(next_ut - last_ut)
829
+ , (unsigned long long)(now_ut - last_ut)
830
+ );
831
+
832
+ rd->calculated_value -= new_value;
833
+ break;
834
+
835
+ case RRD_DIMENSION_ABSOLUTE:
836
+ case RRD_DIMENSION_PCENT_OVER_ROW_TOTAL:
837
+ case RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL:
838
+ default:
839
+ new_value = (calculated_number)
840
+ ( ( (rd->calculated_value - rd->last_calculated_value)
841
+ * (calculated_number)(next_ut - first_ut)
842
+ / (calculated_number)(now_ut - first_ut)
843
+ )
844
+ + rd->last_calculated_value
845
+ );
846
+
847
+ if(st->debug)
848
+ debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
849
+ CALCULATED_NUMBER_FORMAT " = ((("
850
+ "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
851
+ " * %llu"
852
+ " / %llu) + " CALCULATED_NUMBER_FORMAT
853
+ , st->id, rd->name
854
+ , new_value
855
+ , rd->calculated_value, rd->last_calculated_value
856
+ , (next_ut - first_ut)
857
+ , (now_ut - first_ut), rd->last_calculated_value
858
+ );
859
+
860
+ if(next_ut + st->update_every * 1000000ULL > now_ut) rd->calculated_value = new_value;
861
+ break;
862
+
863
+ }
864
+
865
+
866
+ rd->values[st->current_entry] = pack_storage_number(
867
+ new_value
868
+ * (calculated_number)rd->multiplier
869
+ / (calculated_number)rd->divisor
870
+ );
871
+
872
+ if(st->debug)
873
+ debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
874
+ CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
875
+ " * %ld"
876
+ " / %ld"
877
+ , st->id, rd->name
878
+ , st->current_entry
879
+ , unpack_storage_number(rd->values[st->current_entry]), new_value
880
+ , rd->multiplier
881
+ , rd->divisor
882
+ );
883
+ }
884
+
885
+ if(st->first_entry_t && st->counter >= (unsigned long long)st->entries) {
886
+ // the db is overwriting values
887
+ // add the value we will overwrite
888
+ st->first_entry_t += st->update_every * 1000000ULL;
889
+ }
890
+
891
+ st->counter++;
892
+ st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
893
+ if(!st->first_entry_t) st->first_entry_t = next_ut;
894
+ last_ut = next_ut;
895
+ }
896
+
897
+ for( rd = st->dimensions; rd ; rd = rd->next ) {
898
+ rd->last_collected_value = rd->collected_value;
899
+ rd->last_calculated_value = rd->calculated_value;
900
+ rd->collected_value = 0;
901
+ }
902
+ st->last_collected_total = st->collected_total;
903
+
904
+ // ALL DONE ABOUT THE DATA UPDATE
905
+ // --------------------------------------------------------------------
906
+
907
+
908
+ // find if there are any obsolete dimensions (not updated recently)
909
+ for( rd = st->dimensions; rd ; rd = rd->next )
910
+ if((rd->last_collected_time.tv_sec + (10 * st->update_every)) < st->last_collected_time.tv_sec)
911
+ break;
912
+
913
+ if(rd) {
914
+ // there is dimension to free
915
+ // upgrade our read lock to a write lock
916
+ pthread_rwlock_unlock(&st->rwlock);
917
+ pthread_rwlock_wrlock(&st->rwlock);
918
+
919
+ for( rd = st->dimensions, last = NULL ; rd ; ) {
920
+ if((rd->last_collected_time.tv_sec + (10 * st->update_every)) < st->last_collected_time.tv_sec) { // remove it only it is not updated in 10 seconds
921
+ debug(D_RRD_STATS, "Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
922
+
923
+ if(!last) {
924
+ st->dimensions = rd->next;
925
+ rd->next = NULL;
926
+ rrd_stats_dimension_free(rd);
927
+ rd = st->dimensions;
928
+ continue;
929
+ }
930
+ else {
931
+ last->next = rd->next;
932
+ rd->next = NULL;
933
+ rrd_stats_dimension_free(rd);
934
+ rd = last->next;
935
+ continue;
936
+ }
937
+ }
938
+
939
+ last = rd;
940
+ rd = rd->next;
941
+ }
942
+
943
+ if(!st->dimensions) st->enabled = 0;
944
+ }
945
+
946
+ pthread_rwlock_unlock(&st->rwlock);
947
+
948
+ if(pthread_setcancelstate(oldstate, NULL) != 0)
949
+ error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
950
+
951
+ return(st->usec_since_last_update);
952
+}
953
+
954
+
955
+// find the oldest entry in the data, skipping all empty slots
956
+time_t rrd_stats_first_entry_t(RRD_STATS *st)
957
+{
958
+ if(!st->first_entry_t) return st->last_updated.tv_sec;
959
+
960
+ return st->first_entry_t / 1000000;
961
+}
src/rrd.h
new
+204
@@ -0,0 +1,204 @@
1
+#include <sys/time.h>
2
+#include <pthread.h>
3
+
4
+#include "storage_number.h"
5
+
6
+#ifndef NETDATA_RRD_H
7
+#define NETDATA_RRD_H 1
8
+
9
+#define UPDATE_EVERY 1
10
+#define UPDATE_EVERY_MAX 3600
11
+extern int update_every;
12
+
13
+#define HISTORY 3600
14
+#define HISTORY_MAX (86400*10)
15
+extern int save_history;
16
+
17
+typedef long long total_number;
18
+
19
+#define TOTAL_NUMBER_FORMAT "%lld"
20
+
21
+#define RRD_STATS_NAME_MAX 1024
22
+
23
+#define RRD_STATS_MAGIC "NETDATA CACHE STATS FILE V009"
24
+#define RRD_DIMENSION_MAGIC "NETDATA CACHE DIMENSION FILE V009"
25
+
26
+
27
+// ----------------------------------------------------------------------------
28
+// chart types
29
+
30
+#define CHART_TYPE_LINE_NAME "line"
31
+#define CHART_TYPE_AREA_NAME "area"
32
+#define CHART_TYPE_STACKED_NAME "stacked"
33
+
34
+#define CHART_TYPE_LINE 0
35
+#define CHART_TYPE_AREA 1
36
+#define CHART_TYPE_STACKED 2
37
+
38
+int chart_type_id(const char *name);
39
+const char *chart_type_name(int chart_type);
40
+
41
+
42
+// ----------------------------------------------------------------------------
43
+// memory mode
44
+
45
+#define NETDATA_MEMORY_MODE_RAM_NAME "ram"
46
+#define NETDATA_MEMORY_MODE_MAP_NAME "map"
47
+#define NETDATA_MEMORY_MODE_SAVE_NAME "save"
48
+
49
+#define NETDATA_MEMORY_MODE_RAM 0
50
+#define NETDATA_MEMORY_MODE_MAP 1
51
+#define NETDATA_MEMORY_MODE_SAVE 2
52
+
53
+extern int memory_mode;
54
+
55
+extern const char *memory_mode_name(int id);
56
+extern int memory_mode_id(const char *name);
57
+
58
+
59
+// ----------------------------------------------------------------------------
60
+// algorithms types
61
+
62
+#define RRD_DIMENSION_ABSOLUTE_NAME "absolute"
63
+#define RRD_DIMENSION_INCREMENTAL_NAME "incremental"
64
+#define RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL_NAME "percentage-of-incremental-row"
65
+#define RRD_DIMENSION_PCENT_OVER_ROW_TOTAL_NAME "percentage-of-absolute-row"
66
+
67
+#define RRD_DIMENSION_ABSOLUTE 0
68
+#define RRD_DIMENSION_INCREMENTAL 1
69
+#define RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL 2
70
+#define RRD_DIMENSION_PCENT_OVER_ROW_TOTAL 3
71
+
72
+extern int algorithm_id(const char *name);
73
+extern const char *algorithm_name(int chart_type);
74
+
75
+struct rrd_dimension {
76
+ char magic[sizeof(RRD_DIMENSION_MAGIC) + 1]; // our magic
77
+ char id[RRD_STATS_NAME_MAX + 1]; // the id of this dimension (for internal identification)
78
+ char *name; // the name of this dimension (as presented to user)
79
+ char cache_file[FILENAME_MAX+1];
80
+
81
+ unsigned long hash; // a simple hash on the id, to speed up searching
82
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
83
+
84
+ long entries; // how many entries this dimension has
85
+ // this should be the same to the entries of the data set
86
+
87
+ long current_entry; // the entry that is currently being updated
88
+ int update_every; // every how many seconds is this updated?
89
+
90
+ int hidden; // if set to non zero, this dimension will not be sent to the client
91
+ int mapped; // 1 if the file is mapped
92
+ unsigned long memsize; // the memory allocated for this dimension
93
+
94
+ int algorithm;
95
+ long multiplier;
96
+ long divisor;
97
+
98
+ struct timeval last_collected_time; // when was this dimension last updated
99
+ // this is only used to detect un-updated dimensions
100
+ // which are removed after some time
101
+
102
+ calculated_number calculated_value;
103
+ calculated_number last_calculated_value;
104
+
105
+ collected_number collected_value; // the value collected at this round
106
+ collected_number last_collected_value; // the value that was collected at the last round
107
+
108
+ struct rrd_dimension *next; // linking of dimensions within the same data set
109
+
110
+ storage_number values[]; // the array of values - THIS HAS TO BE THE LAST MEMBER
111
+};
112
+typedef struct rrd_dimension RRD_DIMENSION;
113
+
114
+struct rrd_stats {
115
+ char magic[sizeof(RRD_STATS_MAGIC) + 1]; // our magic
116
+
117
+ char id[RRD_STATS_NAME_MAX + 1]; // id of the data set
118
+ char *name; // name of the data set
119
+ char *cache_dir; // the directory to store dimension maps
120
+ char cache_file[FILENAME_MAX+1];
121
+
122
+ char *type; // the type of graph RRD_TYPE_* (a category, for determining graphing options)
123
+ char *family; // the family of this data set (for grouping them together)
124
+ char *title; // title shown to user
125
+ char *units; // units of measurement
126
+
127
+ pthread_rwlock_t rwlock;
128
+ unsigned long counter; // the number of times we added values to this rrd
129
+ unsigned long counter_done; // the number of times we added values to this rrd
130
+
131
+ int mapped; // if set to 1, this is memory mapped
132
+ unsigned long memsize; // how much mem we have allocated for this (without dimensions)
133
+
134
+ unsigned long hash_name; // a simple hash on the name
135
+ unsigned long hash; // a simple hash on the id, to speed up searching
136
+ // we first compare hashes, and only if the hashes are equal we do string comparisons
137
+
138
+ long priority;
139
+
140
+ long entries; // total number of entries in the data set
141
+ long current_entry; // the entry that is currently being updated
142
+ // it goes around in a round-robin fashion
143
+
144
+ int update_every; // every how many seconds is this updated?
145
+ unsigned long long first_entry_t; // the timestamp (in microseconds) of the oldest entry in the db
146
+ struct timeval last_updated; // when this data set was last updated (updated every time the rrd_stats_done() function)
147
+ struct timeval last_collected_time; //
148
+ unsigned long long usec_since_last_update;
149
+
150
+ total_number collected_total;
151
+ total_number last_collected_total;
152
+
153
+ int chart_type;
154
+ int debug;
155
+ int enabled;
156
+ int isdetail; // if set, the data set should be considered as a detail of another
157
+ // (the master data set should be the one that has the same family and is not detail)
158
+
159
+ RRD_DIMENSION *dimensions; // the actual data for every dimension
160
+
161
+ struct rrd_stats *next; // linking of rrd stats
162
+};
163
+typedef struct rrd_stats RRD_STATS;
164
+
165
+extern RRD_STATS *root;
166
+extern pthread_rwlock_t root_rwlock;
167
+
168
+extern char *rrd_stats_strncpy_name(char *to, const char *from, int length);
169
+extern void rrd_stats_set_name(RRD_STATS *st, const char *name);
170
+
171
+extern char *rrd_stats_cache_dir(const char *id);
172
+
173
+extern void rrd_stats_reset(RRD_STATS *st);
174
+
175
+extern RRD_STATS *rrd_stats_create(const char *type, const char *id, const char *name, const char *family, const char *title, const char *units, long priority, int update_every, int chart_type);
176
+
177
+extern RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *id, const char *name, long multiplier, long divisor, int algorithm);
178
+
179
+extern void rrd_stats_dimension_set_name(RRD_STATS *st, RRD_DIMENSION *rd, const char *name);
180
+extern void rrd_stats_dimension_free(RRD_DIMENSION *rd);
181
+
182
+extern void rrd_stats_free_all(void);
183
+extern void rrd_stats_save_all(void);
184
+
185
+extern RRD_STATS *rrd_stats_find(const char *id);
186
+extern RRD_STATS *rrd_stats_find_bytype(const char *type, const char *id);
187
+extern RRD_STATS *rrd_stats_find_byname(const char *name);
188
+
189
+extern RRD_DIMENSION *rrd_stats_dimension_find(RRD_STATS *st, const char *id);
190
+
191
+extern int rrd_stats_dimension_hide(RRD_STATS *st, const char *id);
192
+
193
+extern void rrd_stats_dimension_set_by_pointer(RRD_STATS *st, RRD_DIMENSION *rd, collected_number value);
194
+extern int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value);
195
+
196
+extern void rrd_stats_next_usec(RRD_STATS *st, unsigned long long microseconds);
197
+extern void rrd_stats_next(RRD_STATS *st);
198
+extern void rrd_stats_next_plugins(RRD_STATS *st);
199
+
200
+extern unsigned long long rrd_stats_done(RRD_STATS *st);
201
+
202
+extern time_t rrd_stats_first_entry_t(RRD_STATS *st);
203
+
204
+#endif /* NETDATA_RRD_H */
src/rrd2json.c
new
+464
@@ -0,0 +1,464 @@
1
+#include <pthread.h>
2
+#include <sys/time.h>
3
+
4
+#include "log.h"
5
+#include "common.h"
6
+#include "rrd2json.h"
7
+
8
+#define HOSTNAME_MAX 1024
9
+char *hostname = "unknown";
10
+
11
+unsigned long rrd_stats_one_json(RRD_STATS *st, char *options, struct web_buffer *wb)
12
+{
13
+ time_t now = time(NULL);
14
+ web_buffer_increase(wb, 16384);
15
+
16
+ pthread_rwlock_rdlock(&st->rwlock);
17
+
18
+ web_buffer_printf(wb,
19
+ "\t\t{\n"
20
+ "\t\t\t\"id\": \"%s\",\n"
21
+ "\t\t\t\"name\": \"%s\",\n"
22
+ "\t\t\t\"type\": \"%s\",\n"
23
+ "\t\t\t\"family\": \"%s\",\n"
24
+ "\t\t\t\"title\": \"%s\",\n"
25
+ "\t\t\t\"priority\": %ld,\n"
26
+ "\t\t\t\"enabled\": %d,\n"
27
+ "\t\t\t\"units\": \"%s\",\n"
28
+ "\t\t\t\"url\": \"/data/%s/%s\",\n"
29
+ "\t\t\t\"chart_type\": \"%s\",\n"
30
+ "\t\t\t\"counter\": %ld,\n"
31
+ "\t\t\t\"entries\": %ld,\n"
32
+ "\t\t\t\"first_entry_t\": %lu,\n"
33
+ "\t\t\t\"last_entry\": %ld,\n"
34
+ "\t\t\t\"last_entry_t\": %lu,\n"
35
+ "\t\t\t\"last_entry_secs_ago\": %lu,\n"
36
+ "\t\t\t\"update_every\": %d,\n"
37
+ "\t\t\t\"isdetail\": %d,\n"
38
+ "\t\t\t\"usec_since_last_update\": %llu,\n"
39
+ "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
40
+ "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
41
+ "\t\t\t\"dimensions\": [\n"
42
+ , st->id
43
+ , st->name
44
+ , st->type
45
+ , st->family
46
+ , st->title
47
+ , st->priority
48
+ , st->enabled
49
+ , st->units
50
+ , st->name, options?options:""
51
+ , chart_type_name(st->chart_type)
52
+ , st->counter
53
+ , st->entries
54
+ , rrd_stats_first_entry_t(st)
55
+ , st->current_entry
56
+ , st->last_updated.tv_sec
57
+ , now - (st->last_updated.tv_sec > now) ? now : st->last_updated.tv_sec
58
+ , st->update_every
59
+ , st->isdetail
60
+ , st->usec_since_last_update
61
+ , st->collected_total
62
+ , st->last_collected_total
63
+ );
64
+
65
+ unsigned long memory = st->memsize;
66
+
67
+ RRD_DIMENSION *rd;
68
+ for(rd = st->dimensions; rd ; rd = rd->next) {
69
+ memory += rd->memsize;
70
+
71
+ web_buffer_printf(wb,
72
+ "\t\t\t\t{\n"
73
+ "\t\t\t\t\t\"id\": \"%s\",\n"
74
+ "\t\t\t\t\t\"name\": \"%s\",\n"
75
+ "\t\t\t\t\t\"entries\": %ld,\n"
76
+ "\t\t\t\t\t\"isHidden\": %d,\n"
77
+ "\t\t\t\t\t\"algorithm\": \"%s\",\n"
78
+ "\t\t\t\t\t\"multiplier\": %ld,\n"
79
+ "\t\t\t\t\t\"divisor\": %ld,\n"
80
+ "\t\t\t\t\t\"last_entry_t\": %lu,\n"
81
+ "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
82
+ "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
83
+ "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
84
+ "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
85
+ "\t\t\t\t\t\"memory\": %lu\n"
86
+ "\t\t\t\t}%s\n"
87
+ , rd->id
88
+ , rd->name
89
+ , rd->entries
90
+ , rd->hidden
91
+ , algorithm_name(rd->algorithm)
92
+ , rd->multiplier
93
+ , rd->divisor
94
+ , rd->last_collected_time.tv_sec
95
+ , rd->collected_value
96
+ , rd->calculated_value
97
+ , rd->last_collected_value
98
+ , rd->last_calculated_value
99
+ , rd->memsize
100
+ , rd->next?",":""
101
+ );
102
+ }
103
+
104
+ web_buffer_printf(wb,
105
+ "\t\t\t],\n"
106
+ "\t\t\t\"memory\" : %lu\n"
107
+ "\t\t}"
108
+ , memory
109
+ );
110
+
111
+ pthread_rwlock_unlock(&st->rwlock);
112
+ return memory;
113
+}
114
+
115
+#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
116
+#define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n"
117
+
118
+void rrd_stats_graph_json(RRD_STATS *st, char *options, struct web_buffer *wb)
119
+{
120
+ web_buffer_increase(wb, 16384);
121
+
122
+ web_buffer_printf(wb, RRD_GRAPH_JSON_HEADER);
123
+ rrd_stats_one_json(st, options, wb);
124
+ web_buffer_printf(wb, RRD_GRAPH_JSON_FOOTER);
125
+}
126
+
127
+void rrd_stats_all_json(struct web_buffer *wb)
128
+{
129
+ web_buffer_increase(wb, 1024);
130
+
131
+ unsigned long memory = 0;
132
+ long c;
133
+ RRD_STATS *st;
134
+
135
+ web_buffer_printf(wb, RRD_GRAPH_JSON_HEADER);
136
+
137
+ pthread_rwlock_rdlock(&root_rwlock);
138
+ for(st = root, c = 0; st ; st = st->next) {
139
+ if(st->enabled) {
140
+ if(c) web_buffer_printf(wb, "%s", ",\n");
141
+ memory += rrd_stats_one_json(st, NULL, wb);
142
+ c++;
143
+ }
144
+ }
145
+ pthread_rwlock_unlock(&root_rwlock);
146
+
147
+ web_buffer_printf(wb, "\n\t],\n"
148
+ "\t\"hostname\": \"%s\",\n"
149
+ "\t\"update_every\": %d,\n"
150
+ "\t\"history\": %d,\n"
151
+ "\t\"memory\": %lu\n"
152
+ "}\n"
153
+ , hostname
154
+ , update_every
155
+ , save_history
156
+ , memory
157
+ );
158
+}
159
+
160
+unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero)
161
+{
162
+ int c;
163
+ pthread_rwlock_rdlock(&st->rwlock);
164
+
165
+
166
+ // -------------------------------------------------------------------------
167
+ // switch from JSON to google JSON
168
+
169
+ char kq[2] = "\"";
170
+ char sq[2] = "\"";
171
+ switch(type) {
172
+ case DATASOURCE_GOOGLE_JSON:
173
+ case DATASOURCE_GOOGLE_JSONP:
174
+ kq[0] = '\0';
175
+ sq[0] = '\'';
176
+ break;
177
+
178
+ case DATASOURCE_JSON:
179
+ default:
180
+ break;
181
+ }
182
+
183
+
184
+ // -------------------------------------------------------------------------
185
+ // validate the parameters
186
+
187
+ if(entries_to_show < 1) entries_to_show = 1;
188
+ if(group < 1) group = 1;
189
+
190
+ // make sure current_entry is within limits
191
+ long current_entry = (long)st->current_entry - (long)1;
192
+ if(current_entry < 0) current_entry = 0;
193
+ else if(current_entry >= st->entries) current_entry = st->entries - 1;
194
+
195
+ // find the oldest entry of the round-robin
196
+ long max_entries_init = (st->counter < (unsigned long)st->entries) ? st->counter : (unsigned long)st->entries;
197
+
198
+ time_t time_init = st->last_updated.tv_sec;
199
+
200
+ if(before == 0 || before > time_init) before = time_init;
201
+ if(after == 0) after = rrd_stats_first_entry_t(st);
202
+
203
+
204
+ // ---
205
+
206
+ // our return value (the last timestamp printed)
207
+ // this is required to detect re-transmit in google JSONP
208
+ time_t last_timestamp = 0;
209
+
210
+
211
+ // -------------------------------------------------------------------------
212
+ // find how many dimensions we have
213
+
214
+ int dimensions = 0;
215
+ RRD_DIMENSION *rd;
216
+ for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
217
+ if(!dimensions) {
218
+ pthread_rwlock_unlock(&st->rwlock);
219
+ web_buffer_printf(wb, "No dimensions yet.");
220
+ return 0;
221
+ }
222
+
223
+
224
+ // -------------------------------------------------------------------------
225
+ // prepare various strings, to speed up the loop
226
+
227
+ char overflow_annotation[201]; snprintf(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
228
+ char normal_annotation[201]; snprintf(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
229
+ char pre_date[51]; snprintf(pre_date, 50, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
230
+ char post_date[21]; snprintf(post_date, 20, "%s}", sq);
231
+ char pre_value[21]; snprintf(pre_value, 20, ",{%sv%s:", kq, kq);
232
+ char post_value[21]; snprintf(post_value, 20, "}");
233
+
234
+
235
+ // -------------------------------------------------------------------------
236
+ // checks for debuging
237
+
238
+ if(st->debug) {
239
+ debug(D_RRD_STATS, "%s first_entry_t = %lu, last_entry_t = %lu, duration = %lu, after = %lu, before = %lu, duration = %lu, entries_to_show = %lu, group = %lu, max_entries = %ld"
240
+ , st->id
241
+ , rrd_stats_first_entry_t(st)
242
+ , st->last_updated.tv_sec
243
+ , st->last_updated.tv_sec - rrd_stats_first_entry_t(st)
244
+ , after
245
+ , before
246
+ , before - after
247
+ , entries_to_show
248
+ , group
249
+ , max_entries_init
250
+ );
251
+
252
+ if(before < after)
253
+ debug(D_RRD_STATS, "WARNING: %s The newest value in the database (%lu) is earlier than the oldest (%lu)", st->name, before, after);
254
+
255
+ if((before - after) > st->entries * st->update_every)
256
+ debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%lu) is higher than the capacity of the database (%lu)", st->name, before - after, st->entries * st->update_every);
257
+ }
258
+
259
+
260
+ // -------------------------------------------------------------------------
261
+ // temp arrays for keeping values per dimension
262
+
263
+ calculated_number group_values[dimensions]; // keep sums when grouping
264
+ calculated_number print_values[dimensions]; // keep the final value to be printed
265
+ int print_hidden[dimensions]; // keep hidden flags
266
+ int found_non_zero[dimensions];
267
+
268
+ // initialize them
269
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
270
+ group_values[c] = print_values[c] = 0;
271
+ print_hidden[c] = rd->hidden;
272
+ found_non_zero[c] = 0;
273
+ }
274
+
275
+ // -------------------------------------------------------------------------
276
+ // remove dimensions that contain only zeros
277
+
278
+ int max_loop = 1;
279
+ if(only_non_zero) max_loop = 2;
280
+
281
+ for(; max_loop ; max_loop--) {
282
+
283
+ // -------------------------------------------------------------------------
284
+ // print the JSON header
285
+
286
+ web_buffer_printf(wb, "{\n %scols%s:\n [\n", kq, kq);
287
+ web_buffer_printf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
288
+ web_buffer_printf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
289
+ web_buffer_printf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
290
+
291
+ // print the header for each dimension
292
+ // and update the print_hidden array for the dimensions that should be hidden
293
+ int pc = 0;
294
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
295
+ if(!print_hidden[c]) {
296
+ pc++;
297
+ web_buffer_printf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
298
+ }
299
+ }
300
+ if(!pc) {
301
+ web_buffer_printf(wb, ",\n {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
302
+ }
303
+
304
+ // print the begin of row data
305
+ web_buffer_printf(wb, "\n ],\n %srows%s:\n [\n", kq, kq);
306
+
307
+
308
+ // -------------------------------------------------------------------------
309
+ // the main loop
310
+
311
+ int annotate_reset = 0;
312
+ int annotation_count = 0;
313
+
314
+ // to allow grouping on the same values, we need a pad
315
+ long pad = before % group;
316
+
317
+ // the minimum line length we expect
318
+ int line_size = 4096 + (dimensions * 200);
319
+
320
+ time_t now = time_init;
321
+ long max_entries = max_entries_init;
322
+
323
+ long t;
324
+
325
+ long count = 0, printed = 0, group_count = 0;
326
+ last_timestamp = 0;
327
+ for(t = current_entry; max_entries ; now -= st->update_every, t--, max_entries--) {
328
+ if(t < 0) t = st->entries - 1;
329
+
330
+ int print_this = 0;
331
+
332
+ if(st->debug) {
333
+ debug(D_RRD_STATS, "%s t = %ld, count = %ld, group_count = %ld, printed = %ld, now = %lu, %s %s"
334
+ , st->id
335
+ , t
336
+ , count + 1
337
+ , group_count + 1
338
+ , printed
339
+ , now
340
+ , (((count + 1 - pad) % group) == 0)?"PRINT":" - "
341
+ , (now >= after && now <= before)?"RANGE":" - "
342
+ );
343
+ }
344
+
345
+ // make sure we return data in the proper time range
346
+ if(now < after || now > before) continue;
347
+
348
+ count++;
349
+ group_count++;
350
+
351
+ if(((count - pad) % group) == 0) {
352
+ if(printed >= entries_to_show) {
353
+ // debug(D_RRD_STATS, "Already printed all rows. Stopping.");
354
+ break;
355
+ }
356
+
357
+ if(group_count != group) {
358
+ // this is an incomplete group, skip it.
359
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
360
+ group_values[c] = 0;
361
+
362
+ group_count = 0;
363
+ continue;
364
+ }
365
+
366
+ // check if we may exceed the buffer provided
367
+ web_buffer_increase(wb, line_size);
368
+
369
+ // generate the local date time
370
+ struct tm *tm = localtime(&now);
371
+ if(!tm) { error("localtime() failed."); continue; }
372
+ if(now > last_timestamp) last_timestamp = now;
373
+
374
+ if(printed) web_buffer_strcpy(wb, "]},\n");
375
+ web_buffer_strcpy(wb, pre_date);
376
+ web_buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
377
+ web_buffer_strcpy(wb, post_date);
378
+
379
+ print_this = 1;
380
+ }
381
+
382
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
383
+ calculated_number value = unpack_storage_number(rd->values[t]);
384
+
385
+ switch(group_method) {
386
+ case GROUP_MAX:
387
+ if(abs(value) > abs(group_values[c])) group_values[c] = value;
388
+ break;
389
+
390
+ case GROUP_SUM:
391
+ group_values[c] += value;
392
+ break;
393
+
394
+ default:
395
+ case GROUP_AVERAGE:
396
+ group_values[c] += value;
397
+ if(print_this) group_values[c] /= group_count;
398
+ break;
399
+ }
400
+
401
+ if(print_this) {
402
+ print_values[c] = group_values[c];
403
+ group_values[c] = 0;
404
+ }
405
+ }
406
+
407
+ if(print_this) {
408
+ group_count = 0;
409
+
410
+ if(annotate_reset) {
411
+ annotation_count++;
412
+ web_buffer_strcpy(wb, overflow_annotation);
413
+ annotate_reset = 0;
414
+ }
415
+ else
416
+ web_buffer_strcpy(wb, normal_annotation);
417
+
418
+ pc = 0;
419
+ for(c = 0 ; c < dimensions ; c++) {
420
+ if(!print_hidden[c]) {
421
+ pc++;
422
+ web_buffer_strcpy(wb, pre_value);
423
+ web_buffer_rrd_value(wb, print_values[c]);
424
+ web_buffer_strcpy(wb, post_value);
425
+
426
+ if(print_values[c]) found_non_zero[c]++;
427
+ }
428
+ }
429
+ if(!pc) {
430
+ web_buffer_strcpy(wb, pre_value);
431
+ web_buffer_rrd_value(wb, (calculated_number)0);
432
+ web_buffer_strcpy(wb, post_value);
433
+ }
434
+
435
+ printed++;
436
+ }
437
+ }
438
+
439
+ if(printed) web_buffer_printf(wb, "]}");
440
+ web_buffer_printf(wb, "\n ]\n}\n");
441
+
442
+ if(only_non_zero && max_loop > 1) {
443
+ int changed = 0;
444
+ for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
445
+ group_values[c] = 0;
446
+
447
+ if(!print_hidden[c] && !found_non_zero[c]) {
448
+ changed = 1;
449
+ print_hidden[c] = 1;
450
+ }
451
+ }
452
+
453
+ if(changed) web_buffer_reset(wb);
454
+ else break;
455
+ }
456
+ else break;
457
+
458
+ } // max_loop
459
+
460
+ debug(D_RRD_STATS, "RRD_STATS_JSON: %s total %ld bytes", st->name, wb->bytes);
461
+
462
+ pthread_rwlock_unlock(&st->rwlock);
463
+ return last_timestamp;
464
+}
src/rrd2json.h
new
+29
@@ -0,0 +1,29 @@
1
+#include <time.h>
2
+
3
+#include "web_buffer.h"
4
+#include "rrd.h"
5
+
6
+#ifndef NETDATA_RRD2JSON_H
7
+#define NETDATA_RRD2JSON_H 1
8
+
9
+#define HOSTNAME_MAX 1024
10
+extern char *hostname;
11
+
12
+// type of JSON generations
13
+#define DATASOURCE_JSON 0
14
+#define DATASOURCE_GOOGLE_JSON 1
15
+#define DATASOURCE_GOOGLE_JSONP 2
16
+
17
+#define GROUP_AVERAGE 0
18
+#define GROUP_MAX 1
19
+#define GROUP_SUM 2
20
+
21
+extern unsigned long rrd_stats_one_json(RRD_STATS *st, char *options, struct web_buffer *wb);
22
+
23
+extern void rrd_stats_graph_json(RRD_STATS *st, char *options, struct web_buffer *wb);
24
+
25
+extern void rrd_stats_all_json(struct web_buffer *wb);
26
+
27
+extern unsigned long rrd_stats_json(int type, RRD_STATS *st, struct web_buffer *wb, int entries_to_show, int group, int group_method, time_t after, time_t before, int only_non_zero);
28
+
29
+#endif /* NETDATA_RRD2JSON_H */
src/storage_number.c
new
+85
@@ -0,0 +1,85 @@
1
+
2
+#include "log.h"
3
+#include "storage_number.h"
4
+
5
+storage_number pack_storage_number(calculated_number value)
6
+{
7
+ storage_number r = 0;
8
+ if(!value) return r;
9
+
10
+ // bit 32 = sign
11
+ // bit 31 = 0:divide, 1:multiply
12
+ // bit 30, 29, 28 = (multiplier or divider) 0-7
13
+ // bit 27 to 25 = reserved for flags
14
+ // bit 24 to bit 1 = the value
15
+
16
+ storage_number sign = 0, exp = 0, mul;
17
+ int m = 0;
18
+ calculated_number n = value;
19
+
20
+ if(n < 0) {
21
+ sign = 1;
22
+ n = -n;
23
+ }
24
+
25
+ while(m < 7 && n > (calculated_number)0x00ffffff) {
26
+ n /= 10;
27
+ m++;
28
+ }
29
+ while(m > -7 && n < (calculated_number)0x00199999) {
30
+ n *= 10;
31
+ m--;
32
+ }
33
+
34
+ if(m <= 0) {
35
+ exp = 0;
36
+ m = -m;
37
+ }
38
+ else exp = 1;
39
+
40
+ if(n > (calculated_number)0x00ffffff) {
41
+ error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
42
+ n = (calculated_number)0x00ffffff;
43
+ }
44
+
45
+ mul = m;
46
+
47
+ r = (sign << 31) + (exp << 30) + (mul << 27) + n;
48
+ // fprintf(stderr, "PACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", r, sign, exp, mul, n);
49
+
50
+ return r;
51
+}
52
+
53
+calculated_number unpack_storage_number(storage_number value)
54
+{
55
+ if(!value) return 0;
56
+
57
+ int sign = 0;
58
+ int exp = 0;
59
+
60
+ if(value & (1 << 31)) {
61
+ sign = 1;
62
+ value ^= 1 << 31;
63
+ }
64
+
65
+ if(value & (1 << 30)) {
66
+ exp = 1;
67
+ value ^= 1 << 30;
68
+ }
69
+
70
+ int mul = value >> 27;
71
+ value ^= mul << 27;
72
+
73
+ calculated_number n = value;
74
+
75
+ // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
76
+
77
+ while(mul > 0) {
78
+ if(exp) n *= 10;
79
+ else n /= 10;
80
+ mul--;
81
+ }
82
+
83
+ if(sign) n = -n;
84
+ return n;
85
+}
src/storage_number.h
new
+21
@@ -0,0 +1,21 @@
1
+#include <inttypes.h>
2
+
3
+#ifndef NETDATA_STORAGE_NUMBER_H
4
+#define NETDATA_STORAGE_NUMBER_H
5
+
6
+typedef long double calculated_number;
7
+#define CALCULATED_NUMBER_FORMAT "%0.7Lf"
8
+//typedef long long calculated_number;
9
+//#define CALCULATED_NUMBER_FORMAT "%lld"
10
+
11
+typedef long long collected_number;
12
+#define COLLECTED_NUMBER_FORMAT "%lld"
13
+
14
+typedef int32_t storage_number;
15
+typedef uint32_t ustorage_number;
16
+#define STORAGE_NUMBER_FORMAT "%d"
17
+
18
+storage_number pack_storage_number(calculated_number value);
19
+calculated_number unpack_storage_number(storage_number value);
20
+
21
+#endif /* NETDATA_STORAGE_NUMBER_H */
src/unit_test.c
new
+141
@@ -0,0 +1,141 @@
1
+#include <stdio.h>
2
+
3
+#include "storage_number.h"
4
+#include "rrd.h"
5
+#include "log.h"
6
+#include "web_buffer.h"
7
+
8
+int unit_test_storage()
9
+{
10
+ char buffer[100];
11
+ storage_number s;
12
+ calculated_number c, a = 0, d, f;
13
+ int i, j, g, r = 0, l;
14
+
15
+ for(g = -1; g <= 1 ; g++) {
16
+ a = 0;
17
+
18
+ if(!g) continue;
19
+
20
+ for(j = 0; j < 9 ;j++) {
21
+ a += 0.0000001;
22
+ c = a * g;
23
+ for(i = 0; i < 21 ;i++, c *= 10) {
24
+ s = pack_storage_number(c);
25
+ d = unpack_storage_number(s);
26
+
27
+ f = d / c;
28
+
29
+ l = print_calculated_number(buffer, d);
30
+
31
+ if(f < 0.99999 || f > 1.00001) {
32
+ fprintf(stderr, "\nERROR\n" CALCULATED_NUMBER_FORMAT " original\n" CALCULATED_NUMBER_FORMAT " unpacked, (stored as 0x%08X)\n%s printed as %d bytes\n", c, d, s, buffer, l);
33
+ r++;
34
+ }
35
+ else {
36
+ fprintf(stderr, "\nOK\n" CALCULATED_NUMBER_FORMAT " original\n" CALCULATED_NUMBER_FORMAT " unpacked, (stored as 0x%08X)\n%s printed as %d bytes\n", c, d, s, buffer, l);
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ return r;
43
+}
44
+
45
+int unit_test(long delay, long shift)
46
+{
47
+ static int repeat = 0;
48
+ repeat++;
49
+
50
+ char name[101];
51
+ snprintf(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
52
+
53
+ debug_flags = 0xffffffff;
54
+ memory_mode = NETDATA_MEMORY_MODE_RAM;
55
+ update_every = 1;
56
+
57
+ int do_abs = 1;
58
+ int do_inc = 1;
59
+ int do_abst = 1;
60
+ int do_absi = 1;
61
+
62
+ RRD_STATS *st = rrd_stats_create("netdata", name, name, "netdata", "Unit Testing", "a value", 1, 1, CHART_TYPE_LINE);
63
+ st->debug = 1;
64
+
65
+ RRD_DIMENSION *rdabs = NULL;
66
+ RRD_DIMENSION *rdinc = NULL;
67
+ RRD_DIMENSION *rdabst = NULL;
68
+ RRD_DIMENSION *rdabsi = NULL;
69
+
70
+ if(do_abs) rdabs = rrd_stats_dimension_add(st, "absolute", "absolute", 1, 1, RRD_DIMENSION_ABSOLUTE);
71
+ if(do_inc) rdinc = rrd_stats_dimension_add(st, "incremental", "incremental", 1, 1 * update_every, RRD_DIMENSION_INCREMENTAL);
72
+ if(do_abst) rdabst = rrd_stats_dimension_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRD_DIMENSION_PCENT_OVER_ROW_TOTAL);
73
+ if(do_absi) rdabsi = rrd_stats_dimension_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRD_DIMENSION_PCENT_OVER_DIFF_TOTAL);
74
+
75
+ long increment = 1000;
76
+ collected_number i = 0;
77
+
78
+ unsigned long c, dimensions = 0;
79
+ RRD_DIMENSION *rd;
80
+ for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
81
+
82
+ for(c = 0; c < 20 ;c++) {
83
+ i += increment;
84
+
85
+ fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
86
+ if(c) {
87
+ rrd_stats_next_usec(st, delay);
88
+ }
89
+ if(do_abs) rrd_stats_dimension_set(st, "absolute", i);
90
+ if(do_inc) rrd_stats_dimension_set(st, "incremental", i);
91
+ if(do_abst) rrd_stats_dimension_set(st, "percentage-of-absolute-row", i);
92
+ if(do_absi) rrd_stats_dimension_set(st, "percentage-of-incremental-row", i);
93
+
94
+ if(!c) {
95
+ gettimeofday(&st->last_collected_time, NULL);
96
+ st->last_collected_time.tv_usec = shift;
97
+ }
98
+
99
+ // prevent it from deleting the dimensions
100
+ for(rd = st->dimensions ; rd ; rd = rd->next) rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
101
+
102
+ rrd_stats_done(st);
103
+ }
104
+
105
+ unsigned long oincrement = increment;
106
+ increment = increment * st->update_every * 1000000 / delay;
107
+ fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %lu, DELAY %lu, SHIFT %lu\n", oincrement * 10, increment * 10, delay, shift);
108
+
109
+ int ret = 0;
110
+ storage_number v;
111
+ for(c = 0 ; c < st->counter ; c++) {
112
+ fprintf(stderr, "\nPOSITION: c = %lu, VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
113
+
114
+ for(rd = st->dimensions ; rd ; rd = rd->next) {
115
+ fprintf(stderr, "\t %s " STORAGE_NUMBER_FORMAT " -> ", rd->id, rd->values[c]);
116
+
117
+ if(rd == rdabs) v =
118
+ ( oincrement
119
+ + (increment * (1000000 - shift) / 1000000)
120
+ + c * increment
121
+ ) * 10;
122
+
123
+ else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000)) * 10;
124
+ else if(rd == rdabst) v = oincrement / dimensions;
125
+ else if(rd == rdabsi) v = oincrement / dimensions;
126
+ else v = 0;
127
+
128
+ if(v == rd->values[c]) fprintf(stderr, "passed.\n");
129
+ else {
130
+ fprintf(stderr, "ERROR! (expected " STORAGE_NUMBER_FORMAT ")\n", v);
131
+ ret = 1;
132
+ }
133
+ }
134
+ }
135
+
136
+ if(ret)
137
+ fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
138
+
139
+ return ret;
140
+}
141
+
src/unit_test.h
new
+7
@@ -0,0 +1,7 @@
1
+#ifndef NETDATA_UNIT_TEST_H
2
+#define NETDATA_UNIT_TEST_H 1
3
+
4
+extern int unit_test_storage(void);
5
+extern int unit_test(long delay, long shift);
6
+
7
+#endif /* NETDATA_UNIT_TEST_H */
src/url.c
new
+77
@@ -0,0 +1,77 @@
1
+#include <stdlib.h>
2
+#include <string.h>
3
+#include <ctype.h>
4
+
5
+#include "log.h"
6
+#include "url.h"
7
+
8
+// ----------------------------------------------------------------------------
9
+// URL encode / decode
10
+// code from: http://www.geekhideout.com/urlcode.shtml
11
+
12
+/* Converts a hex character to its integer value */
13
+char from_hex(char ch) {
14
+ return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
15
+}
16
+
17
+/* Converts an integer value to its hex character*/
18
+char to_hex(char code) {
19
+ static char hex[] = "0123456789abcdef";
20
+ return hex[code & 15];
21
+}
22
+
23
+/* Returns a url-encoded version of str */
24
+/* IMPORTANT: be sure to free() the returned string after use */
25
+char *url_encode(char *str) {
26
+ char *pstr = str,
27
+ *buf = malloc(strlen(str) * 3 + 1),
28
+ *pbuf = buf;
29
+
30
+ while (*pstr) {
31
+ if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
32
+ *pbuf++ = *pstr;
33
+
34
+ else if (*pstr == ' ')
35
+ *pbuf++ = '+';
36
+
37
+ else
38
+ *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
39
+
40
+ pstr++;
41
+ }
42
+
43
+ *pbuf = '\0';
44
+
45
+ return buf;
46
+}
47
+
48
+/* Returns a url-decoded version of str */
49
+/* IMPORTANT: be sure to free() the returned string after use */
50
+char *url_decode(char *str) {
51
+ char *pstr = str,
52
+ *buf = malloc(strlen(str) + 1),
53
+ *pbuf = buf;
54
+
55
+ if(!buf) fatal("Cannot allocate memory.");
56
+
57
+ while (*pstr) {
58
+ if (*pstr == '%') {
59
+ if (pstr[1] && pstr[2]) {
60
+ *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
61
+ pstr += 2;
62
+ }
63
+ }
64
+ else if (*pstr == '+')
65
+ *pbuf++ = ' ';
66
+
67
+ else
68
+ *pbuf++ = *pstr;
69
+
70
+ pstr++;
71
+ }
72
+
73
+ *pbuf = '\0';
74
+
75
+ return buf;
76
+}
77
+
src/url.h
new
+22
@@ -0,0 +1,22 @@
1
+#ifndef NETDATA_URL_H
2
+#define NETDATA_URL_H 1
3
+
4
+// ----------------------------------------------------------------------------
5
+// URL encode / decode
6
+// code from: http://www.geekhideout.com/urlcode.shtml
7
+
8
+/* Converts a hex character to its integer value */
9
+extern char from_hex(char ch);
10
+
11
+/* Converts an integer value to its hex character*/
12
+extern char to_hex(char code);
13
+
14
+/* Returns a url-encoded version of str */
15
+/* IMPORTANT: be sure to free() the returned string after use */
16
+extern char *url_encode(char *str);
17
+
18
+/* Returns a url-decoded version of str */
19
+/* IMPORTANT: be sure to free() the returned string after use */
20
+extern char *url_decode(char *str);
21
+
22
+#endif /* NETDATA_URL_H */
src/web_buffer.c
new
+153
@@ -0,0 +1,153 @@
1
+#include <stdlib.h>
2
+
3
+#include "web_buffer.h"
4
+
5
+#include "common.h"
6
+#include "log.h"
7
+
8
+void web_buffer_strcpy(struct web_buffer *wb, const char *txt)
9
+{
10
+ char *buffer = wb->buffer;
11
+ long bytes = wb->bytes, size = wb->size, i = 0;
12
+
13
+ while(txt[i] && bytes < size)
14
+ buffer[bytes++] = txt[i++];
15
+
16
+ wb->bytes = bytes;
17
+}
18
+
19
+int print_calculated_number(char *str, calculated_number value)
20
+{
21
+ char *wstr = str;
22
+
23
+ // make sure it is unsigned
24
+ unsigned long long uvalue = (unsigned long long)(((value < 0) ? -value : value) * (calculated_number)10000);
25
+
26
+ // print each digit
27
+ do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
28
+
29
+ // make sure we have 8 bytes at least
30
+ while((wstr - str) < 5) *wstr++ = '0';
31
+
32
+ // put the sign back
33
+ if (value < 0) *wstr++ = '-';
34
+
35
+ // reverse it
36
+ wstr--;
37
+ strreverse(str, wstr);
38
+
39
+ // terminate it, one position to the right
40
+ // to let space for a dot
41
+ wstr[2] = '\0';
42
+
43
+ int i;
44
+ for(i = 0; i < 4 ;i++) {
45
+ wstr[1] = wstr[0];
46
+ wstr--;
47
+ }
48
+ wstr[1] = '.';
49
+
50
+ // return the buffer length
51
+ return ( (wstr - str) + 6 );
52
+}
53
+
54
+void web_buffer_rrd_value(struct web_buffer *wb, calculated_number value)
55
+{
56
+ if(wb->size - wb->bytes < 50) return;
57
+ wb->bytes += print_calculated_number(&wb->buffer[wb->bytes], value);
58
+}
59
+
60
+// generate a javascript date, the fastest possible way...
61
+void web_buffer_jsdate(struct web_buffer *wb, int year, int month, int day, int hours, int minutes, int seconds)
62
+{
63
+ // 10 20 30 = 35
64
+ // 01234567890123456789012345678901234
65
+ // Date(2014, 04, 01, 03, 28, 20, 065)
66
+
67
+ if(wb->size - wb->bytes < 36) return;
68
+
69
+ char *b = &wb->buffer[wb->bytes];
70
+
71
+ int i = 0;
72
+ b[i++]='D';
73
+ b[i++]='a';
74
+ b[i++]='t';
75
+ b[i++]='e';
76
+ b[i++]='(';
77
+ b[i++]= 48 + year / 1000; year -= (year / 1000) * 1000;
78
+ b[i++]= 48 + year / 100; year -= (year / 100) * 100;
79
+ b[i++]= 48 + year / 10;
80
+ b[i++]= 48 + year % 10;
81
+ b[i++]=',';
82
+ //b[i++]=' ';
83
+ b[i]= 48 + month / 10; if(b[i] != '0') i++;
84
+ b[i++]= 48 + month % 10;
85
+ b[i++]=',';
86
+ //b[i++]=' ';
87
+ b[i]= 48 + day / 10; if(b[i] != '0') i++;
88
+ b[i++]= 48 + day % 10;
89
+ b[i++]=',';
90
+ //b[i++]=' ';
91
+ b[i]= 48 + hours / 10; if(b[i] != '0') i++;
92
+ b[i++]= 48 + hours % 10;
93
+ b[i++]=',';
94
+ //b[i++]=' ';
95
+ b[i]= 48 + minutes / 10; if(b[i] != '0') i++;
96
+ b[i++]= 48 + minutes % 10;
97
+ b[i++]=',';
98
+ //b[i++]=' ';
99
+ b[i]= 48 + seconds / 10; if(b[i] != '0') i++;
100
+ b[i++]= 48 + seconds % 10;
101
+ b[i++]=')';
102
+ b[i]='\0';
103
+
104
+ wb->bytes += i;
105
+}
106
+
107
+struct web_buffer *web_buffer_create(long size)
108
+{
109
+ struct web_buffer *b;
110
+
111
+ debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size);
112
+
113
+ b = calloc(1, sizeof(struct web_buffer));
114
+ if(!b) {
115
+ error("Cannot allocate a web_buffer.");
116
+ return NULL;
117
+ }
118
+
119
+ b->buffer = malloc(size);
120
+ if(!b->buffer) {
121
+ error("Cannot allocate a buffer of size %u.", size);
122
+ free(b);
123
+ return NULL;
124
+ }
125
+ b->buffer[0] = '\0';
126
+ b->size = size;
127
+ b->contenttype = CT_TEXT_PLAIN;
128
+ return(b);
129
+}
130
+
131
+void web_buffer_free(struct web_buffer *b)
132
+{
133
+ debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size);
134
+
135
+ if(b->buffer) free(b->buffer);
136
+ free(b);
137
+}
138
+
139
+void web_buffer_increase(struct web_buffer *b, long free_size_required)
140
+{
141
+ long left = b->size - b->bytes;
142
+
143
+ if(left >= free_size_required) return;
144
+ long increase = free_size_required - left;
145
+ if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
146
+
147
+ debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
148
+
149
+ b->buffer = realloc(b->buffer, b->size + increase);
150
+ if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size, b->size + increase);
151
+
152
+ b->size += increase;
153
+}
src/web_buffer.h
new
+49
@@ -0,0 +1,49 @@
1
+#include <stdarg.h>
2
+#include <time.h>
3
+#include "storage_number.h"
4
+
5
+#ifndef NETDATA_WEB_BUFFER_H
6
+#define NETDATA_WEB_BUFFER_H 1
7
+
8
+#define WEB_DATA_LENGTH_INCREASE_STEP 65536
9
+
10
+struct web_buffer {
11
+ long size; // allocation size of buffer
12
+ long bytes; // current data length in buffer
13
+ long sent; // current data length sent to output
14
+ char *buffer; // the buffer
15
+ int contenttype;
16
+ long rbytes; // if non-zero, the excepted size of ifd
17
+ time_t date; // the date this content has been generated
18
+};
19
+
20
+// content-types
21
+#define CT_APPLICATION_JSON 1
22
+#define CT_TEXT_PLAIN 2
23
+#define CT_TEXT_HTML 3
24
+#define CT_APPLICATION_X_JAVASCRIPT 4
25
+#define CT_TEXT_CSS 5
26
+#define CT_TEXT_XML 6
27
+#define CT_APPLICATION_XML 7
28
+#define CT_TEXT_XSL 8
29
+#define CT_APPLICATION_OCTET_STREAM 9
30
+#define CT_APPLICATION_X_FONT_TRUETYPE 10
31
+#define CT_APPLICATION_X_FONT_OPENTYPE 11
32
+#define CT_APPLICATION_FONT_WOFF 12
33
+#define CT_APPLICATION_VND_MS_FONTOBJ 13
34
+#define CT_IMAGE_SVG_XML 14
35
+
36
+#define web_buffer_printf(wb, args...) wb->bytes += snprintf(&wb->buffer[wb->bytes], (wb->size - wb->bytes), ##args)
37
+#define web_buffer_reset(wb) wb->buffer[wb->bytes = 0] = '\0'
38
+
39
+void web_buffer_strcpy(struct web_buffer *wb, const char *txt);
40
+int print_calculated_number(char *str, calculated_number value);
41
+void web_buffer_rrd_value(struct web_buffer *wb, calculated_number value);
42
+
43
+void web_buffer_jsdate(struct web_buffer *wb, int year, int month, int day, int hours, int minutes, int seconds);
44
+
45
+struct web_buffer *web_buffer_create(long size);
46
+void web_buffer_free(struct web_buffer *b);
47
+void web_buffer_increase(struct web_buffer *b, long free_size_required);
48
+
49
+#endif /* NETDATA_WEB_BUFFER_H */
src/web_client.c
new
+86
@@ -0,0 +1,86 @@
1
+#include <stdlib.h>
2
+#include <sys/types.h>
3
+#include <sys/socket.h>
4
+#include <netinet/in.h>
5
+#include <arpa/inet.h>
6
+
7
+#include "global_statistics.h"
8
+#include "web_client.h"
9
+#include "log.h"
10
+
11
+#define INITIAL_WEB_DATA_LENGTH 65536
12
+
13
+struct web_client *web_clients = NULL;
14
+unsigned long long web_clients_count = 0;
15
+
16
+struct web_client *web_client_create(int listener)
17
+{
18
+ struct web_client *w;
19
+ socklen_t addrlen;
20
+
21
+ w = calloc(1, sizeof(struct web_client));
22
+ if(!w) {
23
+ error("Cannot allocate new web_client memory.");
24
+ return NULL;
25
+ }
26
+
27
+ w->id = ++web_clients_count;
28
+ w->mode = WEB_CLIENT_MODE_NORMAL;
29
+
30
+ addrlen = sizeof(w->clientaddr);
31
+ w->ifd = accept(listener, (struct sockaddr *)&w->clientaddr, &addrlen);
32
+ if (w->ifd == -1) {
33
+ error("%llu: Cannot accept new incoming connection.", w->id);
34
+ free(w);
35
+ return NULL;
36
+ }
37
+ w->ofd = w->ifd;
38
+
39
+ strncpy(w->client_ip, inet_ntoa(w->clientaddr.sin_addr), 100);
40
+ w->client_ip[100] = '\0';
41
+
42
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New web client from %s on socket %d.", w->id, w->client_ip, w->ifd);
43
+
44
+ {
45
+ int flag = 1;
46
+ if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
47
+ }
48
+
49
+ w->data = web_buffer_create(INITIAL_WEB_DATA_LENGTH);
50
+ if(!w->data) {
51
+ close(w->ifd);
52
+ free(w);
53
+ return NULL;
54
+ }
55
+
56
+ w->wait_receive = 1;
57
+
58
+ if(web_clients) web_clients->prev = w;
59
+ w->next = web_clients;
60
+ web_clients = w;
61
+
62
+ global_statistics.connected_clients++;
63
+
64
+ return(w);
65
+}
66
+
67
+struct web_client *web_client_free(struct web_client *w)
68
+{
69
+ struct web_client *n = w->next;
70
+
71
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s.", w->id, inet_ntoa(w->clientaddr.sin_addr));
72
+
73
+ if(w->prev) w->prev->next = w->next;
74
+ if(w->next) w->next->prev = w->prev;
75
+
76
+ if(w == web_clients) web_clients = w->next;
77
+
78
+ if(w->data) web_buffer_free(w->data);
79
+ close(w->ifd);
80
+ if(w->ofd != w->ifd) close(w->ofd);
81
+ free(w);
82
+
83
+ global_statistics.connected_clients--;
84
+
85
+ return(n);
86
+}
src/web_client.h
new
+61
@@ -0,0 +1,61 @@
1
+#include <zlib.h>
2
+#include <sys/time.h>
3
+#include <string.h>
4
+#include <sys/socket.h>
5
+#include <netinet/in.h>
6
+#include <arpa/inet.h>
7
+
8
+#include "web_buffer.h"
9
+
10
+#ifndef NETDATA_WEB_CLIENT_H
11
+#define NETDATA_WEB_CLIENT_H 1
12
+
13
+#define WEB_CLIENT_MODE_NORMAL 0
14
+#define WEB_CLIENT_MODE_FILECOPY 1
15
+
16
+#define URL_MAX 8192
17
+#define ZLIB_CHUNK 16384
18
+#define MAX_HTTP_HEADER_SIZE 16384
19
+
20
+struct web_client {
21
+ unsigned long long id;
22
+ char client_ip[101];
23
+ char last_url[URL_MAX+1];
24
+
25
+ struct timeval tv_in, tv_ready;
26
+
27
+ int mode;
28
+ int keepalive;
29
+
30
+ struct sockaddr_in clientaddr;
31
+
32
+ pthread_t thread; // the thread servicing this client
33
+ int obsolete; // if set to 1, the listener will remove this client
34
+
35
+ int ifd;
36
+ int ofd;
37
+
38
+ struct web_buffer *data;
39
+
40
+ int zoutput; // if set to 1, web_client_send() will send compressed data
41
+ z_stream zstream; // zlib stream for sending compressed output to client
42
+ Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output
43
+ long zsent; // the compressed bytes we have sent to the client
44
+ long zhave; // the compressed bytes that we have to send
45
+ int zinitialized;
46
+
47
+ int wait_receive;
48
+ int wait_send;
49
+
50
+ char response_header[MAX_HTTP_HEADER_SIZE+1];
51
+
52
+ struct web_client *prev;
53
+ struct web_client *next;
54
+};
55
+
56
+extern struct web_client *web_clients;
57
+
58
+extern struct web_client *web_client_create(int listener);
59
+extern struct web_client *web_client_free(struct web_client *w);
60
+
61
+#endif
src/web_server.c
new
+1247
@@ -0,0 +1,1247 @@
1
+// enable strcasestr()
2
+#define _GNU_SOURCE
3
+
4
+#include <stdlib.h>
5
+#include <sys/types.h>
6
+#include <sys/socket.h>
7
+#include <netinet/in.h>
8
+#include <arpa/inet.h>
9
+#include <errno.h>
10
+#include <pthread.h>
11
+#include <sys/stat.h>
12
+#include <fcntl.h>
13
+#include <netinet/tcp.h>
14
+
15
+#include "common.h"
16
+#include "log.h"
17
+#include "config.h"
18
+#include "url.h"
19
+#include "web_buffer.h"
20
+#include "web_client.h"
21
+#include "web_server.h"
22
+#include "global_statistics.h"
23
+#include "rrd.h"
24
+#include "rrd2json.h"
25
+
26
+int listen_fd = -1;
27
+int listen_port = LISTEN_PORT;
28
+
29
+int create_listen_socket(int port)
30
+{
31
+ int sock=-1;
32
+ int sockopt=1;
33
+ struct sockaddr_in name;
34
+
35
+ debug(D_LISTENER, "Creating new listening socket on port %d", port);
36
+
37
+ sock = socket(AF_INET, SOCK_STREAM, 0);
38
+ if (sock < 0)
39
+ fatal("socket() failed, errno=%d", errno);
40
+
41
+ /* avoid "address already in use" */
42
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
43
+
44
+ memset(&name, 0, sizeof(struct sockaddr_in));
45
+ name.sin_family = AF_INET;
46
+ name.sin_port = htons (port);
47
+ name.sin_addr.s_addr = htonl (INADDR_ANY);
48
+ if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
49
+ fatal("bind() failed, errno=%d", errno);
50
+
51
+ if (listen(sock, LISTEN_BACKLOG) < 0)
52
+ fatal("listen() failed, errno=%d", errno);
53
+
54
+ debug(D_LISTENER, "Listening Port %d created", port);
55
+ return sock;
56
+}
57
+
58
+
59
+int mysendfile(struct web_client *w, char *filename)
60
+{
61
+ static char *web_dir = NULL;
62
+ if(!web_dir) web_dir = config_get("global", "web files directory", "web");
63
+
64
+ debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
65
+
66
+ // skip leading slashes
67
+ while (*filename == '/') filename++;
68
+
69
+ // if the filename contain known paths, skip them
70
+ if(strncmp(filename, WEB_PATH_DATA "/", strlen(WEB_PATH_DATA) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATA) + 1];
71
+ else if(strncmp(filename, WEB_PATH_DATASOURCE "/", strlen(WEB_PATH_DATASOURCE) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATASOURCE) + 1];
72
+ else if(strncmp(filename, WEB_PATH_GRAPH "/", strlen(WEB_PATH_GRAPH) + 1) == 0) filename = &filename[strlen(WEB_PATH_GRAPH) + 1];
73
+ else if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
74
+
75
+ // if the filename contains a / or a .., refuse to serve it
76
+ if(strchr(filename, '/') != 0 || strstr(filename, "..") != 0) {
77
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
78
+ web_buffer_printf(w->data, "File '%s' cannot be served. Filenames cannot contain / or ..", filename);
79
+ return 400;
80
+ }
81
+
82
+ // access the file
83
+ char webfilename[FILENAME_MAX + 1];
84
+ snprintf(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
85
+
86
+ // check if the file exists
87
+ struct stat stat;
88
+ if(lstat(webfilename, &stat) != 0) {
89
+ error("%llu: File '%s' is not found.", w->id, webfilename);
90
+ web_buffer_printf(w->data, "File '%s' does not exist, or is not accessible.", filename);
91
+ return 404;
92
+ }
93
+
94
+ // check if the file is owned by us
95
+ if(stat.st_uid != getuid() && stat.st_uid != geteuid()) {
96
+ error("%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, webfilename, stat.st_uid, getuid());
97
+ web_buffer_printf(w->data, "Access to file '%s' is not permitted.", filename);
98
+ return 403;
99
+ }
100
+
101
+ // open the file
102
+ w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
103
+ if(w->ifd == -1) {
104
+ w->ifd = w->ofd;
105
+
106
+ if(errno == EBUSY || errno == EAGAIN) {
107
+ error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
108
+ snprintf(w->response_header, MAX_HTTP_HEADER_SIZE, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
109
+ web_buffer_printf(w->data, "The file '%s' is currently busy. Please try again later.", filename);
110
+ return 307;
111
+ }
112
+ else {
113
+ error("%llu: Cannot open file '%s'.", w->id, webfilename);
114
+ web_buffer_printf(w->data, "Cannot open file '%s'.", filename);
115
+ return 404;
116
+ }
117
+ }
118
+
119
+ // pick a Content-Type for the file
120
+ if(strstr(filename, ".html") != NULL) w->data->contenttype = CT_TEXT_HTML;
121
+ else if(strstr(filename, ".js") != NULL) w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
122
+ else if(strstr(filename, ".css") != NULL) w->data->contenttype = CT_TEXT_CSS;
123
+ else if(strstr(filename, ".xml") != NULL) w->data->contenttype = CT_TEXT_XML;
124
+ else if(strstr(filename, ".xsl") != NULL) w->data->contenttype = CT_TEXT_XSL;
125
+ else if(strstr(filename, ".txt") != NULL) w->data->contenttype = CT_TEXT_PLAIN;
126
+ else if(strstr(filename, ".svg") != NULL) w->data->contenttype = CT_IMAGE_SVG_XML;
127
+ else if(strstr(filename, ".ttf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
128
+ else if(strstr(filename, ".otf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
129
+ else if(strstr(filename, ".woff") != NULL) w->data->contenttype = CT_APPLICATION_FONT_WOFF;
130
+ else if(strstr(filename, ".eot") != NULL) w->data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
131
+ else w->data->contenttype = CT_APPLICATION_OCTET_STREAM;
132
+
133
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd);
134
+
135
+ w->mode = WEB_CLIENT_MODE_FILECOPY;
136
+ w->wait_receive = 1;
137
+ w->wait_send = 0;
138
+ w->data->bytes = 0;
139
+ w->data->buffer[0] = '\0';
140
+ w->data->rbytes = stat.st_size;
141
+ w->data->date = stat.st_mtim.tv_sec;
142
+
143
+ return 200;
144
+}
145
+
146
+void web_client_reset(struct web_client *w)
147
+{
148
+ struct timeval tv;
149
+ gettimeofday(&tv, NULL);
150
+
151
+ long sent = w->zoutput?(long)w->zstream.total_out:((w->mode == WEB_CLIENT_MODE_FILECOPY)?w->data->rbytes:w->data->bytes);
152
+ long size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->data->rbytes:w->data->bytes;
153
+
154
+ if(w->last_url[0]) log_access("%llu: (sent/all = %ld/%ld bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: '%s'",
155
+ w->id,
156
+ sent, size, -((size>0)?((float)(size-sent)/(float)size * 100.0):0.0),
157
+ (float)usecdiff(&w->tv_ready, &w->tv_in) / 1000.0,
158
+ (float)usecdiff(&tv, &w->tv_ready) / 1000.0,
159
+ (float)usecdiff(&tv, &w->tv_in) / 1000.0,
160
+ (w->mode == WEB_CLIENT_MODE_FILECOPY)?"filecopy":"data",
161
+ w->last_url
162
+ );
163
+
164
+ debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
165
+
166
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
167
+ debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id);
168
+ close(w->ifd);
169
+ w->ifd = w->ofd;
170
+ }
171
+
172
+ w->last_url[0] = '\0';
173
+
174
+ w->data->contenttype = CT_TEXT_PLAIN;
175
+ w->mode = WEB_CLIENT_MODE_NORMAL;
176
+
177
+ w->data->rbytes = 0;
178
+ w->data->bytes = 0;
179
+ w->data->sent = 0;
180
+
181
+ w->response_header[0] = '\0';
182
+ w->data->buffer[0] = '\0';
183
+
184
+ w->wait_receive = 1;
185
+ w->wait_send = 0;
186
+
187
+ // if we had enabled compression, release it
188
+ if(w->zinitialized) {
189
+ debug(D_DEFLATE, "%llu: Reseting compression.", w->id);
190
+ deflateEnd(&w->zstream);
191
+ w->zoutput = 0;
192
+ w->zsent = 0;
193
+ w->zhave = 0;
194
+ w->zstream.avail_in = 0;
195
+ w->zstream.avail_out = 0;
196
+ w->zstream.total_in = 0;
197
+ w->zstream.total_out = 0;
198
+ w->zinitialized = 0;
199
+ }
200
+}
201
+
202
+void web_client_enable_deflate(struct web_client *w) {
203
+ if(w->zinitialized == 1) {
204
+ error("%llu: Compression has already be initialized for this client.", w->id);
205
+ return;
206
+ }
207
+
208
+ if(w->data->sent) {
209
+ error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
210
+ return;
211
+ }
212
+
213
+ w->zstream.zalloc = Z_NULL;
214
+ w->zstream.zfree = Z_NULL;
215
+ w->zstream.opaque = Z_NULL;
216
+
217
+ w->zstream.next_in = (Bytef *)w->data->buffer;
218
+ w->zstream.avail_in = 0;
219
+ w->zstream.total_in = 0;
220
+
221
+ w->zstream.next_out = w->zbuffer;
222
+ w->zstream.avail_out = 0;
223
+ w->zstream.total_out = 0;
224
+
225
+ w->zstream.zalloc = Z_NULL;
226
+ w->zstream.zfree = Z_NULL;
227
+ w->zstream.opaque = Z_NULL;
228
+
229
+// if(deflateInit(&w->zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
230
+// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
231
+// return;
232
+// }
233
+
234
+ // Select GZIP compression: windowbits = 15 + 16 = 31
235
+ if(deflateInit2(&w->zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
236
+ error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
237
+ return;
238
+ }
239
+
240
+ w->zsent = 0;
241
+ w->zoutput = 1;
242
+ w->zinitialized = 1;
243
+
244
+ debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
245
+}
246
+
247
+int web_client_data_request(struct web_client *w, char *url, int datasource_type)
248
+{
249
+ char *args = strchr(url, '?');
250
+ if(args) {
251
+ *args='\0';
252
+ args = &args[1];
253
+ }
254
+
255
+ // get the name of the data to show
256
+ char *tok = mystrsep(&url, "/");
257
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
258
+
259
+ // do we have such a data set?
260
+ RRD_STATS *st = rrd_stats_find_byname(tok);
261
+ if(!st) st = rrd_stats_find(tok);
262
+ if(!st) {
263
+ // we don't have it
264
+ // try to send a file with that name
265
+ w->data->bytes = 0;
266
+ return(mysendfile(w, tok));
267
+ }
268
+
269
+ // we have it
270
+ debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
271
+
272
+ // how many entries does the client want?
273
+ long lines = save_history;
274
+ long group_count = 1;
275
+ time_t after = 0, before = 0;
276
+ int group_method = GROUP_AVERAGE;
277
+ int nonzero = 0;
278
+
279
+ if(url) {
280
+ // parse the lines required
281
+ tok = mystrsep(&url, "/");
282
+ if(tok) lines = atoi(tok);
283
+ if(lines < 1) lines = 1;
284
+ }
285
+ if(url) {
286
+ // parse the group count required
287
+ tok = mystrsep(&url, "/");
288
+ if(tok) group_count = atoi(tok);
289
+ if(group_count < 1) group_count = 1;
290
+ //if(group_count > save_history / 20) group_count = save_history / 20;
291
+ }
292
+ if(url) {
293
+ // parse the grouping method required
294
+ tok = mystrsep(&url, "/");
295
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
296
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
297
+ else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
298
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
299
+ }
300
+ if(url) {
301
+ // parse after time
302
+ tok = mystrsep(&url, "/");
303
+ if(tok) after = strtoul(tok, NULL, 10);
304
+ if(after < 0) after = 0;
305
+ }
306
+ if(url) {
307
+ // parse before time
308
+ tok = mystrsep(&url, "/");
309
+ if(tok) before = strtoul(tok, NULL, 10);
310
+ if(before < 0) before = 0;
311
+ }
312
+ if(url) {
313
+ // parse nonzero
314
+ tok = mystrsep(&url, "/");
315
+ if(tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
316
+ }
317
+
318
+ w->data->contenttype = CT_APPLICATION_JSON;
319
+ w->data->bytes = 0;
320
+
321
+ char *google_version = "0.6";
322
+ char *google_reqId = "0";
323
+ char *google_sig = "0";
324
+ char *google_out = "json";
325
+ char *google_responseHandler = "google.visualization.Query.setResponse";
326
+ char *google_outFileName = NULL;
327
+ unsigned long last_timestamp_in_data = 0;
328
+ if(datasource_type == DATASOURCE_GOOGLE_JSON || datasource_type == DATASOURCE_GOOGLE_JSONP) {
329
+
330
+ w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
331
+
332
+ while(args) {
333
+ tok = mystrsep(&args, "&");
334
+ if(tok) {
335
+ char *name = mystrsep(&tok, "=");
336
+ if(name && strcmp(name, "tqx") == 0) {
337
+ char *key = mystrsep(&tok, ":");
338
+ char *value = mystrsep(&tok, ";");
339
+ if(key && value && *key && *value) {
340
+ if(strcmp(key, "version") == 0)
341
+ google_version = value;
342
+
343
+ else if(strcmp(key, "reqId") == 0)
344
+ google_reqId = value;
345
+
346
+ else if(strcmp(key, "sig") == 0)
347
+ google_sig = value;
348
+
349
+ else if(strcmp(key, "out") == 0)
350
+ google_out = value;
351
+
352
+ else if(strcmp(key, "responseHandler") == 0)
353
+ google_responseHandler = value;
354
+
355
+ else if(strcmp(key, "outFileName") == 0)
356
+ google_outFileName = value;
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
363
+ w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
364
+ );
365
+
366
+ if(datasource_type == DATASOURCE_GOOGLE_JSONP) {
367
+ last_timestamp_in_data = strtoul(google_sig, NULL, 0);
368
+
369
+ // check the client wants json
370
+ if(strcmp(google_out, "json") != 0) {
371
+ w->data->bytes = snprintf(w->data->buffer, w->data->size,
372
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
373
+ google_responseHandler, google_version, google_reqId, google_out);
374
+ return 200;
375
+ }
376
+ }
377
+ }
378
+
379
+ if(datasource_type == DATASOURCE_GOOGLE_JSONP) {
380
+ w->data->bytes = snprintf(w->data->buffer, w->data->size,
381
+ "%s({version:'%s',reqId:'%s',status:'ok',sig:'%lu',table:",
382
+ google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
383
+ }
384
+
385
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %lu after, %lu before).", w->id, st->name, st->id, lines, group_count, group_method, after, before);
386
+ unsigned long timestamp_in_data = rrd_stats_json(datasource_type, st, w->data, lines, group_count, group_method, after, before, nonzero);
387
+
388
+ if(datasource_type == DATASOURCE_GOOGLE_JSONP) {
389
+ if(timestamp_in_data > last_timestamp_in_data)
390
+ w->data->bytes += snprintf(&w->data->buffer[w->data->bytes], w->data->size - w->data->bytes, "});");
391
+
392
+ else {
393
+ // the client already has the latest data
394
+ w->data->bytes = snprintf(w->data->buffer, w->data->size,
395
+ "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
396
+ google_responseHandler, google_version, google_reqId);
397
+ }
398
+ }
399
+
400
+ return 200;
401
+}
402
+
403
+void web_client_process(struct web_client *w)
404
+{
405
+ int code = 500;
406
+ int bytes;
407
+
408
+ w->wait_receive = 0;
409
+
410
+ // check if we have an empty line (end of HTTP header)
411
+ if(strstr(w->data->buffer, "\r\n\r\n")) {
412
+ global_statistics_lock();
413
+ global_statistics.web_requests++;
414
+ global_statistics_unlock();
415
+
416
+ gettimeofday(&w->tv_in, NULL);
417
+ debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer);
418
+
419
+ // check if the client requested keep-alive HTTP
420
+ if(strcasestr(w->data->buffer, "Connection: keep-alive")) w->keepalive = 1;
421
+ else w->keepalive = 0;
422
+
423
+ // check if the client accepts deflate
424
+ if(strstr(w->data->buffer, "gzip"))
425
+ web_client_enable_deflate(w);
426
+
427
+ int datasource_type = DATASOURCE_GOOGLE_JSONP;
428
+ //if(strstr(w->data->buffer, "X-DataSource-Auth"))
429
+ // datasource_type = DATASOURCE_GOOGLE_JSON;
430
+
431
+ char *buf = w->data->buffer;
432
+ char *tok = strsep(&buf, " \r\n");
433
+ char *url = NULL;
434
+ char *pointer_to_free = NULL; // keep url_decode() allocated buffer
435
+
436
+ if(buf && strcmp(tok, "GET") == 0) {
437
+ tok = strsep(&buf, " \r\n");
438
+ pointer_to_free = url = url_decode(tok);
439
+ debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
440
+ }
441
+ else if (buf && strcmp(tok, "POST") == 0) {
442
+ w->keepalive = 0;
443
+ tok = strsep(&buf, " \r\n");
444
+ pointer_to_free = url = url_decode(tok);
445
+
446
+ debug(D_WEB_CLIENT, "%llu: I don't know how to handle POST with form data. Assuming it is a GET on url '%s'.", w->id, url);
447
+ }
448
+
449
+ w->last_url[0] = '\0';
450
+ if(url) {
451
+ strncpy(w->last_url, url, URL_MAX);
452
+ w->last_url[URL_MAX] = '\0';
453
+
454
+ tok = mystrsep(&url, "/?&");
455
+
456
+ debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
457
+
458
+ if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
459
+ // the client is requesting rrd data
460
+ datasource_type = DATASOURCE_JSON;
461
+ code = web_client_data_request(w, url, datasource_type);
462
+ }
463
+ else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
464
+ // the client is requesting google datasource
465
+ code = web_client_data_request(w, url, datasource_type);
466
+ }
467
+ else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
468
+ // the client is requesting an rrd graph
469
+
470
+ // get the name of the data to show
471
+ tok = mystrsep(&url, "/?&");
472
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
473
+
474
+ // do we have such a data set?
475
+ RRD_STATS *st = rrd_stats_find_byname(tok);
476
+ if(!st) {
477
+ // we don't have it
478
+ // try to send a file with that name
479
+ w->data->bytes = 0;
480
+ code = mysendfile(w, tok);
481
+ }
482
+ else {
483
+ code = 200;
484
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
485
+ w->data->contenttype = CT_APPLICATION_JSON;
486
+ w->data->bytes = 0;
487
+ rrd_stats_graph_json(st, url, w->data);
488
+ }
489
+ }
490
+ else if(strcmp(tok, "debug") == 0) {
491
+ w->data->bytes = 0;
492
+
493
+ // get the name of the data to show
494
+ tok = mystrsep(&url, "/?&");
495
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
496
+
497
+ // do we have such a data set?
498
+ RRD_STATS *st = rrd_stats_find_byname(tok);
499
+ if(!st) {
500
+ code = 404;
501
+ web_buffer_printf(w->data, "Chart %s is not found.\r\n", tok);
502
+ debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
503
+ }
504
+ else {
505
+ code = 200;
506
+ debug_flags |= D_RRD_STATS;
507
+ st->debug = st->debug?0:1;
508
+ web_buffer_printf(w->data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
509
+ debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
510
+ }
511
+ }
512
+ else if(strcmp(tok, "mirror") == 0) {
513
+ code = 200;
514
+
515
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
516
+
517
+ // replace the zero bytes with spaces
518
+ int i;
519
+ for(i = 0; i < w->data->size; i++)
520
+ if(w->data->buffer[i] == '\0') w->data->buffer[i] = ' ';
521
+
522
+ // just leave the buffer as is
523
+ // it will be copied back to the client
524
+ }
525
+ else if(strcmp(tok, "list") == 0) {
526
+ code = 200;
527
+
528
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
529
+
530
+ w->data->bytes = 0;
531
+ RRD_STATS *st = root;
532
+
533
+ for ( ; st ; st = st->next )
534
+ web_buffer_printf(w->data, "%s\n", st->name);
535
+ }
536
+ else if(strcmp(tok, "all.json") == 0) {
537
+ code = 200;
538
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
539
+
540
+ w->data->contenttype = CT_APPLICATION_JSON;
541
+ w->data->bytes = 0;
542
+ rrd_stats_all_json(w->data);
543
+ }
544
+ else if(strcmp(tok, "netdata.conf") == 0) {
545
+ code = 200;
546
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
547
+
548
+ w->data->contenttype = CT_TEXT_PLAIN;
549
+ w->data->bytes = 0;
550
+ generate_config(w->data, 0);
551
+ }
552
+ else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file"
553
+ tok = mystrsep(&url, "/?&");
554
+ if(tok && *tok) code = mysendfile(w, tok);
555
+ else {
556
+ code = 400;
557
+ w->data->bytes = 0;
558
+ strcpy(w->data->buffer, "You have to give a filename to get.\r\n");
559
+ w->data->bytes = strlen(w->data->buffer);
560
+ }
561
+ }
562
+ else if(!tok[0]) {
563
+ w->data->bytes = 0;
564
+ code = mysendfile(w, "index.html");
565
+ }
566
+ else {
567
+ w->data->bytes = 0;
568
+ code = mysendfile(w, tok);
569
+ }
570
+
571
+ }
572
+ else {
573
+ strcpy(w->last_url, "not a valid response");
574
+
575
+ if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
576
+
577
+ code = 500;
578
+ w->data->bytes = 0;
579
+ strcpy(w->data->buffer, "I don't understand you...\r\n");
580
+ w->data->bytes = strlen(w->data->buffer);
581
+ }
582
+
583
+ // free url_decode() buffer
584
+ if(pointer_to_free) free(pointer_to_free);
585
+ }
586
+ else if(w->data->bytes > 8192) {
587
+ strcpy(w->last_url, "too big request");
588
+
589
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big.", w->id);
590
+
591
+ code = 400;
592
+ w->data->bytes = 0;
593
+ strcpy(w->data->buffer, "Received request is too big.\r\n");
594
+ w->data->bytes = strlen(w->data->buffer);
595
+ }
596
+ else {
597
+ // wait for more data
598
+ w->wait_receive = 1;
599
+ return;
600
+ }
601
+
602
+ if(w->data->bytes > w->data->size) {
603
+ error("%llu: memory overflow encountered (size is %ld, written %ld).", w->data->size, w->data->bytes);
604
+ }
605
+
606
+ gettimeofday(&w->tv_ready, NULL);
607
+ w->data->date = time(NULL);
608
+ w->data->sent = 0;
609
+
610
+ // prepare the HTTP response header
611
+ debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
612
+
613
+ char *content_type_string = "";
614
+ switch(w->data->contenttype) {
615
+ case CT_TEXT_HTML:
616
+ content_type_string = "text/html";
617
+ break;
618
+
619
+ case CT_APPLICATION_XML:
620
+ content_type_string = "application/xml";
621
+ break;
622
+
623
+ case CT_APPLICATION_JSON:
624
+ content_type_string = "application/json";
625
+ break;
626
+
627
+ case CT_APPLICATION_X_JAVASCRIPT:
628
+ content_type_string = "application/x-javascript";
629
+ break;
630
+
631
+ case CT_TEXT_CSS:
632
+ content_type_string = "text/css";
633
+ break;
634
+
635
+ case CT_TEXT_XML:
636
+ content_type_string = "text/xml";
637
+ break;
638
+
639
+ case CT_TEXT_XSL:
640
+ content_type_string = "text/xsl";
641
+ break;
642
+
643
+ case CT_APPLICATION_OCTET_STREAM:
644
+ content_type_string = "application/octet-stream";
645
+ break;
646
+
647
+ case CT_IMAGE_SVG_XML:
648
+ content_type_string = "image/svg+xml";
649
+ break;
650
+
651
+ case CT_APPLICATION_X_FONT_TRUETYPE:
652
+ content_type_string = "application/x-font-truetype";
653
+ break;
654
+
655
+ case CT_APPLICATION_X_FONT_OPENTYPE:
656
+ content_type_string = "application/x-font-opentype";
657
+ break;
658
+
659
+ case CT_APPLICATION_FONT_WOFF:
660
+ content_type_string = "application/font-woff";
661
+ break;
662
+
663
+ case CT_APPLICATION_VND_MS_FONTOBJ:
664
+ content_type_string = "application/vnd.ms-fontobject";
665
+ break;
666
+
667
+ default:
668
+ case CT_TEXT_PLAIN:
669
+ content_type_string = "text/plain";
670
+ break;
671
+ }
672
+
673
+ char *code_msg = "";
674
+ switch(code) {
675
+ case 200:
676
+ code_msg = "OK";
677
+ break;
678
+
679
+ case 307:
680
+ code_msg = "Temporary Redirect";
681
+ break;
682
+
683
+ case 400:
684
+ code_msg = "Bad Request";
685
+ break;
686
+
687
+ case 403:
688
+ code_msg = "Forbidden";
689
+ break;
690
+
691
+ case 404:
692
+ code_msg = "Not Found";
693
+ break;
694
+
695
+ default:
696
+ code_msg = "Internal Server Error";
697
+ break;
698
+ }
699
+
700
+ char date[100];
701
+ struct tm tm = *gmtime(&w->data->date);
702
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm);
703
+
704
+ char custom_header[MAX_HTTP_HEADER_SIZE + 1] = "";
705
+ if(w->response_header[0])
706
+ strcpy(custom_header, w->response_header);
707
+
708
+ int headerlen = 0;
709
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
710
+ "HTTP/1.1 %d %s\r\n"
711
+ "Connection: %s\r\n"
712
+ "Server: NetData Embedded HTTP Server\r\n"
713
+ "Content-Type: %s\r\n"
714
+ "Access-Control-Allow-Origin: *\r\n"
715
+ "Date: %s\r\n"
716
+ , code, code_msg
717
+ , w->keepalive?"keep-alive":"close"
718
+ , content_type_string
719
+ , date
720
+ );
721
+
722
+ if(custom_header[0])
723
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "%s", custom_header);
724
+
725
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) {
726
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
727
+ "Expires: %s\r\n"
728
+ "Cache-Control: no-cache\r\n"
729
+ , date
730
+ );
731
+ }
732
+ else {
733
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
734
+ "Cache-Control: public\r\n"
735
+ );
736
+ }
737
+
738
+ // if we know the content length, put it
739
+ if(!w->zoutput && (w->data->bytes || w->data->rbytes))
740
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
741
+ "Content-Length: %ld\r\n"
742
+ , w->data->bytes?w->data->bytes:w->data->rbytes
743
+ );
744
+ else if(!w->zoutput)
745
+ w->keepalive = 0; // content-length is required for keep-alive
746
+
747
+ if(w->zoutput) {
748
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen,
749
+ "Content-Encoding: gzip\r\n"
750
+ "Transfer-Encoding: chunked\r\n"
751
+ );
752
+ }
753
+
754
+ headerlen += snprintf(&w->response_header[headerlen], MAX_HTTP_HEADER_SIZE - headerlen, "\r\n");
755
+
756
+ // disable TCP_NODELAY, to buffer the header
757
+ int flag = 0;
758
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
759
+
760
+ // sent the HTTP header
761
+ debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %d: '%s'", w->id, headerlen, w->response_header);
762
+
763
+ bytes = send(w->ofd, w->response_header, headerlen, 0);
764
+ if(bytes != headerlen)
765
+ error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes);
766
+ else {
767
+ global_statistics_lock();
768
+ global_statistics.bytes_sent += bytes;
769
+ global_statistics_unlock();
770
+ }
771
+
772
+ // enable TCP_NODELAY, to send all data immediately at the next send()
773
+ flag = 1;
774
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
775
+
776
+ // enable sending immediately if we have data
777
+ if(w->data->bytes) w->wait_send = 1;
778
+ else w->wait_send = 0;
779
+
780
+ // pretty logging
781
+ switch(w->mode) {
782
+ case WEB_CLIENT_MODE_NORMAL:
783
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->bytes);
784
+ break;
785
+
786
+ case WEB_CLIENT_MODE_FILECOPY:
787
+ if(w->data->rbytes) {
788
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rbytes);
789
+ w->wait_receive = 1;
790
+
791
+ /*
792
+ // utilize the kernel sendfile() for copying the file to the socket.
793
+ // this block of code can be commented, without anything missing.
794
+ // when it is commented, the program will copy the data using async I/O.
795
+ {
796
+ long len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes);
797
+ if(len != w->data->rbytes) error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->data->rbytes, len);
798
+ else web_client_reset(w);
799
+ }
800
+ */
801
+ }
802
+ else
803
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
804
+ break;
805
+
806
+ default:
807
+ fatal("%llu: Unknown client mode %d.", w->id, w->mode);
808
+ break;
809
+ }
810
+}
811
+
812
+long web_client_send_chunk_header(struct web_client *w, int len)
813
+{
814
+ debug(D_DEFLATE, "%llu: OPEN CHUNK of %d bytes (hex: %x).", w->id, len, len);
815
+ char buf[1024];
816
+ sprintf(buf, "%X\r\n", len);
817
+ int bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT);
818
+
819
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk header %d bytes.", w->id, bytes);
820
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk header to the client.", w->id);
821
+ else debug(D_DEFLATE, "%llu: Failed to send chunk header to client. Reason: %s", w->id, strerror(errno));
822
+
823
+ return bytes;
824
+}
825
+
826
+long web_client_send_chunk_close(struct web_client *w)
827
+{
828
+ //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
829
+
830
+ int bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT);
831
+
832
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
833
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
834
+ else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client. Reason: %s", w->id, strerror(errno));
835
+
836
+ return bytes;
837
+}
838
+
839
+long web_client_send_chunk_finalize(struct web_client *w)
840
+{
841
+ //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
842
+
843
+ int bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT);
844
+
845
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
846
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
847
+ else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client. Reason: %s", w->id, strerror(errno));
848
+
849
+ return bytes;
850
+}
851
+
852
+long web_client_send_deflate(struct web_client *w)
853
+{
854
+ long bytes = 0, t = 0;
855
+
856
+ // when using compression,
857
+ // w->data->sent is the amount of bytes passed through compression
858
+
859
+ // debug(D_DEFLATE, "%llu: TEST w->data->bytes = %d, w->data->sent = %d, w->zhave = %d, w->zsent = %d, w->zstream.avail_in = %d, w->zstream.avail_out = %d, w->zstream.total_in = %d, w->zstream.total_out = %d.", w->id, w->data->bytes, w->data->sent, w->zhave, w->zsent, w->zstream.avail_in, w->zstream.avail_out, w->zstream.total_in, w->zstream.total_out);
860
+
861
+ if(w->data->bytes - w->data->sent == 0 && w->zstream.avail_in == 0 && w->zhave == w->zsent && w->zstream.avail_out != 0) {
862
+ // there is nothing to send
863
+
864
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
865
+
866
+ // finalize the chunk
867
+ if(w->data->sent != 0)
868
+ t += web_client_send_chunk_finalize(w);
869
+
870
+ // there can be two cases for this
871
+ // A. we have done everything
872
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
873
+
874
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rbytes && w->data->rbytes > w->data->bytes) {
875
+ // we have to wait, more data will come
876
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
877
+ w->wait_send = 0;
878
+ return(0);
879
+ }
880
+
881
+ if(w->keepalive == 0) {
882
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent);
883
+ errno = 0;
884
+ return(-1);
885
+ }
886
+
887
+ // reset the client
888
+ web_client_reset(w);
889
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
890
+ return(0);
891
+ }
892
+
893
+ if(w->zstream.avail_out == 0 && w->zhave == w->zsent) {
894
+ // compress more input data
895
+
896
+ // close the previous open chunk
897
+ if(w->data->sent != 0) t += web_client_send_chunk_close(w);
898
+
899
+ debug(D_DEFLATE, "%llu: Compressing %d bytes starting from %d.", w->id, (w->data->bytes - w->data->sent), w->data->sent);
900
+
901
+ // give the compressor all the data not passed through the compressor yet
902
+ if(w->data->bytes > w->data->sent) {
903
+ w->zstream.next_in = (Bytef *)&w->data->buffer[w->data->sent];
904
+ w->zstream.avail_in = (w->data->bytes - w->data->sent);
905
+ }
906
+
907
+ // reset the compressor output buffer
908
+ w->zstream.next_out = w->zbuffer;
909
+ w->zstream.avail_out = ZLIB_CHUNK;
910
+
911
+ // ask for FINISH if we have all the input
912
+ int flush = Z_SYNC_FLUSH;
913
+ if(w->mode == WEB_CLIENT_MODE_NORMAL
914
+ || (w->mode == WEB_CLIENT_MODE_FILECOPY && w->data->bytes == w->data->rbytes)) {
915
+ flush = Z_FINISH;
916
+ debug(D_DEFLATE, "%llu: Requesting Z_FINISH.", w->id);
917
+ }
918
+ else {
919
+ debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
920
+ }
921
+
922
+ // compress
923
+ if(deflate(&w->zstream, flush) == Z_STREAM_ERROR) {
924
+ error("%llu: Compression failed. Closing down client.", w->id);
925
+ web_client_reset(w);
926
+ return(-1);
927
+ }
928
+
929
+ w->zhave = ZLIB_CHUNK - w->zstream.avail_out;
930
+ w->zsent = 0;
931
+
932
+ // keep track of the bytes passed through the compressor
933
+ w->data->sent = w->data->bytes;
934
+
935
+ debug(D_DEFLATE, "%llu: Compression produced %d bytes.", w->id, w->zhave);
936
+
937
+ // open a new chunk
938
+ t += web_client_send_chunk_header(w, w->zhave);
939
+ }
940
+
941
+ bytes = send(w->ofd, &w->zbuffer[w->zsent], w->zhave - w->zsent, MSG_DONTWAIT);
942
+ if(bytes > 0) {
943
+ w->zsent += bytes;
944
+ if(t > 0) bytes += t;
945
+ debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
946
+ }
947
+ else if(bytes == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
948
+ else debug(D_WEB_CLIENT, "%llu: Failed to send data to client. Reason: %s", w->id, strerror(errno));
949
+
950
+ return(bytes);
951
+}
952
+
953
+long web_client_send(struct web_client *w)
954
+{
955
+ if(w->zoutput) return web_client_send_deflate(w);
956
+
957
+ long bytes;
958
+
959
+ if(w->data->bytes - w->data->sent == 0) {
960
+ // there is nothing to send
961
+
962
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
963
+
964
+ // there can be two cases for this
965
+ // A. we have done everything
966
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
967
+
968
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rbytes && w->data->rbytes > w->data->bytes) {
969
+ // we have to wait, more data will come
970
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
971
+ w->wait_send = 0;
972
+ return(0);
973
+ }
974
+
975
+ if(w->keepalive == 0) {
976
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent);
977
+ errno = 0;
978
+ return(-1);
979
+ }
980
+
981
+ web_client_reset(w);
982
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
983
+ return(0);
984
+ }
985
+
986
+ bytes = send(w->ofd, &w->data->buffer[w->data->sent], w->data->bytes - w->data->sent, MSG_DONTWAIT);
987
+ if(bytes > 0) {
988
+ w->data->sent += bytes;
989
+ debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
990
+ }
991
+ else if(bytes == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
992
+ else debug(D_WEB_CLIENT, "%llu: Failed to send data to client. Reason: %s", w->id, strerror(errno));
993
+
994
+
995
+ return(bytes);
996
+}
997
+
998
+long web_client_receive(struct web_client *w)
999
+{
1000
+ // do we have any space for more data?
1001
+ web_buffer_increase(w->data, WEB_DATA_LENGTH_INCREASE_STEP);
1002
+
1003
+ long left = w->data->size - w->data->bytes;
1004
+ long bytes;
1005
+
1006
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY)
1007
+ bytes = read(w->ifd, &w->data->buffer[w->data->bytes], (left-1));
1008
+ else
1009
+ bytes = recv(w->ifd, &w->data->buffer[w->data->bytes], left-1, MSG_DONTWAIT);
1010
+
1011
+ if(bytes > 0) {
1012
+ int old = w->data->bytes;
1013
+ w->data->bytes += bytes;
1014
+ w->data->buffer[w->data->bytes] = '\0';
1015
+
1016
+ debug(D_WEB_CLIENT, "%llu: Received %d bytes.", w->id, bytes);
1017
+ debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->data->buffer[old]);
1018
+
1019
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1020
+ w->wait_send = 1;
1021
+ if(w->data->rbytes && w->data->bytes >= w->data->rbytes) w->wait_receive = 0;
1022
+ }
1023
+ }
1024
+ else if(bytes == 0) {
1025
+ debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
1026
+
1027
+ // if we cannot read, it means we have an error on input.
1028
+ // if however, we are copying a file from ifd to ofd, we should not return an error.
1029
+ // in this case, the error should be generated when the file has been sent to the client.
1030
+
1031
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1032
+ // we are copying data fron ifd to ofd
1033
+ // let it finish copying...
1034
+ w->wait_receive = 0;
1035
+ debug(D_WEB_CLIENT, "%llu: Disabling input.", w->id);
1036
+ }
1037
+ else {
1038
+ bytes = -1;
1039
+ errno = 0;
1040
+ }
1041
+ }
1042
+
1043
+ return(bytes);
1044
+}
1045
+
1046
+
1047
+// --------------------------------------------------------------------------------------
1048
+// the thread of a single client
1049
+
1050
+// 1. waits for input and output, using async I/O
1051
+// 2. it processes HTTP requests
1052
+// 3. it generates HTTP responses
1053
+// 4. it copies data from input to output if mode is FILECOPY
1054
+
1055
+void *new_client(void *ptr)
1056
+{
1057
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
1058
+ error("Cannot set pthread cancel type to DEFERRED.");
1059
+
1060
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
1061
+ error("Cannot set pthread cancel state to ENABLE.");
1062
+
1063
+ struct timeval tv;
1064
+ struct web_client *w = ptr;
1065
+ int retval;
1066
+ fd_set ifds, ofds, efds;
1067
+ int fdmax = 0;
1068
+
1069
+ for(;;) {
1070
+ FD_ZERO (&ifds);
1071
+ FD_ZERO (&ofds);
1072
+ FD_ZERO (&efds);
1073
+
1074
+ FD_SET(w->ifd, &efds);
1075
+ if(w->ifd != w->ofd) FD_SET(w->ofd, &efds);
1076
+ if (w->wait_receive) {
1077
+ FD_SET(w->ifd, &ifds);
1078
+ if(w->ifd > fdmax) fdmax = w->ifd;
1079
+ }
1080
+ if (w->wait_send) {
1081
+ FD_SET(w->ofd, &ofds);
1082
+ if(w->ofd > fdmax) fdmax = w->ofd;
1083
+ }
1084
+
1085
+ tv.tv_sec = 30;
1086
+ tv.tv_usec = 0;
1087
+
1088
+ debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
1089
+ retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1090
+
1091
+ if(retval == -1) {
1092
+ error("%llu: LISTENER: select() failed.", w->id);
1093
+ continue;
1094
+ }
1095
+ else if(!retval) {
1096
+ // timeout
1097
+ web_client_reset(w);
1098
+ w->obsolete = 1;
1099
+ return NULL;
1100
+ }
1101
+
1102
+ if(FD_ISSET(w->ifd, &efds)) {
1103
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on input socket (%s).", w->id, strerror(errno));
1104
+ web_client_reset(w);
1105
+ w->obsolete = 1;
1106
+ return NULL;
1107
+ }
1108
+
1109
+ if(FD_ISSET(w->ofd, &efds)) {
1110
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on output socket (%s).", w->id, strerror(errno));
1111
+ web_client_reset(w);
1112
+ w->obsolete = 1;
1113
+ return NULL;
1114
+ }
1115
+
1116
+ if(w->wait_send && FD_ISSET(w->ofd, &ofds)) {
1117
+ long bytes;
1118
+ if((bytes = web_client_send(w)) < 0) {
1119
+ debug(D_WEB_CLIENT, "%llu: Closing client (input: %s).", w->id, strerror(errno));
1120
+ web_client_reset(w);
1121
+ w->obsolete = 1;
1122
+ errno = 0;
1123
+ return NULL;
1124
+ }
1125
+ else {
1126
+ global_statistics_lock();
1127
+ global_statistics.bytes_sent += bytes;
1128
+ global_statistics_unlock();
1129
+ }
1130
+ }
1131
+
1132
+ if(w->wait_receive && FD_ISSET(w->ifd, &ifds)) {
1133
+ long bytes;
1134
+ if((bytes = web_client_receive(w)) < 0) {
1135
+ debug(D_WEB_CLIENT, "%llu: Closing client (output: %s).", w->id, strerror(errno));
1136
+ web_client_reset(w);
1137
+ w->obsolete = 1;
1138
+ errno = 0;
1139
+ return NULL;
1140
+ }
1141
+ else {
1142
+ if(w->mode != WEB_CLIENT_MODE_FILECOPY) {
1143
+ global_statistics_lock();
1144
+ global_statistics.bytes_received += bytes;
1145
+ global_statistics_unlock();
1146
+ }
1147
+ }
1148
+
1149
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) web_client_process(w);
1150
+ }
1151
+ }
1152
+ debug(D_WEB_CLIENT, "%llu: done...", w->id);
1153
+
1154
+ return NULL;
1155
+}
1156
+
1157
+// --------------------------------------------------------------------------------------
1158
+// the main socket listener
1159
+
1160
+// 1. it accepts new incoming requests on our port
1161
+// 2. creates a new web_client for each connection received
1162
+// 3. spawns a new pthread to serve the client (this is optimal for keep-alive clients)
1163
+// 4. cleans up old web_clients that their pthreads have been exited
1164
+
1165
+void *socket_listen_main(void *ptr)
1166
+{
1167
+ if(ptr) { ; }
1168
+ struct web_client *w;
1169
+ struct timeval tv;
1170
+ int retval;
1171
+
1172
+ if(ptr) { ; }
1173
+
1174
+ if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
1175
+ error("Cannot set pthread cancel type to DEFERRED.");
1176
+
1177
+ if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
1178
+ error("Cannot set pthread cancel state to ENABLE.");
1179
+
1180
+ // int listener = create_listen_socket(listen_port);
1181
+ int listener = listen_fd;
1182
+ if(listener == -1) fatal("LISTENER: Cannot create listening socket on port 19999.");
1183
+
1184
+ fd_set ifds, ofds, efds;
1185
+ int fdmax = listener;
1186
+
1187
+ FD_ZERO (&ifds);
1188
+ FD_ZERO (&ofds);
1189
+ FD_ZERO (&efds);
1190
+
1191
+ for(;;) {
1192
+ tv.tv_sec = 0;
1193
+ tv.tv_usec = 200000;
1194
+
1195
+ FD_SET(listener, &ifds);
1196
+ FD_SET(listener, &efds);
1197
+
1198
+ // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
1199
+ retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1200
+
1201
+ if(retval == -1) {
1202
+ error("LISTENER: select() failed.");
1203
+ continue;
1204
+ }
1205
+ else if(retval) {
1206
+ // check for new incoming connections
1207
+ if(FD_ISSET(listener, &ifds)) {
1208
+ w = web_client_create(listener);
1209
+
1210
+ if(pthread_create(&w->thread, NULL, new_client, w) != 0) {
1211
+ error("%llu: failed to create new thread for web client.");
1212
+ w->obsolete = 1;
1213
+ }
1214
+ else if(pthread_detach(w->thread) != 0) {
1215
+ error("%llu: Cannot request detach of newly created web client thread.", w->id);
1216
+ w->obsolete = 1;
1217
+ }
1218
+
1219
+ log_access("%llu: %s connected", w->id, w->client_ip);
1220
+ }
1221
+ else debug(D_WEB_CLIENT, "LISTENER: select() didn't do anything.");
1222
+
1223
+ }
1224
+ //else {
1225
+ // debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
1226
+ //}
1227
+
1228
+ // cleanup unused clients
1229
+ for(w = web_clients; w ; w = w?w->next:NULL) {
1230
+ if(w->obsolete) {
1231
+ log_access("%llu: %s disconnected", w->id, w->client_ip);
1232
+ debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
1233
+ // pthread_join(w->thread, NULL);
1234
+ w = web_client_free(w);
1235
+ log_allocations();
1236
+ }
1237
+ }
1238
+ }
1239
+
1240
+ error("LISTENER: exit!");
1241
+
1242
+ close(listener);
1243
+ exit(2);
1244
+
1245
+ return NULL;
1246
+}
1247
+
src/web_server.h
new
+18
@@ -0,0 +1,18 @@
1
+#ifndef NETDATA_WEB_SERVER_H
2
+#define NETDATA_WEB_SERVER_H 1
3
+
4
+#define WEB_PATH_FILE "file"
5
+#define WEB_PATH_DATA "data"
6
+#define WEB_PATH_DATASOURCE "datasource"
7
+#define WEB_PATH_GRAPH "graph"
8
+
9
+#define LISTEN_PORT 19999
10
+#define LISTEN_BACKLOG 100
11
+
12
+extern int listen_fd;
13
+extern int listen_port;
14
+
15
+extern int create_listen_socket(int port);
16
+extern void *socket_listen_main(void *ptr);
17
+
18
+#endif /* NETDATA_WEB_SERVER_H */