@cryptotaxi247 / netdata-1 / commits / 037bbc245

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 */