added gzip compression and chunked http responses support
Costa Tsaousis (ktsaou) committed
Apr 5, 2014 at 09:02 UTC
99c1ebbeecf83c1a529f9334f3d72a6644b300a4
3 files changed
+3458
-3233
netdata.c
+3453
-3223
@@ -1,3223 +1,3453 @@
1
-// enable strcasestr()
2
-#define _GNU_SOURCE
3
-
4
-#include <stdio.h>
5
-#include <stdlib.h>
6
-#include <string.h>
7
-#include <time.h>
8
-#include <unistd.h>
9
-#include <pwd.h>
10
-#include <sys/types.h>
11
-#include <sys/time.h>
12
-
13
-#include <sys/socket.h>
14
-#include <sys/select.h>
15
-#include <sys/resource.h>
16
-#include <sys/stat.h>
17
-#include <sys/sendfile.h>
18
-
19
-#include <arpa/inet.h>
20
-#include <netinet/in.h>
21
-#include <netinet/tcp.h>
22
-
23
-#include <errno.h>
24
-#include <stdarg.h>
25
-#include <locale.h>
26
-#include <signal.h>
27
-#include <ctype.h>
28
-#include <time.h>
29
-#include <fcntl.h>
30
-#include <syslog.h>
31
-
32
-#include <pthread.h>
33
-
34
-#define RRD_DIMENSION_ABSOLUTE 0
35
-#define RRD_DIMENSION_INCREMENTAL 1
36
-#define RRD_DIMENSION_PCENT_OVER_TOTAL 2
37
-
38
-#define RRD_TYPE_NET "net"
39
-#define RRD_TYPE_NET_LEN strlen(RRD_TYPE_NET)
40
-
41
-#define RRD_TYPE_TC "tc"
42
-#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
43
-
44
-#define RRD_TYPE_DISK "disk"
45
-#define RRD_TYPE_DISK_LEN strlen(RRD_TYPE_DISK)
46
-
47
-#define RRD_TYPE_NET_SNMP "ipv4"
48
-#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
49
-
50
-#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
51
-#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
52
-
53
-#define RRD_TYPE_NET_IPVS "ipvs"
54
-#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
55
-
56
-#define RRD_TYPE_STAT "cpu"
57
-#define RRD_TYPE_STAT_LEN strlen(RRD_TYPE_STAT)
58
-
59
-#define WEB_PATH_FILE "file"
60
-#define WEB_PATH_DATA "data"
61
-#define WEB_PATH_GRAPH "graph"
62
-
63
-// internal defaults
64
-#define UPDATE_EVERY 1
65
-#define UPDATE_EVERY_MAX 3600
66
-#define LISTEN_PORT 19999
67
-#define HISTORY 3600
68
-#define HISTORY_MAX (86400*10)
69
-#define SAVE_PATH "/tmp"
70
-
71
-#define D_WEB_BUFFER 0x00000001
72
-#define D_WEB_CLIENT 0x00000002
73
-#define D_LISTENER 0x00000004
74
-#define D_WEB_DATA 0x00000008
75
-#define D_OPTIONS 0x00000010
76
-#define D_PROCNETDEV_LOOP 0x00000020
77
-#define D_RRD_STATS 0x00000040
78
-#define D_WEB_CLIENT_ACCESS 0x00000080
79
-#define D_TC_LOOP 0x00000100
80
-
81
-#define CT_APPLICATION_JSON 1
82
-#define CT_TEXT_PLAIN 2
83
-#define CT_TEXT_HTML 3
84
-#define CT_APPLICATION_X_JAVASCRIPT 4
85
-#define CT_TEXT_CSS 5
86
-#define CT_TEXT_XML 6
87
-#define CT_APPLICATION_XML 7
88
-#define CT_TEXT_XSL 8
89
-#define CT_APPLICATION_OCTET_STREAM 9
90
-#define CT_APPLICATION_X_FONT_TRUETYPE 10
91
-#define CT_APPLICATION_X_FONT_OPENTYPE 11
92
-#define CT_APPLICATION_FONT_WOFF 12
93
-#define CT_APPLICATION_VND_MS_FONTOBJ 13
94
-#define CT_IMAGE_SVG_XML 14
95
-
96
-// configuration
97
-#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
98
-//#define DEBUG 0xffffffff
99
-//#define DEBUG (0)
100
-
101
-#define EXIT_FAILURE 1
102
-#define LISTEN_BACKLOG 100
103
-
104
-#define MAX_SOCKET_DATA_LENGTH (256 * 1024)
105
-#define SOCKET_DATA_LENGTH_INCREASE_STEP 65536
106
-
107
-#define MAX_HTTP_HEADER_SIZE 16384
108
-
109
-#define MAX_PROC_NET_DEV_LINE 4096
110
-#define MAX_PROC_NET_DEV_IFACE_NAME 1024
111
-
112
-#define MAX_PROC_DISKSTATS_LINE 4096
113
-#define MAX_PROC_DISKSTATS_DISK_NAME 1024
114
-
115
-#define MAX_PROC_NET_SNMP_LINE 4096
116
-#define MAX_PROC_NET_SNMP_NAME 1024
117
-
118
-#define MAX_PROC_NET_STAT_CONNTRACK_LINE 4096
119
-#define MAX_PROC_NET_STAT_CONNTRACK_NAME 1024
120
-
121
-#define MAX_PROC_NET_IPVS_LINE 4096
122
-#define MAX_PROC_NET_IPVS_NAME 1024
123
-
124
-#define MAX_PROC_STAT_LINE 4096
125
-#define MAX_PROC_STAT_NAME 1024
126
-
127
-
128
-int silent = 0;
129
-int save_history = HISTORY;
130
-int update_every = UPDATE_EVERY;
131
-int listen_port = LISTEN_PORT;
132
-
133
-
134
-// ----------------------------------------------------------------------------
135
-// helpers
136
-
137
-unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
138
- return ((((now->tv_sec * 1000000L) + now->tv_usec) - ((last->tv_sec * 1000000L) + last->tv_usec)));
139
-}
140
-
141
-// ----------------------------------------------------------------------------
142
-// LOG
143
-
144
-void log_date()
145
-{
146
- char outstr[200];
147
- time_t t;
148
- struct tm *tmp;
149
-
150
- t = time(NULL);
151
- tmp = localtime(&t);
152
-
153
- if (tmp == NULL) return;
154
- if (strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0) return;
155
-
156
- fprintf(stderr, "%s: ", outstr);
157
-}
158
-
159
-#define debug(args...) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args)
160
-
161
-void debug_int( const char *file, const char *function, const unsigned long line, unsigned long type, const char *fmt, ... )
162
-{
163
- if(silent) return;
164
-
165
- va_list args;
166
-
167
- if(DEBUG & type) {
168
- log_date();
169
- va_start( args, fmt );
170
- fprintf(stderr, "DEBUG (%04lu@%-15.15s): ", line, function);
171
- vfprintf( stderr, fmt, args );
172
- va_end( args );
173
- fprintf(stderr, "\n");
174
- }
175
-}
176
-
177
-#define error(args...) error_int(__FILE__, __FUNCTION__, __LINE__, ##args)
178
-
179
-void error_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
180
-{
181
- va_list args;
182
-
183
- if(!silent) {
184
- log_date();
185
-
186
- va_start( args, fmt );
187
- fprintf(stderr, "ERROR (%04lu@%-15.15s): ", line, function);
188
- vfprintf( stderr, fmt, args );
189
- va_end( args );
190
-
191
- if(errno) {
192
- fprintf(stderr, " (errno %d, %s)\n", errno, strerror(errno));
193
- errno = 0;
194
- }
195
- else fprintf(stderr, "\n");
196
- }
197
-
198
- va_start( args, fmt );
199
- vsyslog(LOG_ERR, fmt, args );
200
- va_end( args );
201
-}
202
-
203
-#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
204
-
205
-void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
206
-{
207
- va_list args;
208
-
209
- if(!silent) {
210
- log_date();
211
-
212
- va_start( args, fmt );
213
- fprintf(stderr, "FATAL (%04lu@%-15.15s): ", line, function);
214
- vfprintf( stderr, fmt, args );
215
- va_end( args );
216
-
217
- perror(" # ");
218
- fprintf(stderr, "\n");
219
- }
220
-
221
- va_start( args, fmt );
222
- vsyslog(LOG_CRIT, fmt, args );
223
- va_end( args );
224
-
225
- exit(EXIT_FAILURE);
226
-}
227
-
228
-
229
-// ----------------------------------------------------------------------------
230
-// URL encode / decode
231
-// code from: http://www.geekhideout.com/urlcode.shtml
232
-
233
-/* Converts a hex character to its integer value */
234
-char from_hex(char ch) {
235
- return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
236
-}
237
-
238
-/* Converts an integer value to its hex character*/
239
-char to_hex(char code) {
240
- static char hex[] = "0123456789abcdef";
241
- return hex[code & 15];
242
-}
243
-
244
-/* Returns a url-encoded version of str */
245
-/* IMPORTANT: be sure to free() the returned string after use */
246
-char *url_encode(char *str) {
247
- char *pstr = str,
248
- *buf = malloc(strlen(str) * 3 + 1),
249
- *pbuf = buf;
250
-
251
- while (*pstr) {
252
- if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
253
- *pbuf++ = *pstr;
254
-
255
- else if (*pstr == ' ')
256
- *pbuf++ = '+';
257
-
258
- else
259
- *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
260
-
261
- pstr++;
262
- }
263
-
264
- *pbuf = '\0';
265
-
266
- return buf;
267
-}
268
-
269
-/* Returns a url-decoded version of str */
270
-/* IMPORTANT: be sure to free() the returned string after use */
271
-char *url_decode(char *str) {
272
- char *pstr = str,
273
- *buf = malloc(strlen(str) + 1),
274
- *pbuf = buf;
275
-
276
- if(!buf) fatal("Cannot allocate memory.");
277
-
278
- while (*pstr) {
279
- if (*pstr == '%') {
280
- if (pstr[1] && pstr[2]) {
281
- *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
282
- pstr += 2;
283
- }
284
- }
285
- else if (*pstr == '+')
286
- *pbuf++ = ' ';
287
-
288
- else
289
- *pbuf++ = *pstr;
290
-
291
- pstr++;
292
- }
293
-
294
- *pbuf = '\0';
295
-
296
- return buf;
297
-}
298
-
299
-
300
-// ----------------------------------------------------------------------------
301
-// socket
302
-
303
-int create_listen_socket(int port)
304
-{
305
- int sock=-1;
306
- int sockopt=1;
307
- struct sockaddr_in name;
308
-
309
- debug(D_LISTENER, "Creating new listening socket on port %d", port);
310
-
311
- sock = socket(AF_INET, SOCK_STREAM, 0);
312
- if (sock < 0)
313
- fatal("socket() failed, errno=%d", errno);
314
-
315
- /* avoid "address already in use" */
316
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
317
-
318
- memset(&name, 0, sizeof(struct sockaddr_in));
319
- name.sin_family = AF_INET;
320
- name.sin_port = htons (port);
321
- name.sin_addr.s_addr = htonl (INADDR_ANY);
322
- if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
323
- fatal("bind() failed, errno=%d", errno);
324
-
325
- if (listen(sock, LISTEN_BACKLOG) < 0)
326
- fatal("listen() failed, errno=%d", errno);
327
-
328
- debug(D_LISTENER, "Listening Port %d created", port);
329
- return sock;
330
-}
331
-
332
-
333
-// ----------------------------------------------------------------------------
334
-// RRD STATS
335
-
336
-#define RRD_STATS_NAME_MAX 1024
337
-
338
-struct rrd_dimension {
339
- char id[RRD_STATS_NAME_MAX + 1];
340
- char name[RRD_STATS_NAME_MAX + 1];
341
- size_t bytes;
342
- size_t entries;
343
-
344
- int type;
345
- int hidden; // if set to non zero, this dimension will not be sent to the client
346
-
347
- long multiplier;
348
- long divisor;
349
-
350
- void *values;
351
-
352
- time_t last_updated;
353
-
354
- struct rrd_dimension *next;
355
-};
356
-typedef struct rrd_dimension RRD_DIMENSION;
357
-
358
-struct rrd_stats {
359
- pthread_mutex_t mutex;
360
-
361
- char id[RRD_STATS_NAME_MAX + 1];
362
- char name[RRD_STATS_NAME_MAX + 1];
363
- char title[RRD_STATS_NAME_MAX + 1];
364
- char vtitle[RRD_STATS_NAME_MAX + 1];
365
-
366
- char usertitle[RRD_STATS_NAME_MAX + 1];
367
- char userpriority[RRD_STATS_NAME_MAX + 1];
368
-
369
- char envtitle[RRD_STATS_NAME_MAX + 1];
370
- char envpriority[RRD_STATS_NAME_MAX + 1];
371
-
372
- char type[RRD_STATS_NAME_MAX + 1];
373
-
374
- char hostname[RRD_STATS_NAME_MAX + 1];
375
-
376
- size_t entries;
377
- size_t current_entry;
378
- // size_t last_entry;
379
-
380
- time_t last_updated;
381
-
382
- struct timeval *times;
383
- RRD_DIMENSION *dimensions;
384
- struct rrd_stats *next;
385
-};
386
-typedef struct rrd_stats RRD_STATS;
387
-
388
-RRD_STATS *root = NULL;
389
-pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;
390
-
391
-RRD_STATS *rrd_stats_create(const char *id, const char *name, unsigned long entries, const char *title, const char *vtitle, const char *type)
392
-{
393
- RRD_STATS *st = NULL;
394
- char *p;
395
-
396
- debug(D_RRD_STATS, "Creating RRD_STATS for '%s'.", name);
397
-
398
- st = calloc(1, sizeof(RRD_STATS));
399
- if(!st) return NULL;
400
-
401
- st->times = calloc(entries, sizeof(struct timeval));
402
- if(!st->times) {
403
- error("Cannot allocate %lu entries of %lu bytes each for RRD_STATS.", st->entries, sizeof(struct timeval));
404
- free(st);
405
- return NULL;
406
- }
407
-
408
- strncpy(st->id, id, RRD_STATS_NAME_MAX);
409
- st->id[RRD_STATS_NAME_MAX] = '\0';
410
-
411
- strncpy(st->name, name, RRD_STATS_NAME_MAX);
412
- st->name[RRD_STATS_NAME_MAX] = '\0';
413
- while((p = strchr(st->name, '/'))) *p = '_';
414
- while((p = strchr(st->name, '?'))) *p = '_';
415
- while((p = strchr(st->name, '&'))) *p = '_';
416
-
417
- strncpy(st->title, title, RRD_STATS_NAME_MAX);
418
- st->title[RRD_STATS_NAME_MAX] = '\0';
419
-
420
- strncpy(st->vtitle, vtitle, RRD_STATS_NAME_MAX);
421
- st->vtitle[RRD_STATS_NAME_MAX] = '\0';
422
-
423
- strncpy(st->type, type, RRD_STATS_NAME_MAX);
424
- st->type[RRD_STATS_NAME_MAX] = '\0';
425
-
426
- // check if there is a name for it in the environment
427
- sprintf(st->envtitle, "NETDATA_TITLE_%s", st->id);
428
- while((p = strchr(st->envtitle, '/'))) *p = '_';
429
- while((p = strchr(st->envtitle, '.'))) *p = '_';
430
- while((p = strchr(st->envtitle, '-'))) *p = '_';
431
- p = getenv(st->envtitle);
432
-
433
- if(p) strncpy(st->usertitle, p, RRD_STATS_NAME_MAX);
434
- else strncpy(st->usertitle, st->name, RRD_STATS_NAME_MAX);
435
- st->usertitle[RRD_STATS_NAME_MAX] = '\0';
436
-
437
- sprintf(st->envpriority, "NETDATA_PRIORITY_%s", st->id);
438
- while((p = strchr(st->envpriority, '/'))) *p = '_';
439
- while((p = strchr(st->envpriority, '.'))) *p = '_';
440
- while((p = strchr(st->envpriority, '-'))) *p = '_';
441
- p = getenv(st->envpriority);
442
-
443
- if(p) strncpy(st->userpriority, p, RRD_STATS_NAME_MAX);
444
- else strncpy(st->userpriority, st->name, RRD_STATS_NAME_MAX);
445
- st->userpriority[RRD_STATS_NAME_MAX] = '\0';
446
-
447
- if(gethostname(st->hostname, RRD_STATS_NAME_MAX) == -1)
448
- error("Cannot get hostname.");
449
-
450
- st->entries = entries;
451
- st->current_entry = 0;
452
- // st->last_entry = 0;
453
- st->dimensions = NULL;
454
- st->last_updated = time(NULL);
455
-
456
- pthread_mutex_init(&st->mutex, NULL);
457
- pthread_mutex_lock(&st->mutex);
458
- pthread_mutex_lock(&root_mutex);
459
-
460
- st->next = root;
461
- root = st;
462
-
463
- pthread_mutex_unlock(&root_mutex);
464
- // leave st->mutex locked
465
-
466
- return(st);
467
-}
468
-
469
-RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *id, const char *name, size_t bytes, long multiplier, long divisor, int type)
470
-{
471
- RRD_DIMENSION *rd = NULL;
472
-
473
- debug(D_RRD_STATS, "Adding dimension '%s' (%s) to RRD_STATS '%s' (%s).", name, id, st->name, st->id);
474
-
475
- rd = calloc(1, sizeof(RRD_DIMENSION));
476
- if(!rd) return NULL;
477
-
478
- rd->bytes = bytes;
479
- rd->entries = st->entries;
480
- rd->multiplier = multiplier;
481
- rd->divisor = divisor;
482
- rd->type = type;
483
- rd->values = calloc(rd->entries, rd->bytes);
484
- if(!rd->values) {
485
- error("Cannot allocate %lu entries of %lu bytes each for RRD_DIMENSION.", rd->entries, rd->bytes);
486
- free(rd);
487
- return NULL;
488
- }
489
-
490
- strncpy(rd->id, id, RRD_STATS_NAME_MAX);
491
- rd->id[RRD_STATS_NAME_MAX] = '\0';
492
-
493
- strncpy(rd->name, name, RRD_STATS_NAME_MAX);
494
- rd->name[RRD_STATS_NAME_MAX] = '\0';
495
-
496
- rd->next = st->dimensions;
497
- st->dimensions = rd;
498
-
499
- return(rd);
500
-}
501
-
502
-void rrd_stats_dimension_free(RRD_DIMENSION *rd)
503
-{
504
- if(rd->next) rrd_stats_dimension_free(rd->next);
505
- debug(D_RRD_STATS, "Removing dimension '%s'.", rd->name);
506
- free(rd->values);
507
- free(rd);
508
-}
509
-
510
-RRD_STATS *rrd_stats_find(const char *id)
511
-{
512
- pthread_mutex_lock(&root_mutex);
513
-
514
- RRD_STATS *st = root;
515
-
516
- for ( ; st ; st = st->next ) {
517
- if(strcmp(st->id, id) == 0) break;
518
- }
519
-
520
- pthread_mutex_unlock(&root_mutex);
521
- return(st);
522
-}
523
-
524
-RRD_STATS *rrd_stats_find_byname(const char *name)
525
-{
526
- pthread_mutex_lock(&root_mutex);
527
-
528
- RRD_STATS *st = root;
529
-
530
- for ( ; st ; st = st->next ) {
531
- if(strcmp(st->name, name) == 0) break;
532
- }
533
-
534
- pthread_mutex_unlock(&root_mutex);
535
- return(st);
536
-}
537
-
538
-RRD_DIMENSION *rrd_stats_dimension_find(RRD_STATS *st, const char *id)
539
-{
540
- RRD_DIMENSION *rd = st->dimensions;
541
-
542
- for ( ; rd ; rd = rd->next ) {
543
- if(strcmp(rd->id, id) == 0) break;
544
- }
545
-
546
- return(rd);
547
-}
548
-
549
-void rrd_stats_next(RRD_STATS *st)
550
-{
551
- struct timeval *now;
552
-
553
- pthread_mutex_lock(&st->mutex);
554
-
555
- // st->current_entry should never be outside the array
556
- // or, the parallel threads may end up crashing
557
- // st->last_entry = st->current_entry;
558
- st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
559
-
560
- now = &st->times[st->current_entry];
561
- gettimeofday(now, NULL);
562
-
563
- st->last_updated = now->tv_sec;
564
-
565
- // leave mutex locked
566
-}
567
-
568
-void rrd_stats_done(RRD_STATS *st)
569
-{
570
- RRD_DIMENSION *rd, *last;
571
-
572
- // find if there are any obsolete dimensions (not updated recently)
573
- for( rd = st->dimensions, last = NULL ; rd ; ) {
574
- if((rd->last_updated + (10 * update_every)) < st->last_updated) { // remove it only it is not updated in 10 seconds
575
- debug(D_RRD_STATS, "Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
576
-
577
- if(!last) {
578
- st->dimensions = rd->next;
579
- rd->next = NULL;
580
- rrd_stats_dimension_free(rd);
581
- rd = st->dimensions;
582
- continue;
583
- }
584
- else {
585
- last->next = rd->next;
586
- rd->next = NULL;
587
- rrd_stats_dimension_free(rd);
588
- rd = last->next;
589
- continue;
590
- }
591
- }
592
-
593
- last = rd;
594
- rd = rd->next;
595
- }
596
-
597
- pthread_mutex_unlock(&st->mutex);
598
-}
599
-
600
-int rrd_stats_dimension_hide(RRD_STATS *st, const char *id)
601
-{
602
- RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
603
- if(!rd) {
604
- error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
605
- return 1;
606
- }
607
-
608
- rd->hidden = 1;
609
- return 0;
610
-}
611
-
612
-
613
-int rrd_stats_dimension_set(RRD_STATS *st, const char *id, void *data)
614
-{
615
- RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
616
- if(!rd) {
617
- error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
618
- return 1;
619
- }
620
-
621
- rd->last_updated = st->last_updated;
622
-
623
- if(rd->bytes == sizeof(long long)) {
624
- long long *dimension = rd->values, *value = data;
625
-
626
- dimension[st->current_entry] = (*value);
627
- }
628
- else if(rd->bytes == sizeof(long)) {
629
- long *dimension = rd->values, *value = data;
630
-
631
- dimension[st->current_entry] = (*value);
632
- }
633
- else if(rd->bytes == sizeof(int)) {
634
- int *dimension = rd->values, *value = data;
635
-
636
- dimension[st->current_entry] = (*value);
637
- }
638
- else if(rd->bytes == sizeof(char)) {
639
- char *dimension = rd->values, *value = data;
640
-
641
- dimension[st->current_entry] = (*value);
642
- }
643
- else fatal("I don't know how to handle data of length %d bytes.", rd->bytes);
644
-
645
- return 0;
646
-}
647
-
648
-#define GROUP_AVERAGE 0
649
-#define GROUP_MAX 1
650
-
651
-size_t rrd_stats_one_json(RRD_STATS *st, char *options, char *buffer, size_t len)
652
-{
653
- size_t i = 0;
654
- if(i + 200 > len) return(0);
655
-
656
- i += sprintf(&buffer[i], "\t\t{\n");
657
- i += sprintf(&buffer[i], "\t\t\t\"id\" : \"%s\",\n", st->id);
658
- i += sprintf(&buffer[i], "\t\t\t\"name\" : \"%s\",\n", st->name);
659
- i += sprintf(&buffer[i], "\t\t\t\"type\" : \"%s\",\n", st->type);
660
- i += sprintf(&buffer[i], "\t\t\t\"title\" : \"%s %s\",\n", st->title, st->name);
661
- i += sprintf(&buffer[i], "\t\t\t\"usertitle\" : \"%s %s (%s)\",\n", st->title, st->usertitle, st->id);
662
- i += sprintf(&buffer[i], "\t\t\t\"userpriority\" : \"%s\",\n", st->userpriority);
663
- i += sprintf(&buffer[i], "\t\t\t\"envtitle\" : \"%s\",\n", st->envtitle);
664
- i += sprintf(&buffer[i], "\t\t\t\"envpriority\" : \"%s\",\n", st->envpriority);
665
- i += sprintf(&buffer[i], "\t\t\t\"hostname\" : \"%s\",\n", st->hostname);
666
- i += sprintf(&buffer[i], "\t\t\t\"vtitle\" : \"%s\",\n", st->vtitle);
667
- i += sprintf(&buffer[i], "\t\t\t\"url\" : \"/data/%s/%s\",\n", st->name, options?options:"");
668
- i += sprintf(&buffer[i], "\t\t\t\"entries\" : %ld,\n", st->entries);
669
- i += sprintf(&buffer[i], "\t\t\t\"current\" : %ld,\n", st->current_entry);
670
- i += sprintf(&buffer[i], "\t\t\t\"update_every\" : %d,\n", update_every);
671
- i += sprintf(&buffer[i], "\t\t\t\"last_updated\" : %lu,\n", st->last_updated);
672
- i += sprintf(&buffer[i], "\t\t\t\"last_updated_secs_ago\" : %lu\n", time(NULL) - st->last_updated);
673
- i += sprintf(&buffer[i], "\t\t}");
674
-
675
- return(i);
676
-}
677
-
678
-#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
679
-#define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n"
680
-
681
-size_t rrd_stats_graph_json(RRD_STATS *st, char * options, char *buffer, size_t len)
682
-{
683
- size_t i = 0;
684
- i += sprintf(&buffer[i], RRD_GRAPH_JSON_HEADER);
685
- i += rrd_stats_one_json(st, options, &buffer[i], len - i);
686
- i += sprintf(&buffer[i], RRD_GRAPH_JSON_FOOTER);
687
- return(i);
688
-}
689
-
690
-size_t rrd_stats_all_json(char *buffer, size_t len)
691
-{
692
- size_t i = 0, c;
693
- RRD_STATS *st;
694
-
695
- i += sprintf(&buffer[i], RRD_GRAPH_JSON_HEADER);
696
-
697
- for(st = root, c = 0; st ; st = st->next, c++) {
698
- if(c) i += sprintf(&buffer[i], "%s", ",\n");
699
- i += rrd_stats_one_json(st, NULL, &buffer[i], len - i);
700
- }
701
-
702
- i += sprintf(&buffer[i], RRD_GRAPH_JSON_FOOTER);
703
- return(i);
704
-}
705
-
706
-long long rrd_stats_dimension_get(RRD_DIMENSION *rd, size_t position)
707
-{
708
- if(rd->bytes == sizeof(long long)) {
709
- long long *dimension = rd->values;
710
- return(dimension[position]);
711
- }
712
- else if(rd->bytes == sizeof(long)) {
713
- long *dimension = rd->values;
714
- return(dimension[position]);
715
- }
716
- else if(rd->bytes == sizeof(int)) {
717
- int *dimension = rd->values;
718
- return(dimension[position]);
719
- }
720
- else if(rd->bytes == sizeof(char)) {
721
- char *dimension = rd->values;
722
- return(dimension[position]);
723
- }
724
- else fatal("Cannot produce JSON for size %d bytes dimension.", rd->bytes);
725
-
726
- return(0);
727
-}
728
-
729
-size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show, size_t group_count, int group_method)
730
-{
731
- pthread_mutex_lock(&st->mutex);
732
-
733
- // check the options
734
- if(entries_to_show <= 0) entries_to_show = 1;
735
- if(group_count <= 0) group_count = 1;
736
- if(group_count > st->entries / 20) group_count = st->entries / 20;
737
-
738
- size_t i = 0; // the bytes of JSON output we have generated so far
739
- size_t printed = 0; // the lines of JSON data we have generated so far
740
-
741
- size_t current_entry = st->current_entry;
742
- size_t t, lt; // t = the current entry, lt = the lest entry of data
743
- long count = st->entries; // count down of the entries examined so far
744
- long pad = 0; // align the entries when grouping values together
745
-
746
- RRD_DIMENSION *rd;
747
- size_t c = 0; // counter for dimension loops
748
- size_t dimensions = 0; // the total number of dimensions present
749
-
750
- unsigned long long usec = 0;// usec between the entries
751
- char dtm[201]; // temp variable for storing dates
752
-
753
- int we_need_totals = 0; // if set, we should calculate totals for all dimensions
754
-
755
- // find how many dimensions we have
756
- for( rd = st->dimensions ; rd ; rd = rd->next) {
757
- dimensions++;
758
- if(rd->type == RRD_DIMENSION_PCENT_OVER_TOTAL) we_need_totals++;
759
- }
760
-
761
- if(!dimensions) {
762
- pthread_mutex_unlock(&st->mutex);
763
- return sprintf(b, "No dimensions yet.");
764
- }
765
-
766
- // temporary storage to keep track of group values and counts
767
- long double group_values[dimensions];
768
- int group_counts[dimensions];
769
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
770
- group_values[c] = group_counts[c] = 0;
771
-
772
- i += sprintf(&b[i], "{\n \"cols\":\n [\n");
773
- i += sprintf(&b[i], " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"datetime\"}");
774
-
775
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
776
- if(!rd->hidden)
777
- i += sprintf(&b[i], ",\n {\"id\":\"\",\"label\":\"%s\",\"pattern\":\"\",\"type\":\"number\"}", rd->name);
778
-
779
- i += sprintf(&b[i], " ],\n \"rows\":\n [\n");
780
-
781
- // to allow grouping on the same values, we need a pad
782
- pad = current_entry % group_count;
783
-
784
- // make sure current_entry is within limits
785
- if(current_entry < 0 || current_entry >= st->entries) current_entry = 0;
786
-
787
- // find the old entry of the round-robin
788
- t = current_entry + 1;
789
- if(t >= st->entries) t = 0;
790
- lt = t;
791
-
792
- // find the current entry
793
- t++;
794
- if(t >= st->entries) t = 0;
795
-
796
- // the loop in dimension data
797
- count -= 2;
798
- for ( ; t != current_entry && count >= 0 ; lt = t++, count--) {
799
- if(t >= st->entries) t = 0;
800
-
801
- // if the last is empty, loop again
802
- if(!st->times[lt].tv_sec) continue;
803
-
804
- // check if we may exceed the buffer provided
805
- if((length - i) < 1024) break;
806
-
807
- // prefer the most recent last entries
808
- if(((count-pad) / group_count) > entries_to_show) continue;
809
-
810
-
811
- // ok. we will use this entry!
812
- // find how much usec since the previous entry
813
-
814
- usec = usecdiff(&st->times[t], &st->times[lt]);
815
-
816
- if(((count-pad) % group_count) == 0) {
817
- if(printed >= entries_to_show) break;
818
-
819
- // generate the local date time
820
- struct tm *tm = localtime(&st->times[t].tv_sec);
821
- if(!tm) { error("localtime() failed."); continue; }
822
-
823
- sprintf(dtm, "\"Date(%d, %d, %d, %d, %d, %d, %d)\"", tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(st->times[t].tv_usec / 1000)); // datetime
824
- // strftime(dtm, 200, "\"Date(%Y, %m, %d, %H, %M, %S)\"", tm); // datetime
825
- // strftime(dtm, 200, "[%H, %M, %S, 0]", tm); // timeofday
826
- i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s}", printed?"]},\n":"", dtm);
827
-
828
- printed++;
829
- }
830
-
831
- // if we need a PCENT_OVER_TOTAL, calculate the totals for the current and the last
832
- long double total = 0, oldtotal = 0;
833
- if(we_need_totals) {
834
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
835
- total += (long double)rrd_stats_dimension_get(rd, t);
836
- oldtotal += (long double)rrd_stats_dimension_get(rd, lt);
837
- }
838
- }
839
-
840
- for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
841
- //long long oldvalue, value; // temp variable for storing data values
842
- long double oldvalue, value; // temp variable for storing data values
843
-
844
- value = (long double)rrd_stats_dimension_get(rd, t);
845
- oldvalue = (long double)rrd_stats_dimension_get(rd, lt);
846
-
847
- switch(rd->type) {
848
- case RRD_DIMENSION_PCENT_OVER_TOTAL:
849
- value = (long double)100 * (value - oldvalue) / (total - oldtotal);
850
- break;
851
-
852
- case RRD_DIMENSION_INCREMENTAL:
853
- if(oldvalue > value) value = 0; // detect overflows and resets
854
- else value -= oldvalue;
855
- value = value * (long double)1000000L / (long double)usec;
856
- break;
857
-
858
- default:
859
- break;
860
- }
861
-
862
- value = value * (long double)rd->multiplier / (long double)rd->divisor;
863
-
864
- group_counts[c]++;
865
- switch(group_method) {
866
- case GROUP_MAX:
867
- if(abs(value) > abs(group_values[c])) group_values[c] = value;
868
- break;
869
-
870
- default:
871
- case GROUP_AVERAGE:
872
- group_values[c] += value;
873
- if(((count - pad) % group_count) == 0) group_values[c] /= (long double)group_counts[c];
874
- break;
875
- }
876
-
877
- if(((count-pad) % group_count) == 0) {
878
- if(!rd->hidden)
879
- i += sprintf(&b[i], ",{\"v\":%0.1Lf}", group_values[c]);
880
- //i += sprintf(&b[i], ",{\"v\":%lld}", group_values[c]);
881
-
882
- group_values[c] = group_counts[c] = 0;
883
- }
884
- }
885
- }
886
- if(printed) i += sprintf(&b[i], "]}");
887
- i += sprintf(&b[i], "\n ]\n}\n");
888
-
889
- pthread_mutex_unlock(&st->mutex);
890
- return(i);
891
-}
892
-
893
-// ----------------------------------------------------------------------------
894
-// listener (web server)
895
-
896
-struct web_buffer {
897
- size_t size; // allocation size of buffer
898
- size_t bytes; // current data length in buffer
899
- size_t sent; // current data length sent to output
900
- char *buffer; // the buffer
901
- int contenttype;
902
- size_t rbytes; // if non-zero, the excepted size of ifd
903
- time_t date; // the date this content has been generated
904
-};
905
-
906
-struct web_buffer *web_buffer_create(size_t size)
907
-{
908
- struct web_buffer *b;
909
-
910
- debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size);
911
-
912
- b = calloc(1, sizeof(struct web_buffer));
913
- if(!b) {
914
- error("Cannot allocate a web_buffer.");
915
- return NULL;
916
- }
917
-
918
- b->buffer = calloc(1, size);
919
- if(!b->buffer) {
920
- error("Cannot allocate a buffer of size %u.", size);
921
- free(b);
922
- return NULL;
923
- }
924
- b->size = size;
925
- b->contenttype = CT_TEXT_PLAIN;
926
- return(b);
927
-}
928
-
929
-void web_buffer_free(struct web_buffer *b)
930
-{
931
- debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size);
932
-
933
- if(b->buffer) free(b->buffer);
934
- free(b);
935
-}
936
-
937
-void web_buffer_increase(struct web_buffer *b, size_t free_size_required)
938
-{
939
- size_t left = b->size - b->bytes;
940
-
941
- if(left >= free_size_required) return;
942
- size_t increase = free_size_required - left;
943
-
944
- debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
945
-
946
- b->buffer = realloc(b->buffer, b->size + free_size_required - left);
947
- if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size, b->size + increase);
948
-
949
- b->size += increase;
950
-}
951
-
952
-#define WEB_CLIENT_MODE_NORMAL 0
953
-#define WEB_CLIENT_MODE_FILECOPY 1
954
-
955
-struct web_client {
956
- unsigned long long id;
957
- char client_ip[101];
958
-
959
- int mode;
960
- int keepalive;
961
-
962
- struct sockaddr_in clientaddr;
963
-
964
- pthread_t thread; // the thread servicing this client
965
- int obsolete; // if set to 1, the listener will remove this client
966
-
967
- int ifd;
968
- int ofd;
969
-
970
- struct web_buffer *data;
971
-
972
- int wait_receive;
973
- int wait_send;
974
-
975
- char response_header[MAX_HTTP_HEADER_SIZE+1];
976
-
977
- struct web_client *prev;
978
- struct web_client *next;
979
-} *web_clients = NULL;
980
-
981
-unsigned long long web_clients_count = 0;
982
-
983
-struct web_client *web_client_create(int listener)
984
-{
985
- struct web_client *w;
986
- socklen_t addrlen;
987
-
988
- w = calloc(1, sizeof(struct web_client));
989
- if(!w) {
990
- error("Cannot allocate new web_client memory.");
991
- return NULL;
992
- }
993
-
994
- w->id = ++web_clients_count;
995
- w->mode = WEB_CLIENT_MODE_NORMAL;
996
-
997
- addrlen = sizeof(w->clientaddr);
998
- w->ifd = accept(listener, (struct sockaddr *)&w->clientaddr, &addrlen);
999
- if (w->ifd == -1) {
1000
- error("%llu: Cannot accept new incoming connection.", w->id);
1001
- free(w);
1002
- return NULL;
1003
- }
1004
- w->ofd = w->ifd;
1005
-
1006
- strncpy(w->client_ip, inet_ntoa(w->clientaddr.sin_addr), 100);
1007
- w->client_ip[100] = '\0';
1008
-
1009
- syslog(LOG_NOTICE, "%llu: New client from %s.", w->id, w->client_ip);
1010
- debug(D_WEB_CLIENT_ACCESS, "%llu: New web client from %s on socket %d.", w->id, w->client_ip, w->ifd);
1011
-
1012
- {
1013
- int flag = 1;
1014
- if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
1015
- }
1016
-
1017
- w->data = web_buffer_create(MAX_SOCKET_DATA_LENGTH);
1018
- if(!w->data) {
1019
- close(w->ifd);
1020
- free(w);
1021
- return NULL;
1022
- }
1023
-
1024
- w->wait_receive = 1;
1025
-
1026
- if(web_clients) web_clients->prev = w;
1027
- w->next = web_clients;
1028
- web_clients = w;
1029
-
1030
- return(w);
1031
-}
1032
-
1033
-struct web_client *web_client_free(struct web_client *w)
1034
-{
1035
- struct web_client *n = w->next;
1036
-
1037
- debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s.", w->id, inet_ntoa(w->clientaddr.sin_addr));
1038
-
1039
- if(w->prev) w->prev->next = w->next;
1040
- if(w->next) w->next->prev = w->prev;
1041
-
1042
- if(w == web_clients) web_clients = w->next;
1043
-
1044
- if(w->data) web_buffer_free(w->data);
1045
- close(w->ifd);
1046
- if(w->ofd != w->ifd) close(w->ofd);
1047
- free(w);
1048
-
1049
- return(n);
1050
-}
1051
-
1052
-int mysendfile(struct web_client *w, char *filename)
1053
-{
1054
- debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
1055
-
1056
- // skip leading slashes
1057
- while (filename[0] == '/') filename = &filename[1];
1058
-
1059
- // if the filename contain known paths, skip them
1060
- if(strncmp(filename, WEB_PATH_DATA "/", strlen(WEB_PATH_DATA) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATA) + 1];
1061
- else if(strncmp(filename, WEB_PATH_GRAPH "/", strlen(WEB_PATH_GRAPH) + 1) == 0) filename = &filename[strlen(WEB_PATH_GRAPH) + 1];
1062
- else if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
1063
-
1064
- // if the filename contains a / or a .., refuse to serve it
1065
- if(strstr(filename, "/") != 0 || strstr(filename, "..") != 0) {
1066
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
1067
- w->data->bytes = sprintf(w->data->buffer, "File '%s' cannot be served. Filenames cannot contain / or ..", filename);
1068
- return 400;
1069
- }
1070
-
1071
- // access the file in web/*
1072
- char webfilename[FILENAME_MAX + 1];
1073
- strcpy(webfilename, "web/");
1074
- strncpy(&webfilename[4], filename, FILENAME_MAX - 4);
1075
- webfilename[FILENAME_MAX] = '\0';
1076
-
1077
- // check if the file exists
1078
- struct stat stat;
1079
- if(lstat(webfilename, &stat) != 0) {
1080
- debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, filename);
1081
- w->data->bytes = sprintf(w->data->buffer, "File '%s' does not exist, or is not accessible.", filename);
1082
- return 404;
1083
- }
1084
-
1085
- // check if the file is owned by us
1086
- if(stat.st_uid != getuid() && stat.st_uid != geteuid()) {
1087
- error("%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, filename, stat.st_uid, getuid());
1088
- w->data->bytes = sprintf(w->data->buffer, "Access to file '%s' is not permitted.", filename);
1089
- return 403;
1090
- }
1091
-
1092
- // open the file
1093
- w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
1094
- if(w->ifd == -1) {
1095
- w->ifd = w->ofd;
1096
-
1097
- if(errno == EBUSY || errno == EAGAIN) {
1098
- error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, filename);
1099
- sprintf(w->response_header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
1100
- w->data->bytes = sprintf(w->data->buffer, "The file '%s' is currently busy. Please try again later.", filename);
1101
- return 307;
1102
- }
1103
- else {
1104
- error("%llu: Cannot open file '%s'.", w->id, filename);
1105
- w->data->bytes = sprintf(w->data->buffer, "Cannot open file '%s'.", filename);
1106
- return 404;
1107
- }
1108
- }
1109
-
1110
- syslog(LOG_NOTICE, "%llu: Sending file '%s' to client %s.", w->id, filename, w->client_ip);
1111
-
1112
- // pick a Content-Type for the file
1113
- if(strstr(filename, ".html") != NULL) w->data->contenttype = CT_TEXT_HTML;
1114
- else if(strstr(filename, ".js") != NULL) w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1115
- else if(strstr(filename, ".css") != NULL) w->data->contenttype = CT_TEXT_CSS;
1116
- else if(strstr(filename, ".xml") != NULL) w->data->contenttype = CT_TEXT_XML;
1117
- else if(strstr(filename, ".xsl") != NULL) w->data->contenttype = CT_TEXT_XSL;
1118
- else if(strstr(filename, ".txt") != NULL) w->data->contenttype = CT_TEXT_PLAIN;
1119
- else if(strstr(filename, ".svg") != NULL) w->data->contenttype = CT_IMAGE_SVG_XML;
1120
- else if(strstr(filename, ".ttf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
1121
- else if(strstr(filename, ".otf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
1122
- else if(strstr(filename, ".woff") != NULL) w->data->contenttype = CT_APPLICATION_FONT_WOFF;
1123
- else if(strstr(filename, ".eot") != NULL) w->data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
1124
- else w->data->contenttype = CT_APPLICATION_OCTET_STREAM;
1125
-
1126
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, filename, stat.st_size, w->ifd, w->ofd);
1127
-
1128
- w->mode = WEB_CLIENT_MODE_FILECOPY;
1129
- w->wait_receive = 1;
1130
- w->wait_send = 0;
1131
- w->data->bytes = 0;
1132
- w->data->buffer[0] = '\0';
1133
- w->data->rbytes = stat.st_size;
1134
- w->data->date = stat.st_mtim.tv_sec;
1135
-
1136
- return 200;
1137
-}
1138
-
1139
-char *mystrsep(char **ptr, char *s)
1140
-{
1141
- char *p = "";
1142
- while ( !p[0] && *ptr ) p = strsep(ptr, s);
1143
- return(p);
1144
-}
1145
-
1146
-void web_client_reset(struct web_client *w)
1147
-{
1148
- if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1149
- close(w->ifd);
1150
- w->ifd = w->ofd;
1151
- }
1152
-
1153
- w->data->contenttype = CT_TEXT_PLAIN;
1154
- w->mode = WEB_CLIENT_MODE_NORMAL;
1155
-
1156
- w->data->rbytes = 0;
1157
- w->data->bytes = 0;
1158
- w->data->sent = 0;
1159
-
1160
- w->response_header[0] = '\0';
1161
- w->data->buffer[0] = '\0';
1162
-
1163
- w->wait_receive = 1;
1164
- w->wait_send = 0;
1165
-}
1166
-
1167
-void web_client_process(struct web_client *w)
1168
-{
1169
- int code = 500;
1170
- int bytes;
1171
-
1172
- // check if we have an empty line (end of HTTP header)
1173
- if(strstr(w->data->buffer, "\r\n\r\n")) {
1174
- debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer);
1175
-
1176
- // check if the client requested keep-alive HTTP
1177
- if(strcasestr(w->data->buffer, "Connection: keep-alive") == 0) w->keepalive = 0;
1178
- else w->keepalive = 1;
1179
-
1180
- char *buf = w->data->buffer;
1181
- char *tok = strsep(&buf, " \r\n");
1182
- char *url = NULL;
1183
-
1184
- if(buf && strcmp(tok, "GET") == 0) {
1185
- tok = strsep(&buf, " \r\n");
1186
- url = url_decode(tok);
1187
- debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
1188
- }
1189
- else if (buf && strcmp(tok, "POST") == 0) {
1190
- w->keepalive = 0;
1191
- tok = strsep(&buf, " \r\n");
1192
- url = url_decode(tok);
1193
-
1194
- 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);
1195
- }
1196
-
1197
- if(url) {
1198
- tok = mystrsep(&url, "/?&");
1199
-
1200
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
1201
-
1202
- if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
1203
- // the client is requesting rrd data
1204
-
1205
- // get the name of the data to show
1206
- tok = mystrsep(&url, "/?&");
1207
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1208
-
1209
- // do we have such a data set?
1210
- RRD_STATS *st = rrd_stats_find_byname(tok);
1211
- if(!st) st = rrd_stats_find(tok);
1212
- if(!st) {
1213
- // we don't have it
1214
- // try to send a file with that name
1215
- code = mysendfile(w, tok);
1216
- }
1217
- else {
1218
- // we have it
1219
- debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
1220
-
1221
- // how many entries does the client want?
1222
- size_t lines = save_history;
1223
- size_t group_count = 1;
1224
- int group_method = GROUP_AVERAGE;
1225
-
1226
- if(url) {
1227
- // parse the lines required
1228
- tok = mystrsep(&url, "/?&");
1229
- if(tok) lines = atoi(tok);
1230
- if(lines < 5) lines = save_history;
1231
- }
1232
- if(url) {
1233
- // parse the group count required
1234
- tok = mystrsep(&url, "/?&");
1235
- if(tok) group_count = atoi(tok);
1236
- if(group_count < 1) group_count = 1;
1237
- if(group_count > save_history / 20) group_count = save_history / 20;
1238
- }
1239
- if(url) {
1240
- // parse the grouping method required
1241
- tok = mystrsep(&url, "/?&");
1242
- if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
1243
- else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
1244
- else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
1245
- }
1246
-
1247
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group_count, %d group_method).", w->id, st->name, st->id, lines, group_count, group_method);
1248
-
1249
- code = 200;
1250
- w->data->contenttype = CT_APPLICATION_JSON;
1251
- w->data->bytes = rrd_stats_json(st, w->data->buffer, w->data->size, lines, group_count, group_method);
1252
- }
1253
- }
1254
- else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
1255
- // the client is requesting an rrd graph
1256
-
1257
- // get the name of the data to show
1258
- tok = mystrsep(&url, "/?&");
1259
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1260
-
1261
- // do we have such a data set?
1262
- RRD_STATS *st = rrd_stats_find_byname(tok);
1263
- if(!st) {
1264
- // we don't have it
1265
- // try to send a file with that name
1266
- code = mysendfile(w, tok);
1267
- }
1268
- else {
1269
- code = 200;
1270
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
1271
- w->data->contenttype = CT_APPLICATION_JSON;
1272
- w->data->bytes = rrd_stats_graph_json(st, url, w->data->buffer, w->data->size);
1273
- }
1274
- }
1275
- else if(strcmp(tok, "mirror") == 0) {
1276
- code = 200;
1277
-
1278
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
1279
-
1280
- // just leave the buffer as is
1281
- // it will be copied back to the client
1282
- }
1283
- else if(strcmp(tok, "list") == 0) {
1284
- code = 200;
1285
-
1286
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
1287
-
1288
- w->data->bytes = 0;
1289
- RRD_STATS *st = root;
1290
-
1291
- for ( ; st ; st = st->next )
1292
- w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s\n", st->name);
1293
- }
1294
- else if(strcmp(tok, "envlist") == 0) {
1295
- code = 200;
1296
-
1297
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending envlist of RRD_STATS...", w->id);
1298
-
1299
- w->data->bytes = 0;
1300
- RRD_STATS *st = root;
1301
-
1302
- for ( ; st ; st = st->next ) {
1303
- w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s=%s\n", st->envtitle, st->usertitle);
1304
- w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s=%s\n", st->envpriority, st->userpriority);
1305
- }
1306
- }
1307
- else if(strcmp(tok, "all.json") == 0) {
1308
- code = 200;
1309
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending all.json of RRD_STATS...", w->id);
1310
- w->data->contenttype = CT_APPLICATION_JSON;
1311
- w->data->bytes = rrd_stats_all_json(w->data->buffer, w->data->size);
1312
- }
1313
- else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file"
1314
- tok = mystrsep(&url, "/?&");
1315
- if(tok && *tok) code = mysendfile(w, tok);
1316
- else {
1317
- code = 400;
1318
- strcpy(w->data->buffer, "You have to give a filename to get.\r\n");
1319
- w->data->bytes = strlen(w->data->buffer);
1320
- }
1321
- }
1322
- else if(!tok[0]) {
1323
- code = mysendfile(w, "index.html");
1324
- }
1325
- else {
1326
- code = mysendfile(w, tok);
1327
- }
1328
- }
1329
- else {
1330
- if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
1331
-
1332
- code = 500;
1333
- strcpy(w->data->buffer, "I don't understand you...\r\n");
1334
- w->data->bytes = strlen(w->data->buffer);
1335
- }
1336
- }
1337
- else if(w->data->bytes > 8192) {
1338
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big.", w->id);
1339
-
1340
- code = 400;
1341
- strcpy(w->data->buffer, "Received request is too big.\r\n");
1342
- w->data->bytes = strlen(w->data->buffer);
1343
- }
1344
- else {
1345
- // wait for more data
1346
- return;
1347
- }
1348
-
1349
- w->data->date = time(NULL);
1350
- w->wait_receive = 0;
1351
- w->data->sent = 0;
1352
-
1353
- // prepare the HTTP response header
1354
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
1355
-
1356
- char *content_type_string = "";
1357
- switch(w->data->contenttype) {
1358
- case CT_TEXT_HTML:
1359
- content_type_string = "text/html";
1360
- break;
1361
-
1362
- case CT_APPLICATION_XML:
1363
- content_type_string = "application/xml";
1364
- break;
1365
-
1366
- case CT_APPLICATION_JSON:
1367
- content_type_string = "application/json";
1368
- break;
1369
-
1370
- case CT_APPLICATION_X_JAVASCRIPT:
1371
- content_type_string = "application/x-javascript";
1372
- break;
1373
-
1374
- case CT_TEXT_CSS:
1375
- content_type_string = "text/css";
1376
- break;
1377
-
1378
- case CT_TEXT_XML:
1379
- content_type_string = "text/xml";
1380
- break;
1381
-
1382
- case CT_TEXT_XSL:
1383
- content_type_string = "text/xsl";
1384
- break;
1385
-
1386
- case CT_APPLICATION_OCTET_STREAM:
1387
- content_type_string = "application/octet-stream";
1388
- break;
1389
-
1390
- case CT_IMAGE_SVG_XML:
1391
- content_type_string = "image/svg+xml";
1392
- break;
1393
-
1394
- case CT_APPLICATION_X_FONT_TRUETYPE:
1395
- content_type_string = "application/x-font-truetype";
1396
- break;
1397
-
1398
- case CT_APPLICATION_X_FONT_OPENTYPE:
1399
- content_type_string = "application/x-font-opentype";
1400
- break;
1401
-
1402
- case CT_APPLICATION_FONT_WOFF:
1403
- content_type_string = "application/font-woff";
1404
- break;
1405
-
1406
- case CT_APPLICATION_VND_MS_FONTOBJ:
1407
- content_type_string = "application/vnd.ms-fontobject";
1408
- break;
1409
-
1410
- default:
1411
- case CT_TEXT_PLAIN:
1412
- content_type_string = "text/plain";
1413
- break;
1414
- }
1415
-
1416
- char *code_msg = "";
1417
- switch(code) {
1418
- case 200:
1419
- code_msg = "OK";
1420
- break;
1421
-
1422
- case 307:
1423
- code_msg = "Temporary Redirect";
1424
- break;
1425
-
1426
- case 400:
1427
- code_msg = "Bad Request";
1428
- break;
1429
-
1430
- case 403:
1431
- code_msg = "Forbidden";
1432
- break;
1433
-
1434
- case 404:
1435
- code_msg = "Not Found";
1436
- break;
1437
-
1438
- default:
1439
- code_msg = "Internal Server Error";
1440
- break;
1441
- }
1442
-
1443
- char date[100];
1444
- struct tm tm = *gmtime(&w->data->date);
1445
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm);
1446
-
1447
- char custom_header[MAX_HTTP_HEADER_SIZE + 1] = "";
1448
- if(w->response_header[0])
1449
- strcpy(custom_header, w->response_header);
1450
-
1451
- size_t headerlen = 0;
1452
- headerlen += sprintf(&w->response_header[headerlen],
1453
- "HTTP/1.1 %d %s\r\n"
1454
- "Connection: %s\r\n"
1455
- "Server: Data Collector HTTP Server\r\n"
1456
- "Content-Type: %s\r\n"
1457
- "Access-Control-Allow-Origin: *\r\n"
1458
- "Date: %s\r\n"
1459
- , code, code_msg
1460
- , w->keepalive?"keep-alive":"close"
1461
- , content_type_string
1462
- , date
1463
- );
1464
-
1465
- if(custom_header[0])
1466
- headerlen += sprintf(&w->response_header[headerlen], "%s", custom_header);
1467
-
1468
- if(w->mode == WEB_CLIENT_MODE_NORMAL) {
1469
- headerlen += sprintf(&w->response_header[headerlen],
1470
- "Expires: %s\r\n"
1471
- "Cache-Control: private\r\n"
1472
- , date
1473
- );
1474
- }
1475
-
1476
- // if we know the content length, put it
1477
- if(w->data->bytes || w->data->rbytes)
1478
- headerlen += sprintf(&w->response_header[headerlen],
1479
- "Content-Length: %ld\r\n"
1480
- , w->data->bytes?w->data->bytes:w->data->rbytes
1481
- );
1482
- else w->keepalive = 0; // content-length is required for keep-alive
1483
-
1484
- headerlen += sprintf(&w->response_header[headerlen], "\r\n");
1485
-
1486
- // disable TCP_NODELAY, to buffer the header
1487
- int flag = 0;
1488
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
1489
-
1490
- // sent the HTTP header
1491
- debug(D_WEB_CLIENT, "%llu: Sending response HTTP header of size %d.", w->id, headerlen);
1492
-
1493
- bytes = send(w->ofd, w->response_header, headerlen, 0);
1494
- if(bytes != headerlen)
1495
- error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes);
1496
-
1497
- // enable TCP_NODELAY, to send all data immediately at the next send()
1498
- flag = 1;
1499
- if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
1500
-
1501
- // enable sending immediately if we have data
1502
- if(w->data->bytes) w->wait_send = 1;
1503
- else w->wait_send = 0;
1504
-
1505
- // pretty logging
1506
- switch(w->mode) {
1507
- case WEB_CLIENT_MODE_NORMAL:
1508
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->bytes);
1509
- break;
1510
-
1511
- case WEB_CLIENT_MODE_FILECOPY:
1512
- if(w->data->rbytes) {
1513
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rbytes);
1514
-
1515
- // utilize the kernel sendfile() for copying the file to the socket.
1516
- // this block of code can be commented, without anything missing.
1517
- // when it is commented, the program will copy the data using async I/O.
1518
- {
1519
- ssize_t len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes);
1520
- 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);
1521
- else web_client_reset(w);
1522
- }
1523
- }
1524
- else
1525
- debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
1526
- break;
1527
-
1528
- default:
1529
- fatal("%llu: Unknown client mode %d.", w->id, w->mode);
1530
- break;
1531
- }
1532
-}
1533
-
1534
-ssize_t web_client_send(struct web_client *w)
1535
-{
1536
- ssize_t bytes;
1537
-
1538
- if(w->data->bytes - w->data->sent == 0) {
1539
- // there is nothing to send
1540
-
1541
- debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
1542
-
1543
- // there can be two cases for this
1544
- // A. we have done everything
1545
- // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
1546
-
1547
- if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rbytes && w->data->rbytes > w->data->bytes) {
1548
- // we have to wait, more data will come
1549
- debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
1550
- w->wait_send = 0;
1551
- return(0);
1552
- }
1553
-
1554
- if(w->keepalive == 0) {
1555
- debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent);
1556
- errno = 0;
1557
- return(-1);
1558
- }
1559
-
1560
- web_client_reset(w);
1561
- debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
1562
- return(0);
1563
- }
1564
-
1565
- bytes = send(w->ofd, &w->data->buffer[w->data->sent], w->data->bytes - w->data->sent, MSG_DONTWAIT);
1566
- if(bytes > 0) {
1567
- w->data->sent += bytes;
1568
- debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
1569
- }
1570
- else if(bytes == 0) debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
1571
-
1572
- return(bytes);
1573
-}
1574
-
1575
-ssize_t web_client_receive(struct web_client *w)
1576
-{
1577
- // do we have any space for more data?
1578
- web_buffer_increase(w->data, SOCKET_DATA_LENGTH_INCREASE_STEP);
1579
-
1580
- ssize_t left = w->data->size - w->data->bytes;
1581
- ssize_t bytes;
1582
-
1583
- if(w->mode == WEB_CLIENT_MODE_FILECOPY)
1584
- bytes = read(w->ifd, &w->data->buffer[w->data->bytes], (left-1));
1585
- else
1586
- bytes = recv(w->ifd, &w->data->buffer[w->data->bytes], left-1, MSG_DONTWAIT);
1587
-
1588
- if(bytes > 0) {
1589
- int old = w->data->bytes;
1590
- w->data->bytes += bytes;
1591
- w->data->buffer[w->data->bytes] = '\0';
1592
-
1593
- debug(D_WEB_CLIENT, "%llu: Received %d bytes.", w->id, bytes);
1594
- debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->data->buffer[old]);
1595
-
1596
- if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1597
- w->wait_send = 1;
1598
- if(w->data->rbytes && w->data->bytes >= w->data->rbytes) w->wait_receive = 0;
1599
- }
1600
- }
1601
- else if(bytes == 0) {
1602
- debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
1603
-
1604
- // if we cannot read, it means we have an error on input.
1605
- // if however, we are copying a file from ifd to ofd, we should not return an error.
1606
- // in this case, the error should be generated when the file has been sent to the client.
1607
-
1608
- if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1609
- // we are copying data fron ifd to ofd
1610
- // let it finish copying...
1611
- w->wait_receive = 0;
1612
- debug(D_WEB_CLIENT, "%llu: Disabling input.", w->id);
1613
- }
1614
- else {
1615
- bytes = -1;
1616
- errno = 0;
1617
- }
1618
- }
1619
-
1620
- return(bytes);
1621
-}
1622
-
1623
-
1624
-// --------------------------------------------------------------------------------------
1625
-// the thread of a single client
1626
-
1627
-// 1. waits for input and output, using async I/O
1628
-// 2. it processes HTTP requests
1629
-// 3. it generates HTTP responses
1630
-// 4. it copies data from input to output if mode is FILECOPY
1631
-
1632
-void *new_client(void *ptr)
1633
-{
1634
- struct timeval tv;
1635
- struct web_client *w = ptr;
1636
- int retval;
1637
- fd_set ifds, ofds, efds;
1638
- int fdmax = 0;
1639
-
1640
- for(;;) {
1641
- FD_ZERO (&ifds);
1642
- FD_ZERO (&ofds);
1643
- FD_ZERO (&efds);
1644
-
1645
- FD_SET(w->ifd, &efds);
1646
- if(w->ifd != w->ofd) FD_SET(w->ofd, &efds);
1647
- if (w->wait_receive) {
1648
- debug(D_WEB_CLIENT, "%llu: Waiting for input (fd %d).", w->id, w->ifd);
1649
- FD_SET(w->ifd, &ifds);
1650
- if(w->ifd > fdmax) fdmax = w->ifd;
1651
- }
1652
- if (w->wait_send) {
1653
- debug(D_WEB_CLIENT, "%llu: Waiting for output (fd %d).", w->id, w->ofd);
1654
- FD_SET(w->ofd, &ofds);
1655
- if(w->ofd > fdmax) fdmax = w->ofd;
1656
- }
1657
-
1658
- tv.tv_sec = 30;
1659
- tv.tv_usec = 0;
1660
-
1661
- debug(D_WEB_CLIENT, "%llu: Waiting...", w->id);
1662
- retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1663
-
1664
- if(retval == -1) {
1665
- error("%llu: LISTENER: select() failed.", w->id);
1666
- continue;
1667
- }
1668
- else if(!retval) {
1669
- // timeout
1670
- w->obsolete = 1;
1671
- return NULL;
1672
- }
1673
-
1674
- if(FD_ISSET(w->ifd, &efds)) {
1675
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on input socket (%s).", w->id, strerror(errno));
1676
- w->obsolete = 1;
1677
- return NULL;
1678
- }
1679
-
1680
- if(FD_ISSET(w->ofd, &efds)) {
1681
- debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on output socket (%s).", w->id, strerror(errno));
1682
- w->obsolete = 1;
1683
- return NULL;
1684
- }
1685
-
1686
- if(w->wait_send && FD_ISSET(w->ofd, &ofds)) {
1687
- if(web_client_send(w) < 0) {
1688
- debug(D_WEB_CLIENT, "%llu: Closing client (input: %s).", w->id, strerror(errno));
1689
- errno = 0;
1690
- w->obsolete = 1;
1691
- return NULL;
1692
- }
1693
- }
1694
-
1695
- if(w->wait_receive && FD_ISSET(w->ifd, &ifds)) {
1696
- if(web_client_receive(w) < 0) {
1697
- debug(D_WEB_CLIENT, "%llu: Closing client (output: %s).", w->id, strerror(errno));
1698
- errno = 0;
1699
- w->obsolete = 1;
1700
- return NULL;
1701
- }
1702
-
1703
- if(w->mode == WEB_CLIENT_MODE_NORMAL) web_client_process(w);
1704
- }
1705
- }
1706
- debug(D_WEB_CLIENT, "%llu: done...", w->id);
1707
-
1708
- return NULL;
1709
-}
1710
-
1711
-// --------------------------------------------------------------------------------------
1712
-// the main socket listener
1713
-
1714
-// 1. it accepts new incoming requests on our port
1715
-// 2. creates a new web_client for each connection received
1716
-// 3. spawns a new pthread to serve the client (this is optimal for keep-alive clients)
1717
-// 4. cleans up old web_clients that their pthreads have been exited
1718
-
1719
-void *socket_listen_main(void *ptr)
1720
-{
1721
- struct web_client *w;
1722
- struct timeval tv;
1723
- int retval;
1724
-
1725
- int listener = create_listen_socket(listen_port);
1726
- if(listener == -1) fatal("LISTENER: Cannot create listening socket on port 19999.");
1727
-
1728
- fd_set ifds, ofds, efds;
1729
- int fdmax = listener;
1730
-
1731
- FD_ZERO (&ifds);
1732
- FD_ZERO (&ofds);
1733
- FD_ZERO (&efds);
1734
-
1735
- for(;;) {
1736
- tv.tv_sec = 1;
1737
- tv.tv_usec = 0;
1738
-
1739
- FD_SET(listener, &ifds);
1740
- FD_SET(listener, &efds);
1741
-
1742
- // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
1743
- retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1744
-
1745
- if(retval == -1) {
1746
- error("LISTENER: select() failed.");
1747
- continue;
1748
- }
1749
- else if(retval) {
1750
- // check for new incoming connections
1751
- if(FD_ISSET(listener, &ifds)) {
1752
- w = web_client_create(listener);
1753
-
1754
- if(pthread_create(&w->thread, NULL, new_client, w) != 0)
1755
- error("%llu: failed to create new thread.");
1756
-
1757
- if(pthread_detach(w->thread) != 0)
1758
- error("%llu: Cannot request detach of newly created thread.", w->id);
1759
- }
1760
- else debug(D_WEB_CLIENT, "LISTENER: select() didn't do anything.");
1761
- }
1762
- else debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
1763
-
1764
- // cleanup unused clients
1765
- for(w = web_clients; w ; w = w?w->next:NULL) {
1766
- if(w->obsolete) {
1767
- debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
1768
- w = web_client_free(w);
1769
- }
1770
- }
1771
- }
1772
-
1773
- error("LISTENER: exit!");
1774
-
1775
- close(listener);
1776
- exit(2);
1777
-
1778
- return NULL;
1779
-}
1780
-
1781
-// ----------------------------------------------------------------------------
1782
-// /proc/net/dev processor
1783
-
1784
-int do_proc_net_dev() {
1785
- char buffer[MAX_PROC_NET_DEV_LINE+1] = "";
1786
- char name[MAX_PROC_NET_DEV_IFACE_NAME + 1] = RRD_TYPE_NET ".";
1787
- unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
1788
- unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
1789
-
1790
- int r;
1791
- char *p;
1792
-
1793
- FILE *fp = fopen("/proc/net/dev", "r");
1794
- if(!fp) {
1795
- error("Cannot read /proc/net/dev.");
1796
- return 1;
1797
- }
1798
-
1799
- // skip the first two lines
1800
- p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1801
- p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1802
-
1803
- // read the rest of the lines
1804
- for(;1;) {
1805
- char *c;
1806
- p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1807
- if(!p) break;
1808
-
1809
- c = strchr(buffer, ':');
1810
- if(c) *c = '\t';
1811
-
1812
- // if(DEBUG) printf("%s\n", buffer);
1813
- r = sscanf(buffer, "%s\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\n",
1814
- &name[RRD_TYPE_NET_LEN + 1],
1815
- &rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,
1816
- &tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);
1817
- if(r == EOF) break;
1818
- if(r != 17) error("Cannot read /proc/net/dev line. Expected 17 params, read %d.", r);
1819
- else {
1820
- RRD_STATS *st = rrd_stats_find(name);
1821
-
1822
- if(!st) {
1823
- st = rrd_stats_create(name, name, save_history, "Network usage for ", "Bandwidth in kilobits/s", RRD_TYPE_NET);
1824
- if(!st) {
1825
- error("Cannot create RRD_STATS for interface %s.", name);
1826
- continue;
1827
- }
1828
-
1829
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -8, 1024, RRD_DIMENSION_INCREMENTAL))
1830
- error("Cannot add RRD_STATS dimension %s.", "sent");
1831
-
1832
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 8, 1024, RRD_DIMENSION_INCREMENTAL))
1833
- error("Cannot add RRD_STATS dimension %s.", "received");
1834
-
1835
- }
1836
- else rrd_stats_next(st);
1837
-
1838
- rrd_stats_dimension_set(st, "received", &rbytes);
1839
- rrd_stats_dimension_set(st, "sent", &tbytes);
1840
- rrd_stats_done(st);
1841
- }
1842
- }
1843
-
1844
- fclose(fp);
1845
- return 0;
1846
-}
1847
-
1848
-// ----------------------------------------------------------------------------
1849
-// /proc/diskstats processor
1850
-
1851
-int do_proc_diskstats() {
1852
- char buffer[MAX_PROC_DISKSTATS_LINE+1] = "";
1853
- char name[MAX_PROC_DISKSTATS_DISK_NAME + 1] = RRD_TYPE_DISK ".";
1854
- // 1 2 3 4 5 6 7 8 9 10 11
1855
- unsigned long long major, minor, reads, reads_merged, readsectors, readms, writes, writes_merged, writesectors, writems, currentios, iosms, wiosms;
1856
-
1857
- int r;
1858
- char *p;
1859
-
1860
- FILE *fp = fopen("/proc/diskstats", "r");
1861
- if(!fp) {
1862
- error("Cannot read /proc/diskstats.");
1863
- return 1;
1864
- }
1865
-
1866
- for(;1;) {
1867
- p = fgets(buffer, MAX_PROC_DISKSTATS_LINE, fp);
1868
- if(!p) break;
1869
-
1870
- // if(DEBUG) printf("%s\n", buffer);
1871
- r = sscanf(buffer, "%llu %llu %s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
1872
- &major, &minor, &name[RRD_TYPE_DISK_LEN + 1],
1873
- &reads, &reads_merged, &readsectors, &readms, &writes, &writes_merged, &writesectors, &writems, ¤tios, &iosms, &wiosms
1874
- );
1875
- if(r == EOF) break;
1876
- if(r != 14) error("Cannot read /proc/diskstats line. Expected 14 params, read %d.", r);
1877
- else {
1878
- switch(major) {
1879
- case 3: // ide
1880
- case 13: // 8bit ide
1881
- case 22: // ide
1882
- case 33: // ide
1883
- case 34: // ide
1884
- case 56: // ide
1885
- case 57: // ide
1886
- case 88: // ide
1887
- case 89: // ide
1888
- case 90: // ide
1889
- case 91: // ide
1890
- if(minor % 64) continue; // partitions
1891
- break;
1892
-
1893
- case 160: // raid
1894
- case 161: // raid
1895
- if(minor % 32) continue; // partitions
1896
- break;
1897
-
1898
- case 8: // scsi disks
1899
- case 65: // scsi disks
1900
- case 66: // scsi disks
1901
- case 67: // scsi disks
1902
- case 68: // scsi disks
1903
- case 69: // scsi disks
1904
- case 70: // scsi disks
1905
- case 71: // scsi disks
1906
- case 72: // scsi disks
1907
- case 73: // scsi disks
1908
- case 74: // scsi disks
1909
- case 75: // scsi disks
1910
- case 76: // scsi disks
1911
- case 77: // scsi disks
1912
- case 78: // scsi disks
1913
- case 79: // scsi disks
1914
- case 80: // i2o
1915
- case 81: // i2o
1916
- case 82: // i2o
1917
- case 83: // i2o
1918
- case 84: // i2o
1919
- case 85: // i2o
1920
- case 86: // i2o
1921
- case 87: // i2o
1922
- case 101: // hyperdisk
1923
- case 102: // compressed
1924
- case 104: // scsi
1925
- case 105: // scsi
1926
- case 106: // scsi
1927
- case 107: // scsi
1928
- case 108: // scsi
1929
- case 109: // scsi
1930
- case 110: // scsi
1931
- case 111: // scsi
1932
- case 114: // bios raid
1933
- case 116: // ram board
1934
- case 128: // scsi
1935
- case 129: // scsi
1936
- case 130: // scsi
1937
- case 131: // scsi
1938
- case 132: // scsi
1939
- case 133: // scsi
1940
- case 134: // scsi
1941
- case 135: // scsi
1942
- case 153: // raid
1943
- case 202: // xen
1944
- case 256: // flash
1945
- case 257: // flash
1946
- if(minor % 16) continue; // partitions
1947
- break;
1948
-
1949
- case 9: // MDs
1950
- case 43: // network block
1951
- case 144: // nfs
1952
- case 145: // nfs
1953
- case 146: // nfs
1954
- case 199: // veritas
1955
- case 201: // veritas
1956
- break;
1957
-
1958
- case 48: // RAID
1959
- case 49: // RAID
1960
- case 50: // RAID
1961
- case 51: // RAID
1962
- case 52: // RAID
1963
- case 53: // RAID
1964
- case 54: // RAID
1965
- case 55: // RAID
1966
- case 112: // RAID
1967
- case 136: // RAID
1968
- case 137: // RAID
1969
- case 138: // RAID
1970
- case 139: // RAID
1971
- case 140: // RAID
1972
- case 141: // RAID
1973
- case 142: // RAID
1974
- case 143: // RAID
1975
- case 179: // MMC
1976
- case 180: // USB
1977
- if(minor % 8) continue; // partitions
1978
- break;
1979
-
1980
- default:
1981
- continue;
1982
- }
1983
-
1984
- RRD_STATS *st = rrd_stats_find(name);
1985
-
1986
- if(!st) {
1987
- char ssfilename[FILENAME_MAX + 1];
1988
- int sector_size = 512;
1989
-
1990
- sprintf(ssfilename, "/sys/block/%s/queue/hw_sector_size", &name[RRD_TYPE_DISK_LEN + 1]);
1991
- FILE *fpss = fopen(ssfilename, "r");
1992
- if(fpss) {
1993
- char ssbuffer[1025];
1994
- char *tmp = fgets(ssbuffer, 1024, fpss);
1995
-
1996
- if(tmp) {
1997
- sector_size = atoi(tmp);
1998
- if(sector_size <= 0) {
1999
- error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, name, ssfilename);
2000
- sector_size = 512;
2001
- }
2002
- }
2003
- else error("Cannot read data for sector size for device %s from %s. Assuming 512.", name, ssfilename);
2004
-
2005
- fclose(fpss);
2006
- }
2007
- else error("Cannot read sector size for device %s from %s. Assuming 512.", name, ssfilename);
2008
-
2009
- st = rrd_stats_create(name, name, save_history, "Disk usage for ", "I/O in kilobytes/s", RRD_TYPE_DISK);
2010
- if(!st) {
2011
- error("Cannot create RRD_STATS for disk %s.", name);
2012
- continue;
2013
- }
2014
-
2015
- if(!rrd_stats_dimension_add(st, "writes", "writes", sizeof(unsigned long long), sector_size * -1, 1024, RRD_DIMENSION_INCREMENTAL))
2016
- error("Cannot add RRD_STATS dimension %s.", "writes");
2017
-
2018
- if(!rrd_stats_dimension_add(st, "reads", "reads", sizeof(unsigned long long), sector_size, 1024, RRD_DIMENSION_INCREMENTAL))
2019
- error("Cannot add RRD_STATS dimension %s.", "reads");
2020
-
2021
- }
2022
- else rrd_stats_next(st);
2023
-
2024
- rrd_stats_dimension_set(st, "reads", &readsectors);
2025
- rrd_stats_dimension_set(st, "writes", &writesectors);
2026
- rrd_stats_done(st);
2027
- }
2028
- }
2029
-
2030
- fclose(fp);
2031
- return 0;
2032
-}
2033
-
2034
-// ----------------------------------------------------------------------------
2035
-// /proc/net/snmp processor
2036
-
2037
-int do_proc_net_snmp() {
2038
- char buffer[MAX_PROC_NET_SNMP_LINE+1] = "";
2039
-
2040
- FILE *fp = fopen("/proc/net/snmp", "r");
2041
- if(!fp) {
2042
- error("Cannot read /proc/net/snmp.");
2043
- return 1;
2044
- }
2045
-
2046
- for(;1;) {
2047
- char *p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2048
- if(!p) break;
2049
-
2050
- if(strncmp(p, "Ip: ", 4) == 0) {
2051
- // skip the header line, read the data
2052
- p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2053
- if(!p) break;
2054
-
2055
- if(strncmp(p, "Ip: ", 4) != 0) {
2056
- error("Cannot read IP line from /proc/net/snmp.");
2057
- break;
2058
- }
2059
-
2060
- // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
2061
- unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
2062
- OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
2063
-
2064
- int r = sscanf(&buffer[4], "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
2065
- &Forwarding, &DefaultTTL, &InReceives, &InHdrErrors, &InAddrErrors, &ForwDatagrams, &InUnknownProtos, &InDiscards, &InDelivers,
2066
- &OutRequests, &OutDiscards, &OutNoRoutes, &ReasmTimeout, &ReasmReqds, &ReasmOKs, &ReasmFails, &FragOKs, &FragFails, &FragCreates);
2067
-
2068
- if(r == EOF) break;
2069
- if(r != 19) error("Cannot read /proc/net/snmp IP line. Expected 19 params, read %d.", r);
2070
-
2071
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_SNMP ".packets");
2072
- if(!st) {
2073
- st = rrd_stats_create(RRD_TYPE_NET_SNMP ".packets", RRD_TYPE_NET_SNMP ".packets", save_history, "IPv4 Packets", "Packets/s", RRD_TYPE_NET_SNMP);
2074
- if(!st) {
2075
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_SNMP ".packets");
2076
- continue;
2077
- }
2078
-
2079
- if(!rrd_stats_dimension_add(st, "forwarded", "forwarded", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2080
- error("Cannot add RRD_STATS dimension %s.", "forwarded");
2081
-
2082
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2083
- error("Cannot add RRD_STATS dimension %s.", "sent");
2084
-
2085
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2086
- error("Cannot add RRD_STATS dimension %s.", "received");
2087
-
2088
- }
2089
- else rrd_stats_next(st);
2090
-
2091
- rrd_stats_dimension_set(st, "sent", &OutRequests);
2092
- rrd_stats_dimension_set(st, "received", &InReceives);
2093
- rrd_stats_dimension_set(st, "forwarded", &ForwDatagrams);
2094
- rrd_stats_done(st);
2095
- }
2096
- else if(strncmp(p, "Tcp: ", 5) == 0) {
2097
- // skip the header line, read the data
2098
- p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2099
- if(!p) break;
2100
-
2101
- if(strncmp(p, "Tcp: ", 5) != 0) {
2102
- error("Cannot read TCP line from /proc/net/snmp.");
2103
- break;
2104
- }
2105
-
2106
- unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets, CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
2107
-
2108
- int r = sscanf(&buffer[5], "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
2109
- &RtoAlgorithm, &RtoMin, &RtoMax, &MaxConn, &ActiveOpens, &PassiveOpens, &AttemptFails, &EstabResets, &CurrEstab, &InSegs, &OutSegs, &RetransSegs, &InErrs, &OutRsts);
2110
-
2111
- if(r == EOF) break;
2112
- if(r != 14) error("Cannot read /proc/net/snmp TCP line. Expected 14 params, read %d.", r);
2113
-
2114
- // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
2115
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcpsock");
2116
- if(!st) {
2117
- st = rrd_stats_create(RRD_TYPE_NET_SNMP ".tcpsock", RRD_TYPE_NET_SNMP ".tcpsock", save_history, "IPv4 TCP Connections", "tcp connections", RRD_TYPE_NET_SNMP);
2118
- if(!st) {
2119
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_SNMP ".tcpsock");
2120
- continue;
2121
- }
2122
-
2123
- if(!rrd_stats_dimension_add(st, "connections", "connections", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_ABSOLUTE))
2124
- error("Cannot add RRD_STATS dimension %s.", "connections");
2125
- }
2126
- else rrd_stats_next(st);
2127
-
2128
- rrd_stats_dimension_set(st, "connections", &CurrEstab);
2129
- rrd_stats_done(st);
2130
-
2131
- st = rrd_stats_find(RRD_TYPE_NET_SNMP ".tcppackets");
2132
- if(!st) {
2133
- st = rrd_stats_create(RRD_TYPE_NET_SNMP ".tcppackets", RRD_TYPE_NET_SNMP ".tcppackets", save_history, "IPv4 TCP Packets", "Packets/s", RRD_TYPE_NET_SNMP);
2134
- if(!st) {
2135
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_SNMP ".tcppackets");
2136
- continue;
2137
- }
2138
-
2139
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2140
- error("Cannot add RRD_STATS dimension %s.", "sent");
2141
-
2142
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2143
- error("Cannot add RRD_STATS dimension %s.", "received");
2144
- }
2145
- else rrd_stats_next(st);
2146
-
2147
- rrd_stats_dimension_set(st, "received", &InSegs);
2148
- rrd_stats_dimension_set(st, "sent", &OutSegs);
2149
- rrd_stats_done(st);
2150
- }
2151
- else if(strncmp(p, "Udp: ", 5) == 0) {
2152
- // skip the header line, read the data
2153
- p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2154
- if(!p) break;
2155
-
2156
- if(strncmp(p, "Udp: ", 5) != 0) {
2157
- error("Cannot read UDP line from /proc/net/snmp.");
2158
- break;
2159
- }
2160
-
2161
- unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
2162
-
2163
- int r = sscanf(&buffer[5], "%llu %llu %llu %llu %llu %llu\n",
2164
- &InDatagrams, &NoPorts, &InErrors, &OutDatagrams, &RcvbufErrors, &SndbufErrors);
2165
-
2166
- if(r == EOF) break;
2167
- if(r != 6) error("Cannot read /proc/net/snmp UDP line. Expected 6 params, read %d.", r);
2168
-
2169
- // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
2170
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_SNMP ".udppackets");
2171
- if(!st) {
2172
- st = rrd_stats_create(RRD_TYPE_NET_SNMP ".udppackets", RRD_TYPE_NET_SNMP ".udppackets", save_history, "IPv4 UDP Packets", "Packets/s", RRD_TYPE_NET_SNMP);
2173
- if(!st) {
2174
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_SNMP ".udppackets");
2175
- continue;
2176
- }
2177
-
2178
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2179
- error("Cannot add RRD_STATS dimension %s.", "sent");
2180
-
2181
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2182
- error("Cannot add RRD_STATS dimension %s.", "received");
2183
- }
2184
- else rrd_stats_next(st);
2185
-
2186
- rrd_stats_dimension_set(st, "received", &InDatagrams);
2187
- rrd_stats_dimension_set(st, "sent", &OutDatagrams);
2188
- rrd_stats_done(st);
2189
- }
2190
- }
2191
-
2192
- fclose(fp);
2193
- return 0;
2194
-}
2195
-
2196
-// ----------------------------------------------------------------------------
2197
-// /proc/net/netstat processor
2198
-
2199
-int do_proc_net_netstat() {
2200
- char buffer[MAX_PROC_NET_SNMP_LINE+1] = "";
2201
-
2202
- FILE *fp = fopen("/proc/net/netstat", "r");
2203
- if(!fp) {
2204
- error("Cannot read /proc/net/netstat.");
2205
- return 1;
2206
- }
2207
-
2208
- for(;1;) {
2209
- char *p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2210
- if(!p) break;
2211
-
2212
- if(strncmp(p, "IpExt: ", 7) == 0) {
2213
- // skip the header line, read the data
2214
- p = fgets(buffer, MAX_PROC_NET_SNMP_LINE, fp);
2215
- if(!p) break;
2216
-
2217
- if(strncmp(p, "IpExt: ", 7) != 0) {
2218
- error("Cannot read IpExt line from /proc/net/netstat.");
2219
- break;
2220
- }
2221
-
2222
- unsigned long long InNoRoutes, InTruncatedPkts, InMcastPkts, OutMcastPkts, InBcastPkts, OutBcastPkts, InOctets, OutOctets, InMcastOctets, OutMcastOctets, InBcastOctets, OutBcastOctets;
2223
-
2224
- int r = sscanf(&buffer[7], "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
2225
- &InNoRoutes, &InTruncatedPkts, &InMcastPkts, &OutMcastPkts, &InBcastPkts, &OutBcastPkts, &InOctets, &OutOctets, &InMcastOctets, &OutMcastOctets, &InBcastOctets, &OutBcastOctets);
2226
-
2227
- if(r == EOF) break;
2228
- if(r != 12) error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %d.", r);
2229
-
2230
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_SNMP ".net");
2231
- if(!st) {
2232
- st = rrd_stats_create(RRD_TYPE_NET_SNMP ".net", RRD_TYPE_NET_SNMP ".net", save_history, "All IPv4 Bandwidth", "bandwidth in kilobits/s", RRD_TYPE_NET_SNMP);
2233
- if(!st) {
2234
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_SNMP ".net");
2235
- continue;
2236
- }
2237
-
2238
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -8, 1024, RRD_DIMENSION_INCREMENTAL))
2239
- error("Cannot add RRD_STATS dimension %s.", "sent");
2240
-
2241
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 8, 1024, RRD_DIMENSION_INCREMENTAL))
2242
- error("Cannot add RRD_STATS dimension %s.", "received");
2243
-
2244
- }
2245
- else rrd_stats_next(st);
2246
-
2247
- rrd_stats_dimension_set(st, "sent", &OutOctets);
2248
- rrd_stats_dimension_set(st, "received", &InOctets);
2249
- rrd_stats_done(st);
2250
- }
2251
- }
2252
-
2253
- fclose(fp);
2254
- return 0;
2255
-}
2256
-
2257
-// ----------------------------------------------------------------------------
2258
-// /proc/net/stat/nf_conntrack processor
2259
-
2260
-int do_proc_net_stat_conntrack() {
2261
- char buffer[MAX_PROC_NET_STAT_CONNTRACK_LINE+1] = "";
2262
-
2263
- FILE *fp = fopen("/proc/net/stat/nf_conntrack", "r");
2264
- if(!fp) {
2265
- error("Cannot read /proc/net/stat/nf_conntrack.");
2266
- return 1;
2267
- }
2268
-
2269
- // read the discard the header
2270
- char *p = fgets(buffer, MAX_PROC_NET_STAT_CONNTRACK_LINE, fp);
2271
-
2272
- unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
2273
- 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;
2274
-
2275
- for(;1;) {
2276
- p = fgets(buffer, MAX_PROC_NET_STAT_CONNTRACK_LINE, fp);
2277
- if(!p) break;
2278
-
2279
- unsigned long long tentries, tsearched, tfound, tnew, tinvalid, tignore, tdelete, tdelete_list, tinsert, tinsert_failed, tdrop, tearly_drop, ticmp_error, texpect_new, texpect_create, texpect_delete, tsearch_restart;
2280
-
2281
- int r = sscanf(buffer, "%llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx\n",
2282
- &tentries, &tsearched, &tfound, &tnew, &tinvalid, &tignore, &tdelete, &tdelete_list, &tinsert, &tinsert_failed, &tdrop, &tearly_drop, &ticmp_error, &texpect_new, &texpect_create, &texpect_delete, &tsearch_restart);
2283
-
2284
- if(r == EOF) break;
2285
- if(r != 17) error("Cannot read /proc/net/stat/nf_conntrack. Expected 17 params, read %d.", r);
2286
-
2287
- if(!aentries) aentries = tentries;
2288
-
2289
- // sum all the cpus together
2290
- asearched += tsearched;
2291
- afound += tfound;
2292
- anew += tnew;
2293
- ainvalid += tinvalid;
2294
- aignore += tignore;
2295
- adelete += tdelete;
2296
- adelete_list += tdelete_list;
2297
- ainsert += tinsert;
2298
- ainsert_failed += tinsert_failed;
2299
- adrop += tdrop;
2300
- aearly_drop += tearly_drop;
2301
- aicmp_error += ticmp_error;
2302
- aexpect_new += texpect_new;
2303
- aexpect_create += texpect_create;
2304
- aexpect_delete += texpect_delete;
2305
- asearch_restart += tsearch_restart;
2306
- }
2307
- fclose(fp);
2308
-
2309
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".sockets");
2310
- if(!st) {
2311
- st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK ".sockets", RRD_TYPE_NET_STAT_CONNTRACK ".sockets", save_history, "Netfilter Connections", "netfilter connections", RRD_TYPE_NET_STAT_CONNTRACK);
2312
- if(!st) {
2313
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_STAT_CONNTRACK ".sockets");
2314
- return 1;
2315
- }
2316
-
2317
- if(!rrd_stats_dimension_add(st, "connections", "connections", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_ABSOLUTE))
2318
- error("Cannot add RRD_STATS dimension %s.", "connections");
2319
- }
2320
- else rrd_stats_next(st);
2321
-
2322
- rrd_stats_dimension_set(st, "connections", &aentries);
2323
- rrd_stats_done(st);
2324
-
2325
- st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".new");
2326
- if(!st) {
2327
- st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK ".new", RRD_TYPE_NET_STAT_CONNTRACK ".new", save_history, "Netfilter New Connections", "connections/s", RRD_TYPE_NET_STAT_CONNTRACK);
2328
- if(!st) {
2329
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_STAT_CONNTRACK ".new");
2330
- return 1;
2331
- }
2332
-
2333
- if(!rrd_stats_dimension_add(st, "dropped", "dropped", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2334
- error("Cannot add RRD_STATS dimension %s.", "dropped");
2335
-
2336
- if(!rrd_stats_dimension_add(st, "new", "new", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2337
- error("Cannot add RRD_STATS dimension %s.", "new");
2338
- }
2339
- else rrd_stats_next(st);
2340
-
2341
- rrd_stats_dimension_set(st, "new", &anew);
2342
- rrd_stats_dimension_set(st, "dropped", &adrop);
2343
- rrd_stats_done(st);
2344
-
2345
- st = rrd_stats_find(RRD_TYPE_NET_STAT_CONNTRACK ".changes");
2346
- if(!st) {
2347
- st = rrd_stats_create(RRD_TYPE_NET_STAT_CONNTRACK ".changes", RRD_TYPE_NET_STAT_CONNTRACK ".changes", save_history, "Netfilter Connection Changes", "connections/s", RRD_TYPE_NET_STAT_CONNTRACK);
2348
- if(!st) {
2349
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_STAT_CONNTRACK ".changes");
2350
- return 1;
2351
- }
2352
-
2353
- if(!rrd_stats_dimension_add(st, "deleted", "deleted", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2354
- error("Cannot add RRD_STATS dimension %s.", "deleted");
2355
-
2356
- if(!rrd_stats_dimension_add(st, "inserted", "inserted", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2357
- error("Cannot add RRD_STATS dimension %s.", "inserted");
2358
- }
2359
- else rrd_stats_next(st);
2360
-
2361
- rrd_stats_dimension_set(st, "inserted", &ainsert);
2362
- rrd_stats_dimension_set(st, "deleted", &adelete);
2363
- rrd_stats_done(st);
2364
-
2365
- return 0;
2366
-}
2367
-
2368
-// ----------------------------------------------------------------------------
2369
-// /proc/net/ip_vs_stats processor
2370
-
2371
-int do_proc_net_ip_vs_stats() {
2372
- char buffer[MAX_PROC_NET_IPVS_LINE+1] = "";
2373
-
2374
- FILE *fp = fopen("/proc/net/ip_vs_stats", "r");
2375
- if(!fp) {
2376
- error("Cannot read /proc/net/ip_vs_stats.");
2377
- return 1;
2378
- }
2379
-
2380
- // read the discard the 2 header lines
2381
- char *p = fgets(buffer, MAX_PROC_NET_IPVS_LINE, fp);
2382
- if(!p) {
2383
- error("Cannot read /proc/net/ip_vs_stats.");
2384
- return 1;
2385
- }
2386
-
2387
- p = fgets(buffer, MAX_PROC_NET_IPVS_LINE, fp);
2388
- if(!p) {
2389
- error("Cannot read /proc/net/ip_vs_stats.");
2390
- return 1;
2391
- }
2392
-
2393
- p = fgets(buffer, MAX_PROC_NET_IPVS_LINE, fp);
2394
- if(!p) {
2395
- error("Cannot read /proc/net/ip_vs_stats.");
2396
- return 1;
2397
- }
2398
-
2399
- unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
2400
-
2401
- int r = sscanf(buffer, "%llx %llx %llx %llx %llx\n", &entries, &InPackets, &OutPackets, &InBytes, &OutBytes);
2402
-
2403
- if(r == EOF) {
2404
- error("Cannot read /proc/net/ip_vs_stats.");
2405
- return 1;
2406
- }
2407
- if(r != 5) error("Cannot read /proc/net/ip_vs_stats. Expected 5 params, read %d.", r);
2408
-
2409
- fclose(fp);
2410
-
2411
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_NET_IPVS ".sockets");
2412
- if(!st) {
2413
- st = rrd_stats_create(RRD_TYPE_NET_IPVS ".sockets", RRD_TYPE_NET_IPVS ".sockets", save_history, "IPVS New Connections", "new connections/s", RRD_TYPE_NET_IPVS);
2414
- if(!st) {
2415
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_IPVS ".sockets");
2416
- return 1;
2417
- }
2418
-
2419
- if(!rrd_stats_dimension_add(st, "connections", "connections", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2420
- error("Cannot add RRD_STATS dimension %s.", "connections");
2421
- }
2422
- else rrd_stats_next(st);
2423
-
2424
- rrd_stats_dimension_set(st, "connections", &entries);
2425
- rrd_stats_done(st);
2426
-
2427
- st = rrd_stats_find(RRD_TYPE_NET_IPVS ".packets");
2428
- if(!st) {
2429
- st = rrd_stats_create(RRD_TYPE_NET_IPVS ".packets", RRD_TYPE_NET_IPVS ".packets", save_history, "IPVS Packets", "Packets/s", RRD_TYPE_NET_IPVS);
2430
- if(!st) {
2431
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_IPVS ".packets");
2432
- return 1;
2433
- }
2434
-
2435
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -1, 1, RRD_DIMENSION_INCREMENTAL))
2436
- error("Cannot add RRD_STATS dimension %s.", "sent");
2437
-
2438
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2439
- error("Cannot add RRD_STATS dimension %s.", "received");
2440
- }
2441
- else rrd_stats_next(st);
2442
-
2443
- rrd_stats_dimension_set(st, "received", &InPackets);
2444
- rrd_stats_dimension_set(st, "sent", &OutPackets);
2445
- rrd_stats_done(st);
2446
-
2447
- st = rrd_stats_find(RRD_TYPE_NET_IPVS ".net");
2448
- if(!st) {
2449
- st = rrd_stats_create(RRD_TYPE_NET_IPVS ".net", RRD_TYPE_NET_IPVS ".net", save_history, "IPVS Bandwidth", "bandwidth in kilobits/s", RRD_TYPE_NET_IPVS);
2450
- if(!st) {
2451
- error("Cannot create RRD_STATS for %s.", RRD_TYPE_NET_IPVS ".net");
2452
- return 1;
2453
- }
2454
-
2455
- if(!rrd_stats_dimension_add(st, "sent", "sent", sizeof(unsigned long long), -8, 1024, RRD_DIMENSION_INCREMENTAL))
2456
- error("Cannot add RRD_STATS dimension %s.", "sent");
2457
-
2458
- if(!rrd_stats_dimension_add(st, "received", "received", sizeof(unsigned long long), 8, 1024, RRD_DIMENSION_INCREMENTAL))
2459
- error("Cannot add RRD_STATS dimension %s.", "received");
2460
- }
2461
- else rrd_stats_next(st);
2462
-
2463
- rrd_stats_dimension_set(st, "received", &InBytes);
2464
- rrd_stats_dimension_set(st, "sent", &OutBytes);
2465
- rrd_stats_done(st);
2466
-
2467
- return 0;
2468
-}
2469
-
2470
-int do_proc_stat() {
2471
- char buffer[MAX_PROC_STAT_LINE+1] = "";
2472
-
2473
- FILE *fp = fopen("/proc/stat", "r");
2474
- if(!fp) {
2475
- error("Cannot read /proc/stat.");
2476
- return 1;
2477
- }
2478
-
2479
- for(;1;) {
2480
- char *p = fgets(buffer, MAX_PROC_STAT_LINE, fp);
2481
- if(!p) break;
2482
-
2483
- if(strncmp(p, "cpu", 3) == 0) {
2484
- char id[MAX_PROC_STAT_NAME + 1] = RRD_TYPE_STAT ".";
2485
- unsigned long long user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;
2486
- char *name = &id[RRD_TYPE_STAT_LEN + 1];
2487
-
2488
- int r = sscanf(buffer, "%s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
2489
- name, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guest_nice);
2490
-
2491
- if(r == EOF) break;
2492
- if(r != 11) error("Cannot read /proc/stat cpu line. Expected 11 params, read %d.", r);
2493
-
2494
- RRD_STATS *st = rrd_stats_find(id);
2495
- if(!st) {
2496
- st = rrd_stats_create(id, id, save_history, "Time for ", "percentage", RRD_TYPE_STAT);
2497
- if(!st) {
2498
- error("Cannot create RRD_STATS for %s.", id);
2499
- continue;
2500
- }
2501
-
2502
- if(strcmp(id, "cpu.cpu") == 0) strcpy(st->usertitle, "all CPUs");
2503
- else strcpy(st->usertitle, name);
2504
-
2505
- long multiplier = 1;
2506
- long divisor = 1; // sysconf(_SC_CLK_TCK);
2507
-
2508
- if(!rrd_stats_dimension_add(st, "iowait", "iowait", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2509
- error("Cannot add RRD_STATS dimension %s.", "iowait");
2510
-
2511
- if(!rrd_stats_dimension_add(st, "nice", "nice", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2512
- error("Cannot add RRD_STATS dimension %s.", "nice");
2513
-
2514
- if(!rrd_stats_dimension_add(st, "system", "system", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2515
- error("Cannot add RRD_STATS dimension %s.", "system");
2516
-
2517
- if(!rrd_stats_dimension_add(st, "user", "user", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2518
- error("Cannot add RRD_STATS dimension %s.", "user");
2519
-
2520
- if(!rrd_stats_dimension_add(st, "idle", "idle", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2521
- error("Cannot add RRD_STATS dimension %s.", "idle");
2522
- else rrd_stats_dimension_hide(st, "idle");
2523
-
2524
- if(!rrd_stats_dimension_add(st, "irq", "irq", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2525
- error("Cannot add RRD_STATS dimension %s.", "irq");
2526
-
2527
- if(!rrd_stats_dimension_add(st, "softirq", "softirq", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2528
- error("Cannot add RRD_STATS dimension %s.", "softirq");
2529
-
2530
- if(!rrd_stats_dimension_add(st, "steal", "steal", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2531
- error("Cannot add RRD_STATS dimension %s.", "steal");
2532
-
2533
- if(!rrd_stats_dimension_add(st, "guest", "guest", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2534
- error("Cannot add RRD_STATS dimension %s.", "guest");
2535
-
2536
- if(!rrd_stats_dimension_add(st, "guest_nice", "guest_nice", sizeof(unsigned long long), multiplier, divisor, RRD_DIMENSION_PCENT_OVER_TOTAL))
2537
- error("Cannot add RRD_STATS dimension %s.", "guest_nice");
2538
-
2539
- }
2540
- else rrd_stats_next(st);
2541
-
2542
- rrd_stats_dimension_set(st, "user", &user);
2543
- rrd_stats_dimension_set(st, "nice", &nice);
2544
- rrd_stats_dimension_set(st, "system", &system);
2545
- rrd_stats_dimension_set(st, "idle", &idle);
2546
- rrd_stats_dimension_set(st, "iowait", &iowait);
2547
- rrd_stats_dimension_set(st, "irq", &irq);
2548
- rrd_stats_dimension_set(st, "softirq", &softirq);
2549
- rrd_stats_dimension_set(st, "steal", &steal);
2550
- rrd_stats_dimension_set(st, "guest", &guest);
2551
- rrd_stats_dimension_set(st, "guest_nice", &guest_nice);
2552
- rrd_stats_done(st);
2553
- }
2554
- else if(strncmp(p, "intr ", 5) == 0) {
2555
- char id[MAX_PROC_STAT_NAME + 1] = RRD_TYPE_STAT ".";
2556
- char *name = &id[RRD_TYPE_STAT_LEN + 1];
2557
-
2558
- unsigned long long value;
2559
-
2560
- int r = sscanf(buffer, "%s %llu ", name, &value);
2561
- if(r == EOF) break;
2562
- if(r != 2) error("Cannot read /proc/stat intr line. Expected 2 params, read %d.", r);
2563
-
2564
- RRD_STATS *st = rrd_stats_find(id);
2565
- if(!st) {
2566
- st = rrd_stats_create(id, id, save_history, "CPU Interrupts", "interrupts/s", RRD_TYPE_STAT);
2567
- if(!st) {
2568
- error("Cannot create RRD_STATS for %s.", id);
2569
- continue;
2570
- }
2571
-
2572
- if(!rrd_stats_dimension_add(st, "interrupts", "interrupts", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2573
- error("Cannot add RRD_STATS dimension %s.", "interrupts");
2574
-
2575
- }
2576
- else rrd_stats_next(st);
2577
-
2578
- rrd_stats_dimension_set(st, "interrupts", &value);
2579
- rrd_stats_done(st);
2580
- }
2581
- else if(strncmp(p, "ctxt ", 5) == 0) {
2582
- char id[MAX_PROC_STAT_NAME + 1] = RRD_TYPE_STAT ".";
2583
- char *name = &id[RRD_TYPE_STAT_LEN + 1];
2584
-
2585
- unsigned long long value;
2586
-
2587
- int r = sscanf(buffer, "%s %llu ", name, &value);
2588
- if(r == EOF) break;
2589
- if(r != 2) error("Cannot read /proc/stat ctxt line. Expected 2 params, read %d.", r);
2590
-
2591
- RRD_STATS *st = rrd_stats_find(id);
2592
- if(!st) {
2593
- st = rrd_stats_create(id, id, save_history, "CPU Context Switches", "context switches/s", RRD_TYPE_STAT);
2594
- if(!st) {
2595
- error("Cannot create RRD_STATS for %s.", id);
2596
- continue;
2597
- }
2598
-
2599
- if(!rrd_stats_dimension_add(st, "switches", "switches", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2600
- error("Cannot add RRD_STATS dimension %s.", "switches");
2601
-
2602
- }
2603
- else rrd_stats_next(st);
2604
-
2605
- rrd_stats_dimension_set(st, "switches", &value);
2606
- rrd_stats_done(st);
2607
- }
2608
- else if(strncmp(p, "processes ", 10) == 0) {
2609
- char id[MAX_PROC_STAT_NAME + 1] = RRD_TYPE_STAT ".";
2610
- char *name = &id[RRD_TYPE_STAT_LEN + 1];
2611
-
2612
- unsigned long long value;
2613
-
2614
- int r = sscanf(buffer, "%s %llu ", name, &value);
2615
- if(r == EOF) break;
2616
- if(r != 2) error("Cannot read /proc/stat processes line. Expected 2 params, read %d.", r);
2617
-
2618
- RRD_STATS *st = rrd_stats_find(id);
2619
- if(!st) {
2620
- st = rrd_stats_create(id, id, save_history, "Started Processes", "processes/s", RRD_TYPE_STAT);
2621
- if(!st) {
2622
- error("Cannot create RRD_STATS for %s.", id);
2623
- continue;
2624
- }
2625
-
2626
- if(!rrd_stats_dimension_add(st, "started", "started", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_INCREMENTAL))
2627
- error("Cannot add RRD_STATS dimension %s.", "started");
2628
-
2629
- }
2630
- else rrd_stats_next(st);
2631
-
2632
- rrd_stats_dimension_set(st, "started", &value);
2633
- rrd_stats_done(st);
2634
- }
2635
- else if(strncmp(p, "procs_running ", 14) == 0) {
2636
- char id[MAX_PROC_STAT_NAME + 1] = RRD_TYPE_STAT ".";
2637
- char *name = &id[RRD_TYPE_STAT_LEN + 1];
2638
-
2639
- unsigned long long value;
2640
-
2641
- int r = sscanf(buffer, "%s %llu ", name, &value);
2642
- if(r == EOF) break;
2643
- if(r != 2) error("Cannot read /proc/stat processes line. Expected 2 params, read %d.", r);
2644
-
2645
- RRD_STATS *st = rrd_stats_find(id);
2646
- if(!st) {
2647
- st = rrd_stats_create(id, id, save_history, "CPU Running Processes", "processes running", RRD_TYPE_STAT);
2648
- if(!st) {
2649
- error("Cannot create RRD_STATS for %s.", id);
2650
- continue;
2651
- }
2652
-
2653
- if(!rrd_stats_dimension_add(st, "running", "running", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_ABSOLUTE))
2654
- error("Cannot add RRD_STATS dimension %s.", "running");
2655
-
2656
- }
2657
- else rrd_stats_next(st);
2658
-
2659
- rrd_stats_dimension_set(st, "running", &value);
2660
- rrd_stats_done(st);
2661
- }
2662
- }
2663
-
2664
- fclose(fp);
2665
- return 0;
2666
-}
2667
-
2668
-
2669
-// ----------------------------------------------------------------------------
2670
-// /proc processor
2671
-
2672
-void *proc_main(void *ptr)
2673
-{
2674
- struct timeval last, now, tmp;
2675
-
2676
- gettimeofday(&last, NULL);
2677
- last.tv_sec -= update_every;
2678
-
2679
- // when ZERO, attempt to do it
2680
- int vdo_proc_net_dev = 0;
2681
- int vdo_proc_diskstats = 0;
2682
- int vdo_proc_net_snmp = 0;
2683
- int vdo_proc_net_netstat = 0;
2684
- int vdo_proc_net_stat_conntrack = 0;
2685
- int vdo_proc_net_ip_vs_stats = 0;
2686
- int vdo_proc_stat = 0;
2687
-
2688
- for(;1;) {
2689
- unsigned long long usec, susec;
2690
- gettimeofday(&now, NULL);
2691
-
2692
- // calculate the time it took for a full loop
2693
- usec = usecdiff(&now, &last);
2694
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Last full loop took %llu usec.", usec);
2695
-
2696
- // BEGIN -- the job to be done
2697
- if(!vdo_proc_net_dev) vdo_proc_net_dev = do_proc_net_dev(usec);
2698
- if(!vdo_proc_diskstats) vdo_proc_diskstats = do_proc_diskstats(usec);
2699
- if(!vdo_proc_net_snmp) vdo_proc_net_snmp = do_proc_net_snmp(usec);
2700
- if(!vdo_proc_net_netstat) vdo_proc_net_netstat = do_proc_net_netstat(usec);
2701
- if(!vdo_proc_net_stat_conntrack) vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(usec);
2702
- if(!vdo_proc_net_ip_vs_stats) vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(usec);
2703
- if(!vdo_proc_stat) vdo_proc_stat = do_proc_stat(usec);
2704
- // END -- the job is done
2705
-
2706
- // find the time to sleep in order to wait exactly update_every seconds
2707
- gettimeofday(&tmp, NULL);
2708
- usec = usecdiff(&tmp, &now);
2709
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: This loop's work took %llu usec.", usec);
2710
-
2711
- if(usec < (update_every * 1000000)) susec = (update_every * 1000000) - usec;
2712
- else susec = 0;
2713
-
2714
- // make sure we will wait at least 100ms
2715
- if(susec < 100000) susec = 100000;
2716
-
2717
- debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Sleeping for %llu usec.", susec);
2718
- usleep(susec);
2719
-
2720
- // copy now to last
2721
- last.tv_sec = now.tv_sec;
2722
- last.tv_usec = now.tv_usec;
2723
- }
2724
-
2725
- return NULL;
2726
-}
2727
-
2728
-// ----------------------------------------------------------------------------
2729
-// /sbin/tc processor
2730
-// this requires the script tc-all.sh
2731
-
2732
-#define PIPE_READ 0
2733
-#define PIPE_WRITE 1
2734
-#define TC_LINE_MAX 1024
2735
-
2736
-struct tc_class {
2737
- char id[RRD_STATS_NAME_MAX + 1];
2738
- char name[RRD_STATS_NAME_MAX + 1];
2739
-
2740
- char parentid[RRD_STATS_NAME_MAX + 1];
2741
-
2742
- int hasparent;
2743
- int isleaf;
2744
- unsigned long long bytes;
2745
-
2746
- struct tc_class *next;
2747
-};
2748
-
2749
-struct tc_device {
2750
- char id[RRD_STATS_NAME_MAX + 1];
2751
- char name[RRD_STATS_NAME_MAX + 1];
2752
-
2753
- struct tc_class *classes;
2754
-};
2755
-
2756
-void tc_device_commit(struct tc_device *d)
2757
-{
2758
- // we only need to add leaf classes
2759
- struct tc_class *c, *x;
2760
- for ( c = d->classes ; c ; c = c->next) {
2761
- c->isleaf = 1;
2762
-
2763
- for ( x = d->classes ; x ; x = x->next) {
2764
- if(strcmp(c->id, x->parentid) == 0) {
2765
- c->isleaf = 0;
2766
- x->hasparent = 1;
2767
- }
2768
- }
2769
- }
2770
-
2771
- // debugging:
2772
- // for ( c = d->classes ; c ; c = c->next) {
2773
- // if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "Device %s, class %s, OK", d->name, c->id);
2774
- // else debug(D_TC_LOOP, "Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
2775
- // }
2776
-
2777
- for ( c = d->classes ; c ; c = c->next) {
2778
- if(c->isleaf && c->hasparent) break;
2779
- }
2780
- if(!c) {
2781
- debug(D_TC_LOOP, "Ignoring TC device '%s'. No leaf classes.", d->name);
2782
- return;
2783
- }
2784
-
2785
- debug(D_TC_LOOP, "Committing TC device '%s'", d->name);
2786
-
2787
- RRD_STATS *st = rrd_stats_find(d->id);
2788
- if(!st) {
2789
- st = rrd_stats_create(d->id, d->name, save_history, "Class usage for ", "Bandwidth in kilobits/s", RRD_TYPE_TC);
2790
- if(!st) {
2791
- error("Cannot create RRD_STATS for interface %s.", d->name);
2792
- return;
2793
- }
2794
-
2795
- for ( c = d->classes ; c ; c = c->next) {
2796
- if(c->isleaf && c->hasparent) {
2797
- if(!rrd_stats_dimension_add(st, c->id, c->name, sizeof(unsigned long long), 8, 1024, RRD_DIMENSION_INCREMENTAL))
2798
- error("Cannot add RRD_STATS dimension %s.", c->name);
2799
- }
2800
- }
2801
- }
2802
- else rrd_stats_next(st);
2803
-
2804
- for ( c = d->classes ; c ; c = c->next) {
2805
- if(c->isleaf && c->hasparent)
2806
- if(rrd_stats_dimension_set(st, c->id, &c->bytes) != 0) {
2807
-
2808
- // new class, we have to add it
2809
- if(!rrd_stats_dimension_add(st, c->id, c->name, sizeof(unsigned long long), 8, 1024, RRD_DIMENSION_INCREMENTAL))
2810
- error("Cannot add RRD_STATS dimension %s.", c->name);
2811
- else rrd_stats_dimension_set(st, c->id, &c->bytes);
2812
- }
2813
-
2814
- // check if the class changed name
2815
- RRD_DIMENSION *rd;
2816
- for(rd = st->dimensions ; rd ; rd = rd->next) {
2817
- if(strcmp(rd->id, c->id) == 0) { strcpy(rd->name, c->name); break; }
2818
- }
2819
- }
2820
- rrd_stats_done(st);
2821
-}
2822
-
2823
-void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
2824
-{
2825
- struct tc_class *c;
2826
- for ( c = d->classes ; c ; c = c->next) {
2827
- if(strcmp(c->id, id) == 0) {
2828
- strncpy(c->name, name, RRD_STATS_NAME_MAX);
2829
- c->name[RRD_STATS_NAME_MAX] = '\0';
2830
- break;
2831
- }
2832
- }
2833
-}
2834
-
2835
-void tc_device_set_device_name(struct tc_device *d, char *name)
2836
-{
2837
- strcpy(d->name, RRD_TYPE_TC ".");
2838
- strncpy(&d->name[RRD_TYPE_TC_LEN + 1], name, RRD_STATS_NAME_MAX - 3);
2839
- d->name[RRD_STATS_NAME_MAX] = '\0';
2840
-}
2841
-
2842
-struct tc_device *tc_device_create(char *name)
2843
-{
2844
- struct tc_device *d;
2845
-
2846
- d = calloc(1, sizeof(struct tc_device));
2847
- if(!d) return NULL;
2848
-
2849
- strcpy(d->name, RRD_TYPE_TC ".");
2850
- strncpy(&d->name[RRD_TYPE_TC_LEN + 1], name, RRD_STATS_NAME_MAX - 3);
2851
- d->name[RRD_STATS_NAME_MAX] = '\0';
2852
-
2853
- strcpy(d->id, d->name);
2854
-
2855
- return(d);
2856
-}
2857
-
2858
-struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid)
2859
-{
2860
- struct tc_class *c;
2861
-
2862
- c = calloc(1, sizeof(struct tc_class));
2863
- if(!c) return NULL;
2864
-
2865
- c->next = n->classes;
2866
- n->classes = c;
2867
-
2868
- strncpy(c->id, id, RRD_STATS_NAME_MAX);
2869
- strcpy(c->name, c->id);
2870
- if(parentid) strncpy(c->parentid, parentid, RRD_STATS_NAME_MAX);
2871
-
2872
- return(c);
2873
-}
2874
-
2875
-void tc_class_free(struct tc_class *c)
2876
-{
2877
- if(c->next) tc_class_free(c->next);
2878
- free(c);
2879
-}
2880
-
2881
-void tc_device_free(struct tc_device *n)
2882
-{
2883
- if(n->classes) tc_class_free(n->classes);
2884
- free(n);
2885
-}
2886
-
2887
-pid_t tc_child_pid = 0;
2888
-void *tc_main(void *ptr)
2889
-{
2890
- char buffer[TC_LINE_MAX+1] = "";
2891
-
2892
- for(;1;) {
2893
- FILE *fp;
2894
- struct tc_device *device = NULL;
2895
- struct tc_class *class = NULL;
2896
-
2897
- sprintf(buffer, "exec ./tc-all.sh %d", update_every);
2898
- fp = popen(buffer, "r");
2899
-
2900
- while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
2901
- buffer[TC_LINE_MAX] = '\0';
2902
- char *b = buffer, *p;
2903
- // debug(D_TC_LOOP, "TC: read '%s'", buffer);
2904
-
2905
- p = strsep(&b, " \n");
2906
- while (p && (*p == ' ' || *p == '\0')) p = strsep(&b, " \n");
2907
- if(!p) continue;
2908
-
2909
- if(strcmp(p, "END") == 0) {
2910
- if(device) {
2911
- tc_device_commit(device);
2912
- tc_device_free(device);
2913
- device = NULL;
2914
- class = NULL;
2915
- }
2916
- }
2917
- else if(strcmp(p, "BEGIN") == 0) {
2918
- if(device) tc_device_free(device);
2919
-
2920
- p = strsep(&b, " \n");
2921
- if(p && *p) device = tc_device_create(p);
2922
- }
2923
- else if(device && (strcmp(p, "class") == 0)) {
2924
- p = strsep(&b, " \n"); // the class: htb, fq_codel, etc
2925
- char *id = strsep(&b, " \n"); // the class major:minor
2926
- char *parent = strsep(&b, " \n"); // 'parent' or 'root'
2927
- char *parentid = strsep(&b, " \n"); // the parent's id
2928
-
2929
- if(id && *id
2930
- && parent && *parent
2931
- && parentid && *parentid
2932
- && (
2933
- (strcmp(parent, "parent") == 0 && parentid && *parentid)
2934
- || strcmp(parent, "root") == 0
2935
- )) {
2936
- if(strcmp(parent, "root") == 0) parentid = NULL;
2937
-
2938
- class = tc_class_add(device, id, parentid);
2939
- }
2940
- }
2941
- else if(device && class && (strcmp(p, "Sent") == 0)) {
2942
- p = strsep(&b, " \n");
2943
- if(p && *p) class->bytes = atoll(p);
2944
- }
2945
- else if(device && (strcmp(p, "SETDEVICENAME") == 0)) {
2946
- char *name = strsep(&b, " |\n");
2947
- if(name && *name) tc_device_set_device_name(device, name);
2948
- }
2949
- else if(device && (strcmp(p, "SETCLASSNAME") == 0)) {
2950
- char *name = strsep(&b, " |\n");
2951
- char *path = strsep(&b, " |\n");
2952
- char *id = strsep(&b, " |\n");
2953
- char *qdisc = strsep(&b, " |\n");
2954
- if(id && *id && path && *path) tc_device_set_class_name(device, id, path);
2955
-
2956
- // prevent unused variables warning
2957
- if(qdisc) qdisc = NULL;
2958
- if(name) name = NULL;
2959
- }
2960
- else if((strcmp(p, "MYPID") == 0)) {
2961
- char *id = strsep(&b, " \n");
2962
- tc_child_pid = atol(id);
2963
- debug(D_TC_LOOP, "Child PID is %d.", tc_child_pid);
2964
- }
2965
- }
2966
- pclose(fp);
2967
-
2968
- sleep(1);
2969
- }
2970
-
2971
- return NULL;
2972
-}
2973
-
2974
-// ----------------------------------------------------------------------------
2975
-// cpu jitter calculation
2976
-
2977
-#define CPU_JITTER_SLEEP_TIME_MS 20
2978
-
2979
-void *cpujitter_main(void *ptr)
2980
-{
2981
-
2982
- struct timeval before, after;
2983
-
2984
- while(1) {
2985
- unsigned long long usec, susec = 0;
2986
-
2987
- while(susec < (update_every * 1000000L)) {
2988
-
2989
- gettimeofday(&before, NULL);
2990
- usleep(CPU_JITTER_SLEEP_TIME_MS * 1000);
2991
- gettimeofday(&after, NULL);
2992
-
2993
- // calculate the time it took for a full loop
2994
- usec = usecdiff(&after, &before);
2995
- susec += usec;
2996
- }
2997
- usec -= (CPU_JITTER_SLEEP_TIME_MS * 1000);
2998
-
2999
- RRD_STATS *st = rrd_stats_find(RRD_TYPE_STAT ".jitter");
3000
- if(!st) {
3001
- st = rrd_stats_create(RRD_TYPE_STAT ".jitter", RRD_TYPE_STAT ".jitter", save_history, "CPU Jitter", "microseconds lost", RRD_TYPE_STAT);
3002
- if(!st) {
3003
- error("Cannot create RRD_STATS for interface %s.", RRD_TYPE_STAT ".jitter");
3004
- continue;
3005
- }
3006
-
3007
- if(!rrd_stats_dimension_add(st, "jitter", "jitter", sizeof(unsigned long long), 1, 1, RRD_DIMENSION_ABSOLUTE))
3008
- error("Cannot add RRD_STATS dimension %s.", "jitter");
3009
-
3010
- }
3011
- else rrd_stats_next(st);
3012
-
3013
- rrd_stats_dimension_set(st, "jitter", &usec);
3014
- rrd_stats_done(st);
3015
- }
3016
-
3017
- return NULL;
3018
-}
3019
-
3020
-
3021
-
3022
-void bye(void)
3023
-{
3024
- error("bye...");
3025
- if(tc_child_pid) kill(tc_child_pid, SIGTERM);
3026
- tc_child_pid = 0;
3027
-}
3028
-
3029
-void sig_handler(int signo)
3030
-{
3031
- switch(signo) {
3032
- case SIGTERM:
3033
- case SIGQUIT:
3034
- case SIGINT:
3035
- case SIGHUP:
3036
- case SIGSEGV:
3037
- error("Signaled cleanup (signal %d).", signo);
3038
- if(tc_child_pid) kill(tc_child_pid, SIGTERM);
3039
- tc_child_pid = 0;
3040
- exit(1);
3041
- break;
3042
-
3043
- default:
3044
- error("Signal %d received. Ignoring it.", signo);
3045
- break;
3046
- }
3047
-}
3048
-
3049
-int become_user(const char *username)
3050
-{
3051
- struct passwd *pw = getpwnam(username);
3052
- if(!pw) {
3053
- fprintf(stderr, "User %s is not present. Error: %s\n", username, strerror(errno));
3054
- return -1;
3055
- }
3056
- if(setgid(pw->pw_gid) != 0) {
3057
- fprintf(stderr, "Cannot switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno));
3058
- return -1;
3059
- }
3060
- if(setegid(pw->pw_gid) != 0) {
3061
- fprintf(stderr, "Cannot effectively switch to user's %s group (gid: %d). Error: %s\n", username, pw->pw_gid, strerror(errno));
3062
- return -1;
3063
- }
3064
- if(setuid(pw->pw_uid) != 0) {
3065
- fprintf(stderr, "Cannot switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno));
3066
- return -1;
3067
- }
3068
- if(seteuid(pw->pw_uid) != 0) {
3069
- fprintf(stderr, "Cannot effectively switch to user %s (uid: %d). Error: %s\n", username, pw->pw_uid, strerror(errno));
3070
- return -1;
3071
- }
3072
-
3073
- return(0);
3074
-}
3075
-
3076
-void become_daemon()
3077
-{
3078
- int i = fork();
3079
- if(i == -1) {
3080
- perror("cannot fork");
3081
- exit(1);
3082
- }
3083
- if(i != 0) {
3084
- exit(0); // the parent
3085
- }
3086
-
3087
- // become session leader
3088
- if (setsid() < 0)
3089
- exit(2);
3090
-
3091
- signal(SIGCHLD, SIG_IGN);
3092
- signal(SIGHUP, SIG_IGN);
3093
- signal(SIGWINCH, SIG_IGN);
3094
-
3095
- // fork() again
3096
- i = fork();
3097
- if(i == -1) {
3098
- perror("cannot fork");
3099
- exit(1);
3100
- }
3101
- if(i != 0) {
3102
- exit(0); // the parent
3103
- }
3104
-
3105
- // Set new file permissions
3106
- umask(0);
3107
-
3108
- // close all files
3109
- for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
3110
- close(i);
3111
-
3112
- silent = 1;
3113
-}
3114
-
3115
-int main(int argc, char **argv)
3116
-{
3117
- int i, daemon = 0;
3118
-
3119
- // parse the arguments
3120
- for(i = 1; i < argc ; i++) {
3121
- if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
3122
- save_history = atoi(argv[i+1]);
3123
- if(save_history < 5 || save_history > HISTORY_MAX) {
3124
- fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", save_history, HISTORY);
3125
- save_history = HISTORY;
3126
- }
3127
- else {
3128
- debug(D_OPTIONS, "save lines set to %d.", save_history);
3129
- }
3130
- i++;
3131
- }
3132
- else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) {
3133
- if(become_user(argv[i+1]) != 0) {
3134
- fprintf(stderr, "Cannot become user %s.\n", argv[i+1]);
3135
- exit(1);
3136
- }
3137
- else {
3138
- debug(D_OPTIONS, "Successfully became user %s.", argv[i+1]);
3139
- }
3140
- i++;
3141
- }
3142
- else if(strcmp(argv[i], "-t") == 0 && (i+1) < argc) {
3143
- update_every = atoi(argv[i+1]);
3144
- if(update_every < 1 || update_every > 600) {
3145
- fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY_MAX);
3146
- update_every = UPDATE_EVERY;
3147
- }
3148
- else {
3149
- debug(D_OPTIONS, "update timer set to %d.", update_every);
3150
- }
3151
- i++;
3152
- }
3153
- else if(strcmp(argv[i], "-p") == 0 && (i+1) < argc) {
3154
- listen_port = atoi(argv[i+1]);
3155
- if(listen_port < 1 || listen_port > 65535) {
3156
- fprintf(stderr, "Invalid listen port %d given. Defaulting to %d.\n", listen_port, LISTEN_PORT);
3157
- listen_port = LISTEN_PORT;
3158
- }
3159
- else {
3160
- debug(D_OPTIONS, "update timer set to %d.", update_every);
3161
- }
3162
- i++;
3163
- }
3164
- else if(strcmp(argv[i], "-d") == 0) {
3165
- daemon = 1;
3166
- debug(D_OPTIONS, "Enabled daemon mode.");
3167
- }
3168
- else {
3169
- fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
3170
- fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT].\n\n", argv[0]);
3171
- fprintf(stderr, " -d enable daemon mode (run in background).\n");
3172
- fprintf(stderr, " -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", HISTORY_MAX, HISTORY);
3173
- fprintf(stderr, " -t UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
3174
- fprintf(stderr, " -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
3175
- fprintf(stderr, " -u USERNAME can be any system username to run as. Default: none.\n");
3176
- exit(1);
3177
- }
3178
- }
3179
-
3180
- // never become a problem
3181
- if(nice(20) == -1) {
3182
- fprintf(stderr, "Cannot lower my CPU priority. Error: %s.\n", strerror(errno));
3183
- }
3184
-
3185
- if(sizeof(long long) >= sizeof(long double)) {
3186
- fprintf(stderr, "\n\nWARNING:\nThis system does not support [long double] variables properly.\nArithmetic overflows and rounding errors may occur.\n\n");
3187
- }
3188
-
3189
- if(daemon) become_daemon();
3190
-
3191
- // open syslog
3192
- openlog("netdata", LOG_PID, LOG_DAEMON);
3193
- syslog(LOG_NOTICE, "netdata started.");
3194
-
3195
- // make sure we cleanup correctly
3196
- atexit(bye);
3197
-
3198
- // catch all signals
3199
- for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, sig_handler);
3200
-
3201
-
3202
- pthread_t p_proc, p_tc, p_jitter;
3203
- int r_proc, r_tc, r_jitter;
3204
-
3205
- // spawn a child to collect data
3206
- r_proc = pthread_create(&p_proc, NULL, proc_main, NULL);
3207
- r_tc = pthread_create(&p_tc, NULL, tc_main, NULL);
3208
- r_jitter = pthread_create(&p_jitter, NULL, cpujitter_main, NULL);
3209
-
3210
- // the main process - the web server listener
3211
- //sleep(1);
3212
- socket_listen_main(NULL);
3213
-
3214
- // wait for the childs to finish
3215
- pthread_join(p_tc, NULL);
3216
- pthread_join(p_proc, NULL);
3217
-
3218
- printf("TC thread returns: %d\n", r_tc);
3219
- printf("PROC NET DEV thread returns: %d\n", r_proc);
3220
- printf("CPU JITTER thread returns: %d\n", r_jitter);
3221
-
3222
- exit(0);
3223
-}
1
+// enable strcasestr()
2
+#define _GNU_SOURCE
3
+
4
+#include <stdio.h>
5
+#include <stdlib.h>
6
+#include <string.h>
7
+#include <time.h>
8
+#include <unistd.h>
9
+#include <pwd.h>
10
+#include <sys/types.h>
11
+#include <sys/time.h>
12
+
13
+#include <sys/socket.h>
14
+#include <sys/select.h>
15
+#include <sys/resource.h>
16
+#include <sys/stat.h>
17
+#include <sys/sendfile.h>
18
+
19
+#include <arpa/inet.h>
20
+#include <netinet/in.h>
21
+#include <netinet/tcp.h>
22
+
23
+#include <errno.h>
24
+#include <stdarg.h>
25
+#include <locale.h>
26
+#include <signal.h>
27
+#include <ctype.h>
28
+#include <time.h>
29
+#include <fcntl.h>
30
+#include <syslog.h>
31
+
32
+#include <pthread.h>
33
+#include <zlib.h>
34
+
35
+#define RRD_DIMENSION_ABSOLUTE 0
36
+#define RRD_DIMENSION_INCREMENTAL 1
37
+#define RRD_DIMENSION_PCENT_OVER_TOTAL 2
38
+
39
+#define RRD_TYPE_NET "net"
40
+#define RRD_TYPE_NET_LEN strlen(RRD_TYPE_NET)
41
+
42
+#define RRD_TYPE_TC "tc"
43
+#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
44
+
45
+#define RRD_TYPE_DISK "disk"
46
+#define RRD_TYPE_DISK_LEN strlen(RRD_TYPE_DISK)
47
+
48
+#define RRD_TYPE_NET_SNMP "ipv4"
49
+#define RRD_TYPE_NET_SNMP_LEN strlen(RRD_TYPE_NET_SNMP)
50
+
51
+#define RRD_TYPE_NET_STAT_CONNTRACK "conntrack"
52
+#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
53
+
54
+#define RRD_TYPE_NET_IPVS "ipvs"
55
+#define RRD_TYPE_NET_IPVS_LEN strlen(RRD_TYPE_NET_IPVS)
56
+
57
+#define RRD_TYPE_STAT "cpu"
58
+#define RRD_TYPE_STAT_LEN strlen(RRD_TYPE_STAT)
59
+
60
+#define WEB_PATH_FILE "file"
61
+#define WEB_PATH_DATA "data"
62
+#define WEB_PATH_GRAPH "graph"
63
+
64
+// internal defaults
65
+#define UPDATE_EVERY 1
66
+#define UPDATE_EVERY_MAX 3600
67
+#define LISTEN_PORT 19999
68
+#define HISTORY 3600
69
+#define HISTORY_MAX (86400*10)
70
+#define SAVE_PATH "/tmp"
71
+
72
+#define D_WEB_BUFFER 0x00000001
73
+#define D_WEB_CLIENT 0x00000002
74
+#define D_LISTENER 0x00000004
75
+#define D_WEB_DATA 0x00000008
76
+#define D_OPTIONS 0x00000010
77
+#define D_PROCNETDEV_LOOP 0x00000020
78
+#define D_RRD_STATS 0x00000040
79
+#define D_WEB_CLIENT_ACCESS 0x00000080
80
+#define D_TC_LOOP 0x00000100
81
+#define D_DEFLATE 0x00000200
82
+
83
+#define CT_APPLICATION_JSON 1
84
+#define CT_TEXT_PLAIN 2
85
+#define CT_TEXT_HTML 3
86
+#define CT_APPLICATION_X_JAVASCRIPT 4
87
+#define CT_TEXT_CSS 5
88
+#define CT_TEXT_XML 6
89
+#define CT_APPLICATION_XML 7
90
+#define CT_TEXT_XSL 8
91
+#define CT_APPLICATION_OCTET_STREAM 9
92
+#define CT_APPLICATION_X_FONT_TRUETYPE 10
93
+#define CT_APPLICATION_X_FONT_OPENTYPE 11
94
+#define CT_APPLICATION_FONT_WOFF 12
95
+#define CT_APPLICATION_VND_MS_FONTOBJ 13
96
+#define CT_IMAGE_SVG_XML 14
97
+
98
+// configuration
99
+#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
100
+//#define DEBUG 0xffffffff
101
+//#define DEBUG (0)
102
+
103
+#define EXIT_FAILURE 1
104
+#define LISTEN_BACKLOG 100
105
+
106
+#define MAX_SOCKET_DATA_LENGTH (256 * 1024)
107
+#define SOCKET_DATA_LENGTH_INCREASE_STEP 65536
108
+#define ZLIB_CHUNK 16384
109
+
110
+#define MAX_HTTP_HEADER_SIZE 16384
111
+
112
+#define MAX_PROC_NET_DEV_LINE 4096
113
+#define MAX_PROC_NET_DEV_IFACE_NAME 1024
114
+
115
+#define MAX_PROC_DISKSTATS_LINE 4096
116
+#define MAX_PROC_DISKSTATS_DISK_NAME 1024
117
+
118
+#define MAX_PROC_NET_SNMP_LINE 4096
119
+#define MAX_PROC_NET_SNMP_NAME 1024
120
+
121
+#define MAX_PROC_NET_STAT_CONNTRACK_LINE 4096
122
+#define MAX_PROC_NET_STAT_CONNTRACK_NAME 1024
123
+
124
+#define MAX_PROC_NET_IPVS_LINE 4096
125
+#define MAX_PROC_NET_IPVS_NAME 1024
126
+
127
+#define MAX_PROC_STAT_LINE 4096
128
+#define MAX_PROC_STAT_NAME 1024
129
+
130
+
131
+int silent = 0;
132
+int save_history = HISTORY;
133
+int update_every = UPDATE_EVERY;
134
+int listen_port = LISTEN_PORT;
135
+
136
+
137
+// ----------------------------------------------------------------------------
138
+// helpers
139
+
140
+unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
141
+ return ((((now->tv_sec * 1000000L) + now->tv_usec) - ((last->tv_sec * 1000000L) + last->tv_usec)));
142
+}
143
+
144
+// ----------------------------------------------------------------------------
145
+// LOG
146
+
147
+void log_date()
148
+{
149
+ char outstr[200];
150
+ time_t t;
151
+ struct tm *tmp;
152
+
153
+ t = time(NULL);
154
+ tmp = localtime(&t);
155
+
156
+ if (tmp == NULL) return;
157
+ if (strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0) return;
158
+
159
+ fprintf(stderr, "%s: ", outstr);
160
+}
161
+
162
+#define debug(args...) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args)
163
+
164
+void debug_int( const char *file, const char *function, const unsigned long line, unsigned long type, const char *fmt, ... )
165
+{
166
+ if(silent) return;
167
+
168
+ va_list args;
169
+
170
+ if(DEBUG & type) {
171
+ log_date();
172
+ va_start( args, fmt );
173
+ fprintf(stderr, "DEBUG (%04lu@%-15.15s): ", line, function);
174
+ vfprintf( stderr, fmt, args );
175
+ va_end( args );
176
+ fprintf(stderr, "\n");
177
+ }
178
+}
179
+
180
+#define error(args...) error_int(__FILE__, __FUNCTION__, __LINE__, ##args)
181
+
182
+void error_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
183
+{
184
+ va_list args;
185
+
186
+ if(!silent) {
187
+ log_date();
188
+
189
+ va_start( args, fmt );
190
+ fprintf(stderr, "ERROR (%04lu@%-15.15s): ", line, function);
191
+ vfprintf( stderr, fmt, args );
192
+ va_end( args );
193
+
194
+ if(errno) {
195
+ fprintf(stderr, " (errno %d, %s)\n", errno, strerror(errno));
196
+ errno = 0;
197
+ }
198
+ else fprintf(stderr, "\n");
199
+ }
200
+
201
+ va_start( args, fmt );
202
+ vsyslog(LOG_ERR, fmt, args );
203
+ va_end( args );
204
+}
205
+
206
+#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
207
+
208
+void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
209
+{
210
+ va_list args;
211
+
212
+ if(!silent) {
213
+ log_date();
214
+
215
+ va_start( args, fmt );
216
+ fprintf(stderr, "FATAL (%04lu@%-15.15s): ", line, function);
217
+ vfprintf( stderr, fmt, args );
218
+ va_end( args );
219
+
220
+ perror(" # ");
221
+ fprintf(stderr, "\n");
222
+ }
223
+
224
+ va_start( args, fmt );
225
+ vsyslog(LOG_CRIT, fmt, args );
226
+ va_end( args );
227
+
228
+ exit(EXIT_FAILURE);
229
+}
230
+
231
+
232
+// ----------------------------------------------------------------------------
233
+// URL encode / decode
234
+// code from: http://www.geekhideout.com/urlcode.shtml
235
+
236
+/* Converts a hex character to its integer value */
237
+char from_hex(char ch) {
238
+ return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
239
+}
240
+
241
+/* Converts an integer value to its hex character*/
242
+char to_hex(char code) {
243
+ static char hex[] = "0123456789abcdef";
244
+ return hex[code & 15];
245
+}
246
+
247
+/* Returns a url-encoded version of str */
248
+/* IMPORTANT: be sure to free() the returned string after use */
249
+char *url_encode(char *str) {
250
+ char *pstr = str,
251
+ *buf = malloc(strlen(str) * 3 + 1),
252
+ *pbuf = buf;
253
+
254
+ while (*pstr) {
255
+ if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
256
+ *pbuf++ = *pstr;
257
+
258
+ else if (*pstr == ' ')
259
+ *pbuf++ = '+';
260
+
261
+ else
262
+ *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
263
+
264
+ pstr++;
265
+ }
266
+
267
+ *pbuf = '\0';
268
+
269
+ return buf;
270
+}
271
+
272
+/* Returns a url-decoded version of str */
273
+/* IMPORTANT: be sure to free() the returned string after use */
274
+char *url_decode(char *str) {
275
+ char *pstr = str,
276
+ *buf = malloc(strlen(str) + 1),
277
+ *pbuf = buf;
278
+
279
+ if(!buf) fatal("Cannot allocate memory.");
280
+
281
+ while (*pstr) {
282
+ if (*pstr == '%') {
283
+ if (pstr[1] && pstr[2]) {
284
+ *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
285
+ pstr += 2;
286
+ }
287
+ }
288
+ else if (*pstr == '+')
289
+ *pbuf++ = ' ';
290
+
291
+ else
292
+ *pbuf++ = *pstr;
293
+
294
+ pstr++;
295
+ }
296
+
297
+ *pbuf = '\0';
298
+
299
+ return buf;
300
+}
301
+
302
+
303
+// ----------------------------------------------------------------------------
304
+// socket
305
+
306
+int create_listen_socket(int port)
307
+{
308
+ int sock=-1;
309
+ int sockopt=1;
310
+ struct sockaddr_in name;
311
+
312
+ debug(D_LISTENER, "Creating new listening socket on port %d", port);
313
+
314
+ sock = socket(AF_INET, SOCK_STREAM, 0);
315
+ if (sock < 0)
316
+ fatal("socket() failed, errno=%d", errno);
317
+
318
+ /* avoid "address already in use" */
319
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
320
+
321
+ memset(&name, 0, sizeof(struct sockaddr_in));
322
+ name.sin_family = AF_INET;
323
+ name.sin_port = htons (port);
324
+ name.sin_addr.s_addr = htonl (INADDR_ANY);
325
+ if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
326
+ fatal("bind() failed, errno=%d", errno);
327
+
328
+ if (listen(sock, LISTEN_BACKLOG) < 0)
329
+ fatal("listen() failed, errno=%d", errno);
330
+
331
+ debug(D_LISTENER, "Listening Port %d created", port);
332
+ return sock;
333
+}
334
+
335
+
336
+// ----------------------------------------------------------------------------
337
+// RRD STATS
338
+
339
+#define RRD_STATS_NAME_MAX 1024
340
+
341
+struct rrd_dimension {
342
+ char id[RRD_STATS_NAME_MAX + 1];
343
+ char name[RRD_STATS_NAME_MAX + 1];
344
+ size_t bytes;
345
+ size_t entries;
346
+
347
+ int type;
348
+ int hidden; // if set to non zero, this dimension will not be sent to the client
349
+
350
+ long multiplier;
351
+ long divisor;
352
+
353
+ void *values;
354
+
355
+ time_t last_updated;
356
+
357
+ struct rrd_dimension *next;
358
+};
359
+typedef struct rrd_dimension RRD_DIMENSION;
360
+
361
+struct rrd_stats {
362
+ pthread_mutex_t mutex;
363
+
364
+ char id[RRD_STATS_NAME_MAX + 1];
365
+ char name[RRD_STATS_NAME_MAX + 1];
366
+ char title[RRD_STATS_NAME_MAX + 1];
367
+ char vtitle[RRD_STATS_NAME_MAX + 1];
368
+
369
+ char usertitle[RRD_STATS_NAME_MAX + 1];
370
+ char userpriority[RRD_STATS_NAME_MAX + 1];
371
+
372
+ char envtitle[RRD_STATS_NAME_MAX + 1];
373
+ char envpriority[RRD_STATS_NAME_MAX + 1];
374
+
375
+ char type[RRD_STATS_NAME_MAX + 1];
376
+
377
+ char hostname[RRD_STATS_NAME_MAX + 1];
378
+
379
+ size_t entries;
380
+ size_t current_entry;
381
+ // size_t last_entry;
382
+
383
+ time_t last_updated;
384
+
385
+ struct timeval *times;
386
+ RRD_DIMENSION *dimensions;
387
+ struct rrd_stats *next;
388
+};
389
+typedef struct rrd_stats RRD_STATS;
390
+
391
+RRD_STATS *root = NULL;
392
+pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;
393
+
394
+RRD_STATS *rrd_stats_create(const char *id, const char *name, unsigned long entries, const char *title, const char *vtitle, const char *type)
395
+{
396
+ RRD_STATS *st = NULL;
397
+ char *p;
398
+
399
+ debug(D_RRD_STATS, "Creating RRD_STATS for '%s'.", name);
400
+
401
+ st = calloc(1, sizeof(RRD_STATS));
402
+ if(!st) return NULL;
403
+
404
+ st->times = calloc(entries, sizeof(struct timeval));
405
+ if(!st->times) {
406
+ error("Cannot allocate %lu entries of %lu bytes each for RRD_STATS.", st->entries, sizeof(struct timeval));
407
+ free(st);
408
+ return NULL;
409
+ }
410
+
411
+ strncpy(st->id, id, RRD_STATS_NAME_MAX);
412
+ st->id[RRD_STATS_NAME_MAX] = '\0';
413
+
414
+ strncpy(st->name, name, RRD_STATS_NAME_MAX);
415
+ st->name[RRD_STATS_NAME_MAX] = '\0';
416
+ while((p = strchr(st->name, '/'))) *p = '_';
417
+ while((p = strchr(st->name, '?'))) *p = '_';
418
+ while((p = strchr(st->name, '&'))) *p = '_';
419
+
420
+ strncpy(st->title, title, RRD_STATS_NAME_MAX);
421
+ st->title[RRD_STATS_NAME_MAX] = '\0';
422
+
423
+ strncpy(st->vtitle, vtitle, RRD_STATS_NAME_MAX);
424
+ st->vtitle[RRD_STATS_NAME_MAX] = '\0';
425
+
426
+ strncpy(st->type, type, RRD_STATS_NAME_MAX);
427
+ st->type[RRD_STATS_NAME_MAX] = '\0';
428
+
429
+ // check if there is a name for it in the environment
430
+ sprintf(st->envtitle, "NETDATA_TITLE_%s", st->id);
431
+ while((p = strchr(st->envtitle, '/'))) *p = '_';
432
+ while((p = strchr(st->envtitle, '.'))) *p = '_';
433
+ while((p = strchr(st->envtitle, '-'))) *p = '_';
434
+ p = getenv(st->envtitle);
435
+
436
+ if(p) strncpy(st->usertitle, p, RRD_STATS_NAME_MAX);
437
+ else strncpy(st->usertitle, st->name, RRD_STATS_NAME_MAX);
438
+ st->usertitle[RRD_STATS_NAME_MAX] = '\0';
439
+
440
+ sprintf(st->envpriority, "NETDATA_PRIORITY_%s", st->id);
441
+ while((p = strchr(st->envpriority, '/'))) *p = '_';
442
+ while((p = strchr(st->envpriority, '.'))) *p = '_';
443
+ while((p = strchr(st->envpriority, '-'))) *p = '_';
444
+ p = getenv(st->envpriority);
445
+
446
+ if(p) strncpy(st->userpriority, p, RRD_STATS_NAME_MAX);
447
+ else strncpy(st->userpriority, st->name, RRD_STATS_NAME_MAX);
448
+ st->userpriority[RRD_STATS_NAME_MAX] = '\0';
449
+
450
+ if(gethostname(st->hostname, RRD_STATS_NAME_MAX) == -1)
451
+ error("Cannot get hostname.");
452
+
453
+ st->entries = entries;
454
+ st->current_entry = 0;
455
+ // st->last_entry = 0;
456
+ st->dimensions = NULL;
457
+ st->last_updated = time(NULL);
458
+
459
+ pthread_mutex_init(&st->mutex, NULL);
460
+ pthread_mutex_lock(&st->mutex);
461
+ pthread_mutex_lock(&root_mutex);
462
+
463
+ st->next = root;
464
+ root = st;
465
+
466
+ pthread_mutex_unlock(&root_mutex);
467
+ // leave st->mutex locked
468
+
469
+ return(st);
470
+}
471
+
472
+RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *id, const char *name, size_t bytes, long multiplier, long divisor, int type)
473
+{
474
+ RRD_DIMENSION *rd = NULL;
475
+
476
+ debug(D_RRD_STATS, "Adding dimension '%s' (%s) to RRD_STATS '%s' (%s).", name, id, st->name, st->id);
477
+
478
+ rd = calloc(1, sizeof(RRD_DIMENSION));
479
+ if(!rd) return NULL;
480
+
481
+ rd->bytes = bytes;
482
+ rd->entries = st->entries;
483
+ rd->multiplier = multiplier;
484
+ rd->divisor = divisor;
485
+ rd->type = type;
486
+ rd->values = calloc(rd->entries, rd->bytes);
487
+ if(!rd->values) {
488
+ error("Cannot allocate %lu entries of %lu bytes each for RRD_DIMENSION.", rd->entries, rd->bytes);
489
+ free(rd);
490
+ return NULL;
491
+ }
492
+
493
+ strncpy(rd->id, id, RRD_STATS_NAME_MAX);
494
+ rd->id[RRD_STATS_NAME_MAX] = '\0';
495
+
496
+ strncpy(rd->name, name, RRD_STATS_NAME_MAX);
497
+ rd->name[RRD_STATS_NAME_MAX] = '\0';
498
+
499
+ rd->next = st->dimensions;
500
+ st->dimensions = rd;
501
+
502
+ return(rd);
503
+}
504
+
505
+void rrd_stats_dimension_free(RRD_DIMENSION *rd)
506
+{
507
+ if(rd->next) rrd_stats_dimension_free(rd->next);
508
+ debug(D_RRD_STATS, "Removing dimension '%s'.", rd->name);
509
+ free(rd->values);
510
+ free(rd);
511
+}
512
+
513
+RRD_STATS *rrd_stats_find(const char *id)
514
+{
515
+ pthread_mutex_lock(&root_mutex);
516
+
517
+ RRD_STATS *st = root;
518
+
519
+ for ( ; st ; st = st->next ) {
520
+ if(strcmp(st->id, id) == 0) break;
521
+ }
522
+
523
+ pthread_mutex_unlock(&root_mutex);
524
+ return(st);
525
+}
526
+
527
+RRD_STATS *rrd_stats_find_byname(const char *name)
528
+{
529
+ pthread_mutex_lock(&root_mutex);
530
+
531
+ RRD_STATS *st = root;
532
+
533
+ for ( ; st ; st = st->next ) {
534
+ if(strcmp(st->name, name) == 0) break;
535
+ }
536
+
537
+ pthread_mutex_unlock(&root_mutex);
538
+ return(st);
539
+}
540
+
541
+RRD_DIMENSION *rrd_stats_dimension_find(RRD_STATS *st, const char *id)
542
+{
543
+ RRD_DIMENSION *rd = st->dimensions;
544
+
545
+ for ( ; rd ; rd = rd->next ) {
546
+ if(strcmp(rd->id, id) == 0) break;
547
+ }
548
+
549
+ return(rd);
550
+}
551
+
552
+void rrd_stats_next(RRD_STATS *st)
553
+{
554
+ struct timeval *now;
555
+
556
+ pthread_mutex_lock(&st->mutex);
557
+
558
+ // st->current_entry should never be outside the array
559
+ // or, the parallel threads may end up crashing
560
+ // st->last_entry = st->current_entry;
561
+ st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
562
+
563
+ now = &st->times[st->current_entry];
564
+ gettimeofday(now, NULL);
565
+
566
+ st->last_updated = now->tv_sec;
567
+
568
+ // leave mutex locked
569
+}
570
+
571
+void rrd_stats_done(RRD_STATS *st)
572
+{
573
+ RRD_DIMENSION *rd, *last;
574
+
575
+ // find if there are any obsolete dimensions (not updated recently)
576
+ for( rd = st->dimensions, last = NULL ; rd ; ) {
577
+ if((rd->last_updated + (10 * update_every)) < st->last_updated) { // remove it only it is not updated in 10 seconds
578
+ debug(D_RRD_STATS, "Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
579
+
580
+ if(!last) {
581
+ st->dimensions = rd->next;
582
+ rd->next = NULL;
583
+ rrd_stats_dimension_free(rd);
584
+ rd = st->dimensions;
585
+ continue;
586
+ }
587
+ else {
588
+ last->next = rd->next;
589
+ rd->next = NULL;
590
+ rrd_stats_dimension_free(rd);
591
+ rd = last->next;
592
+ continue;
593
+ }
594
+ }
595
+
596
+ last = rd;
597
+ rd = rd->next;
598
+ }
599
+
600
+ pthread_mutex_unlock(&st->mutex);
601
+}
602
+
603
+int rrd_stats_dimension_hide(RRD_STATS *st, const char *id)
604
+{
605
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
606
+ if(!rd) {
607
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
608
+ return 1;
609
+ }
610
+
611
+ rd->hidden = 1;
612
+ return 0;
613
+}
614
+
615
+
616
+int rrd_stats_dimension_set(RRD_STATS *st, const char *id, void *data)
617
+{
618
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, id);
619
+ if(!rd) {
620
+ error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
621
+ return 1;
622
+ }
623
+
624
+ rd->last_updated = st->last_updated;
625
+
626
+ if(rd->bytes == sizeof(long long)) {
627
+ long long *dimension = rd->values, *value = data;
628
+
629
+ dimension[st->current_entry] = (*value);
630
+ }
631
+ else if(rd->bytes == sizeof(long)) {
632
+ long *dimension = rd->values, *value = data;
633
+
634
+ dimension[st->current_entry] = (*value);
635
+ }
636
+ else if(rd->bytes == sizeof(int)) {
637
+ int *dimension = rd->values, *value = data;
638
+
639
+ dimension[st->current_entry] = (*value);
640
+ }
641
+ else if(rd->bytes == sizeof(char)) {
642
+ char *dimension = rd->values, *value = data;
643
+
644
+ dimension[st->current_entry] = (*value);
645
+ }
646
+ else fatal("I don't know how to handle data of length %d bytes.", rd->bytes);
647
+
648
+ return 0;
649
+}
650
+
651
+#define GROUP_AVERAGE 0
652
+#define GROUP_MAX 1
653
+
654
+size_t rrd_stats_one_json(RRD_STATS *st, char *options, char *buffer, size_t len)
655
+{
656
+ size_t i = 0;
657
+ if(i + 200 > len) return(0);
658
+
659
+ i += sprintf(&buffer[i], "\t\t{\n");
660
+ i += sprintf(&buffer[i], "\t\t\t\"id\" : \"%s\",\n", st->id);
661
+ i += sprintf(&buffer[i], "\t\t\t\"name\" : \"%s\",\n", st->name);
662
+ i += sprintf(&buffer[i], "\t\t\t\"type\" : \"%s\",\n", st->type);
663
+ i += sprintf(&buffer[i], "\t\t\t\"title\" : \"%s %s\",\n", st->title, st->name);
664
+ i += sprintf(&buffer[i], "\t\t\t\"usertitle\" : \"%s %s (%s)\",\n", st->title, st->usertitle, st->id);
665
+ i += sprintf(&buffer[i], "\t\t\t\"userpriority\" : \"%s\",\n", st->userpriority);
666
+ i += sprintf(&buffer[i], "\t\t\t\"envtitle\" : \"%s\",\n", st->envtitle);
667
+ i += sprintf(&buffer[i], "\t\t\t\"envpriority\" : \"%s\",\n", st->envpriority);
668
+ i += sprintf(&buffer[i], "\t\t\t\"hostname\" : \"%s\",\n", st->hostname);
669
+ i += sprintf(&buffer[i], "\t\t\t\"vtitle\" : \"%s\",\n", st->vtitle);
670
+ i += sprintf(&buffer[i], "\t\t\t\"url\" : \"/data/%s/%s\",\n", st->name, options?options:"");
671
+ i += sprintf(&buffer[i], "\t\t\t\"entries\" : %ld,\n", st->entries);
672
+ i += sprintf(&buffer[i], "\t\t\t\"current\" : %ld,\n", st->current_entry);
673
+ i += sprintf(&buffer[i], "\t\t\t\"update_every\" : %d,\n", update_every);
674
+ i += sprintf(&buffer[i], "\t\t\t\"last_updated\" : %lu,\n", st->last_updated);
675
+ i += sprintf(&buffer[i], "\t\t\t\"last_updated_secs_ago\" : %lu\n", time(NULL) - st->last_updated);
676
+ i += sprintf(&buffer[i], "\t\t}");
677
+
678
+ return(i);
679
+}
680
+
681
+#define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
682
+#define RRD_GRAPH_JSON_FOOTER "\n\t]\n}\n"
683
+
684
+size_t rrd_stats_graph_json(RRD_STATS *st, char * options, char *buffer, size_t len)
685
+{
686
+ size_t i = 0;
687
+ i += sprintf(&buffer[i], RRD_GRAPH_JSON_HEADER);
688
+ i += rrd_stats_one_json(st, options, &buffer[i], len - i);
689
+ i += sprintf(&buffer[i], RRD_GRAPH_JSON_FOOTER);
690
+ return(i);
691
+}
692
+
693
+size_t rrd_stats_all_json(char *buffer, size_t len)
694
+{
695
+ size_t i = 0, c;
696
+ RRD_STATS *st;
697
+
698
+ i += sprintf(&buffer[i], RRD_GRAPH_JSON_HEADER);
699
+
700
+ for(st = root, c = 0; st ; st = st->next, c++) {
701
+ if(c) i += sprintf(&buffer[i], "%s", ",\n");
702
+ i += rrd_stats_one_json(st, NULL, &buffer[i], len - i);
703
+ }
704
+
705
+ i += sprintf(&buffer[i], RRD_GRAPH_JSON_FOOTER);
706
+ return(i);
707
+}
708
+
709
+long long rrd_stats_dimension_get(RRD_DIMENSION *rd, size_t position)
710
+{
711
+ if(rd->bytes == sizeof(long long)) {
712
+ long long *dimension = rd->values;
713
+ return(dimension[position]);
714
+ }
715
+ else if(rd->bytes == sizeof(long)) {
716
+ long *dimension = rd->values;
717
+ return(dimension[position]);
718
+ }
719
+ else if(rd->bytes == sizeof(int)) {
720
+ int *dimension = rd->values;
721
+ return(dimension[position]);
722
+ }
723
+ else if(rd->bytes == sizeof(char)) {
724
+ char *dimension = rd->values;
725
+ return(dimension[position]);
726
+ }
727
+ else fatal("Cannot produce JSON for size %d bytes dimension.", rd->bytes);
728
+
729
+ return(0);
730
+}
731
+
732
+size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show, size_t group_count, int group_method)
733
+{
734
+ pthread_mutex_lock(&st->mutex);
735
+
736
+ // check the options
737
+ if(entries_to_show <= 0) entries_to_show = 1;
738
+ if(group_count <= 0) group_count = 1;
739
+ if(group_count > st->entries / 20) group_count = st->entries / 20;
740
+
741
+ size_t i = 0; // the bytes of JSON output we have generated so far
742
+ size_t printed = 0; // the lines of JSON data we have generated so far
743
+
744
+ size_t current_entry = st->current_entry;
745
+ size_t t, lt; // t = the current entry, lt = the lest entry of data
746
+ long count = st->entries; // count down of the entries examined so far
747
+ long pad = 0; // align the entries when grouping values together
748
+
749
+ RRD_DIMENSION *rd;
750
+ size_t c = 0; // counter for dimension loops
751
+ size_t dimensions = 0; // the total number of dimensions present
752
+
753
+ unsigned long long usec = 0;// usec between the entries
754
+ char dtm[201]; // temp variable for storing dates
755
+
756
+ int we_need_totals = 0; // if set, we should calculate totals for all dimensions
757
+
758
+ // find how many dimensions we have
759
+ for( rd = st->dimensions ; rd ; rd = rd->next) {
760
+ dimensions++;
761
+ if(rd->type == RRD_DIMENSION_PCENT_OVER_TOTAL) we_need_totals++;
762
+ }
763
+
764
+ if(!dimensions) {
765
+ pthread_mutex_unlock(&st->mutex);
766
+ return sprintf(b, "No dimensions yet.");
767
+ }
768
+
769
+ // temporary storage to keep track of group values and counts
770
+ long double group_values[dimensions];
771
+ int group_counts[dimensions];
772
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
773
+ group_values[c] = group_counts[c] = 0;
774
+
775
+ i += sprintf(&b[i], "{\n \"cols\":\n [\n");
776
+ i += sprintf(&b[i], " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"datetime\"}");
777
+
778
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++)
779
+ if(!rd->hidden)
780
+ i += sprintf(&b[i], ",\n {\"id\":\"\",\"label\":\"%s\",\"pattern\":\"\",\"type\":\"number\"}", rd->name);
781
+
782
+ i += sprintf(&b[i], " ],\n \"rows\":\n [\n");
783
+
784
+ // to allow grouping on the same values, we need a pad
785
+ pad = current_entry % group_count;
786
+
787
+ // make sure current_entry is within limits
788
+ if(current_entry < 0 || current_entry >= st->entries) current_entry = 0;
789
+
790
+ // find the old entry of the round-robin
791
+ t = current_entry + 1;
792
+ if(t >= st->entries) t = 0;
793
+ lt = t;
794
+
795
+ // find the current entry
796
+ t++;
797
+ if(t >= st->entries) t = 0;
798
+
799
+ // the loop in dimension data
800
+ count -= 2;
801
+ for ( ; t != current_entry && count >= 0 ; lt = t++, count--) {
802
+ if(t >= st->entries) t = 0;
803
+
804
+ // if the last is empty, loop again
805
+ if(!st->times[lt].tv_sec) continue;
806
+
807
+ // check if we may exceed the buffer provided
808
+ if((length - i) < 1024) break;
809
+
810
+ // prefer the most recent last entries
811
+ if(((count-pad) / group_count) > entries_to_show) continue;
812
+
813
+
814
+ // ok. we will use this entry!
815
+ // find how much usec since the previous entry
816
+
817
+ usec = usecdiff(&st->times[t], &st->times[lt]);
818
+
819
+ if(((count-pad) % group_count) == 0) {
820
+ if(printed >= entries_to_show) break;
821
+
822
+ // generate the local date time
823
+ struct tm *tm = localtime(&st->times[t].tv_sec);
824
+ if(!tm) { error("localtime() failed."); continue; }
825
+
826
+ sprintf(dtm, "\"Date(%d, %d, %d, %d, %d, %d, %d)\"", tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(st->times[t].tv_usec / 1000)); // datetime
827
+ // strftime(dtm, 200, "\"Date(%Y, %m, %d, %H, %M, %S)\"", tm); // datetime
828
+ // strftime(dtm, 200, "[%H, %M, %S, 0]", tm); // timeofday
829
+ i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s}", printed?"]},\n":"", dtm);
830
+
831
+ printed++;
832
+ }
833
+
834
+ // if we need a PCENT_OVER_TOTAL, calculate the totals for the current and the last
835
+ long double total = 0, oldtotal = 0;
836
+ if(we_need_totals) {
837
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
838
+ total += (long double)rrd_stats_dimension_get(rd, t);
839
+ oldtotal += (long double)rrd_stats_dimension_get(rd, lt);
840
+ }
841
+ }
842
+
843
+ for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
844
+ //long long oldvalue, value; // temp variable for storing data values
845
+ long double oldvalue, value; // temp variable for storing data values
846
+
847
+ value = (long double)rrd_stats_dimension_get(rd, t);
848
+ oldvalue = (long double)rrd_stats_dimension_get(rd, lt);
849
+
850
+ switch(rd->type) {
851
+ case RRD_DIMENSION_PCENT_OVER_TOTAL:
852
+ value = (long double)100 * (value - oldvalue) / (total - oldtotal);
853
+ break;
854
+
855
+ case RRD_DIMENSION_INCREMENTAL:
856
+ if(oldvalue > value) value = 0; // detect overflows and resets
857
+ else value -= oldvalue;
858
+ value = value * (long double)1000000L / (long double)usec;
859
+ break;
860
+
861
+ default:
862
+ break;
863
+ }
864
+
865
+ value = value * (long double)rd->multiplier / (long double)rd->divisor;
866
+
867
+ group_counts[c]++;
868
+ switch(group_method) {
869
+ case GROUP_MAX:
870
+ if(abs(value) > abs(group_values[c])) group_values[c] = value;
871
+ break;
872
+
873
+ default:
874
+ case GROUP_AVERAGE:
875
+ group_values[c] += value;
876
+ if(((count - pad) % group_count) == 0) group_values[c] /= (long double)group_counts[c];
877
+ break;
878
+ }
879
+
880
+ if(((count-pad) % group_count) == 0) {
881
+ if(!rd->hidden)
882
+ i += sprintf(&b[i], ",{\"v\":%0.1Lf}", group_values[c]);
883
+ //i += sprintf(&b[i], ",{\"v\":%lld}", group_values[c]);
884
+
885
+ group_values[c] = group_counts[c] = 0;
886
+ }
887
+ }
888
+ }
889
+ if(printed) i += sprintf(&b[i], "]}");
890
+ i += sprintf(&b[i], "\n ]\n}\n");
891
+
892
+ pthread_mutex_unlock(&st->mutex);
893
+ return(i);
894
+}
895
+
896
+// ----------------------------------------------------------------------------
897
+// listener (web server)
898
+
899
+struct web_buffer {
900
+ size_t size; // allocation size of buffer
901
+ size_t bytes; // current data length in buffer
902
+ size_t sent; // current data length sent to output
903
+ char *buffer; // the buffer
904
+ int contenttype;
905
+ size_t rbytes; // if non-zero, the excepted size of ifd
906
+ time_t date; // the date this content has been generated
907
+};
908
+
909
+struct web_buffer *web_buffer_create(size_t size)
910
+{
911
+ struct web_buffer *b;
912
+
913
+ debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size);
914
+
915
+ b = calloc(1, sizeof(struct web_buffer));
916
+ if(!b) {
917
+ error("Cannot allocate a web_buffer.");
918
+ return NULL;
919
+ }
920
+
921
+ b->buffer = calloc(1, size);
922
+ if(!b->buffer) {
923
+ error("Cannot allocate a buffer of size %u.", size);
924
+ free(b);
925
+ return NULL;
926
+ }
927
+ b->size = size;
928
+ b->contenttype = CT_TEXT_PLAIN;
929
+ return(b);
930
+}
931
+
932
+void web_buffer_free(struct web_buffer *b)
933
+{
934
+ debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size);
935
+
936
+ if(b->buffer) free(b->buffer);
937
+ free(b);
938
+}
939
+
940
+void web_buffer_increase(struct web_buffer *b, size_t free_size_required)
941
+{
942
+ size_t left = b->size - b->bytes;
943
+
944
+ if(left >= free_size_required) return;
945
+ size_t increase = free_size_required - left;
946
+
947
+ debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
948
+
949
+ b->buffer = realloc(b->buffer, b->size + free_size_required - left);
950
+ if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size, b->size + increase);
951
+
952
+ b->size += increase;
953
+}
954
+
955
+#define WEB_CLIENT_MODE_NORMAL 0
956
+#define WEB_CLIENT_MODE_FILECOPY 1
957
+
958
+struct web_client {
959
+ unsigned long long id;
960
+ char client_ip[101];
961
+
962
+ int mode;
963
+ int keepalive;
964
+
965
+ struct sockaddr_in clientaddr;
966
+
967
+ pthread_t thread; // the thread servicing this client
968
+ int obsolete; // if set to 1, the listener will remove this client
969
+
970
+ int ifd;
971
+ int ofd;
972
+
973
+ struct web_buffer *data;
974
+
975
+ int zoutput; // if set to 1, web_client_send() will send compressed data
976
+ z_stream zstream; // zlib stream for sending compressed output to client
977
+ Bytef zbuffer[ZLIB_CHUNK]; // temporary buffer for storing compressed output
978
+ size_t zsent; // the compressed bytes we have sent to the client
979
+ size_t zhave; // the compressed bytes that we have to send
980
+ int zinitialized;
981
+
982
+ int wait_receive;
983
+ int wait_send;
984
+
985
+ char response_header[MAX_HTTP_HEADER_SIZE+1];
986
+
987
+ struct web_client *prev;
988
+ struct web_client *next;
989
+} *web_clients = NULL;
990
+
991
+unsigned long long web_clients_count = 0;
992
+
993
+struct web_client *web_client_create(int listener)
994
+{
995
+ struct web_client *w;
996
+ socklen_t addrlen;
997
+
998
+ w = calloc(1, sizeof(struct web_client));
999
+ if(!w) {
1000
+ error("Cannot allocate new web_client memory.");
1001
+ return NULL;
1002
+ }
1003
+
1004
+ w->id = ++web_clients_count;
1005
+ w->mode = WEB_CLIENT_MODE_NORMAL;
1006
+
1007
+ addrlen = sizeof(w->clientaddr);
1008
+ w->ifd = accept(listener, (struct sockaddr *)&w->clientaddr, &addrlen);
1009
+ if (w->ifd == -1) {
1010
+ error("%llu: Cannot accept new incoming connection.", w->id);
1011
+ free(w);
1012
+ return NULL;
1013
+ }
1014
+ w->ofd = w->ifd;
1015
+
1016
+ strncpy(w->client_ip, inet_ntoa(w->clientaddr.sin_addr), 100);
1017
+ w->client_ip[100] = '\0';
1018
+
1019
+ syslog(LOG_NOTICE, "%llu: New client from %s.", w->id, w->client_ip);
1020
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New web client from %s on socket %d.", w->id, w->client_ip, w->ifd);
1021
+
1022
+ {
1023
+ int flag = 1;
1024
+ if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
1025
+ }
1026
+
1027
+ w->data = web_buffer_create(MAX_SOCKET_DATA_LENGTH);
1028
+ if(!w->data) {
1029
+ close(w->ifd);
1030
+ free(w);
1031
+ return NULL;
1032
+ }
1033
+
1034
+ w->wait_receive = 1;
1035
+
1036
+ if(web_clients) web_clients->prev = w;
1037
+ w->next = web_clients;
1038
+ web_clients = w;
1039
+
1040
+ return(w);
1041
+}
1042
+
1043
+struct web_client *web_client_free(struct web_client *w)
1044
+{
1045
+ struct web_client *n = w->next;
1046
+
1047
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s.", w->id, inet_ntoa(w->clientaddr.sin_addr));
1048
+
1049
+ if(w->prev) w->prev->next = w->next;
1050
+ if(w->next) w->next->prev = w->prev;
1051
+
1052
+ if(w == web_clients) web_clients = w->next;
1053
+
1054
+ if(w->data) web_buffer_free(w->data);
1055
+ close(w->ifd);
1056
+ if(w->ofd != w->ifd) close(w->ofd);
1057
+ free(w);
1058
+
1059
+ return(n);
1060
+}
1061
+
1062
+int mysendfile(struct web_client *w, char *filename)
1063
+{
1064
+ debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
1065
+
1066
+ // skip leading slashes
1067
+ while (filename[0] == '/') filename = &filename[1];
1068
+
1069
+ // if the filename contain known paths, skip them
1070
+ if(strncmp(filename, WEB_PATH_DATA "/", strlen(WEB_PATH_DATA) + 1) == 0) filename = &filename[strlen(WEB_PATH_DATA) + 1];
1071
+ else if(strncmp(filename, WEB_PATH_GRAPH "/", strlen(WEB_PATH_GRAPH) + 1) == 0) filename = &filename[strlen(WEB_PATH_GRAPH) + 1];
1072
+ else if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0) filename = &filename[strlen(WEB_PATH_FILE) + 1];
1073
+
1074
+ // if the filename contains a / or a .., refuse to serve it
1075
+ if(strstr(filename, "/") != 0 || strstr(filename, "..") != 0) {
1076
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
1077
+ w->data->bytes = sprintf(w->data->buffer, "File '%s' cannot be served. Filenames cannot contain / or ..", filename);
1078
+ return 400;
1079
+ }
1080
+
1081
+ // access the file in web/*
1082
+ char webfilename[FILENAME_MAX + 1];
1083
+ strcpy(webfilename, "web/");
1084
+ strncpy(&webfilename[4], filename, FILENAME_MAX - 4);
1085
+ webfilename[FILENAME_MAX] = '\0';
1086
+
1087
+ // check if the file exists
1088
+ struct stat stat;
1089
+ if(lstat(webfilename, &stat) != 0) {
1090
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, filename);
1091
+ w->data->bytes = sprintf(w->data->buffer, "File '%s' does not exist, or is not accessible.", filename);
1092
+ return 404;
1093
+ }
1094
+
1095
+ // check if the file is owned by us
1096
+ if(stat.st_uid != getuid() && stat.st_uid != geteuid()) {
1097
+ error("%llu: File '%s' is owned by user %d (I run as user %d). Access Denied.", w->id, filename, stat.st_uid, getuid());
1098
+ w->data->bytes = sprintf(w->data->buffer, "Access to file '%s' is not permitted.", filename);
1099
+ return 403;
1100
+ }
1101
+
1102
+ // open the file
1103
+ w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
1104
+ if(w->ifd == -1) {
1105
+ w->ifd = w->ofd;
1106
+
1107
+ if(errno == EBUSY || errno == EAGAIN) {
1108
+ error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, filename);
1109
+ sprintf(w->response_header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
1110
+ w->data->bytes = sprintf(w->data->buffer, "The file '%s' is currently busy. Please try again later.", filename);
1111
+ return 307;
1112
+ }
1113
+ else {
1114
+ error("%llu: Cannot open file '%s'.", w->id, filename);
1115
+ w->data->bytes = sprintf(w->data->buffer, "Cannot open file '%s'.", filename);
1116
+ return 404;
1117
+ }
1118
+ }
1119
+
1120
+ syslog(LOG_NOTICE, "%llu: Sending file '%s' to client %s.", w->id, filename, w->client_ip);
1121
+
1122
+ // pick a Content-Type for the file
1123
+ if(strstr(filename, ".html") != NULL) w->data->contenttype = CT_TEXT_HTML;
1124
+ else if(strstr(filename, ".js") != NULL) w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
1125
+ else if(strstr(filename, ".css") != NULL) w->data->contenttype = CT_TEXT_CSS;
1126
+ else if(strstr(filename, ".xml") != NULL) w->data->contenttype = CT_TEXT_XML;
1127
+ else if(strstr(filename, ".xsl") != NULL) w->data->contenttype = CT_TEXT_XSL;
1128
+ else if(strstr(filename, ".txt") != NULL) w->data->contenttype = CT_TEXT_PLAIN;
1129
+ else if(strstr(filename, ".svg") != NULL) w->data->contenttype = CT_IMAGE_SVG_XML;
1130
+ else if(strstr(filename, ".ttf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
1131
+ else if(strstr(filename, ".otf") != NULL) w->data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
1132
+ else if(strstr(filename, ".woff") != NULL) w->data->contenttype = CT_APPLICATION_FONT_WOFF;
1133
+ else if(strstr(filename, ".eot") != NULL) w->data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
1134
+ else w->data->contenttype = CT_APPLICATION_OCTET_STREAM;
1135
+
1136
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, filename, stat.st_size, w->ifd, w->ofd);
1137
+
1138
+ w->mode = WEB_CLIENT_MODE_FILECOPY;
1139
+ w->wait_receive = 1;
1140
+ w->wait_send = 0;
1141
+ w->data->bytes = 0;
1142
+ w->data->buffer[0] = '\0';
1143
+ w->data->rbytes = stat.st_size;
1144
+ w->data->date = stat.st_mtim.tv_sec;
1145
+
1146
+ return 200;
1147
+}
1148
+
1149
+char *mystrsep(char **ptr, char *s)
1150
+{
1151
+ char *p = "";
1152
+ while ( !p[0] && *ptr ) p = strsep(ptr, s);
1153
+ return(p);
1154
+}
1155
+
1156
+void web_client_reset(struct web_client *w)
1157
+{
1158
+ debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
1159
+
1160
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1161
+ debug(D_WEB_CLIENT, "%llu: Closing filecopy input file.", w->id);
1162
+ close(w->ifd);
1163
+ w->ifd = w->ofd;
1164
+ }
1165
+
1166
+ w->data->contenttype = CT_TEXT_PLAIN;
1167
+ w->mode = WEB_CLIENT_MODE_NORMAL;
1168
+
1169
+ w->data->rbytes = 0;
1170
+ w->data->bytes = 0;
1171
+ w->data->sent = 0;
1172
+
1173
+ w->response_header[0] = '\0';
1174
+ w->data->buffer[0] = '\0';
1175
+
1176
+ w->wait_receive = 1;
1177
+ w->wait_send = 0;
1178
+
1179
+ // if we had enabled compression, release it
1180
+ if(w->zinitialized) {
1181
+ debug(D_DEFLATE, "%llu: Reseting compression.", w->id);
1182
+ deflateEnd(&w->zstream);
1183
+ w->zoutput = 0;
1184
+ w->zsent = 0;
1185
+ w->zhave = 0;
1186
+ w->zstream.avail_in = 0;
1187
+ w->zstream.avail_out = 0;
1188
+ w->zstream.total_in = 0;
1189
+ w->zstream.total_out = 0;
1190
+ w->zinitialized = 0;
1191
+ }
1192
+}
1193
+
1194
+void web_client_enable_deflate(struct web_client *w) {
1195
+ if(w->zinitialized == 1) {
1196
+ error("%llu: Compression has already be initialized for this client.", w->id);
1197
+ return;
1198
+ }
1199
+
1200
+ if(w->data->sent) {
1201
+ error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
1202
+ return;
1203
+ }
1204
+
1205
+ w->zstream.zalloc = Z_NULL;
1206
+ w->zstream.zfree = Z_NULL;
1207
+ w->zstream.opaque = Z_NULL;
1208
+
1209
+ w->zstream.next_in = (Bytef *)w->data->buffer;
1210
+ w->zstream.avail_in = 0;
1211
+ w->zstream.total_in = 0;
1212
+
1213
+ w->zstream.next_out = w->zbuffer;
1214
+ w->zstream.avail_out = 0;
1215
+ w->zstream.total_out = 0;
1216
+
1217
+ w->zstream.zalloc = Z_NULL;
1218
+ w->zstream.zfree = Z_NULL;
1219
+ w->zstream.opaque = Z_NULL;
1220
+
1221
+// if(deflateInit(&w->zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
1222
+// error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
1223
+// return;
1224
+// }
1225
+
1226
+ // Select GZIP compression: windowbits = 15 + 16 = 31
1227
+ if(deflateInit2(&w->zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
1228
+ error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
1229
+ return;
1230
+ }
1231
+
1232
+ w->zsent = 0;
1233
+ w->zoutput = 1;
1234
+ w->zinitialized = 1;
1235
+
1236
+ debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
1237
+}
1238
+
1239
+void web_client_process(struct web_client *w)
1240
+{
1241
+ int code = 500;
1242
+ int bytes;
1243
+
1244
+ w->wait_receive = 0;
1245
+
1246
+ // check if we have an empty line (end of HTTP header)
1247
+ if(strstr(w->data->buffer, "\r\n\r\n")) {
1248
+ debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer);
1249
+
1250
+ // check if the client requested keep-alive HTTP
1251
+ if(strcasestr(w->data->buffer, "Connection: keep-alive")) w->keepalive = 1;
1252
+ else w->keepalive = 0;
1253
+
1254
+ // check if the client accepts deflate
1255
+ if(strstr(w->data->buffer, "gzip"))
1256
+ web_client_enable_deflate(w);
1257
+
1258
+ char *buf = w->data->buffer;
1259
+ char *tok = strsep(&buf, " \r\n");
1260
+ char *url = NULL;
1261
+
1262
+ if(buf && strcmp(tok, "GET") == 0) {
1263
+ tok = strsep(&buf, " \r\n");
1264
+ url = url_decode(tok);
1265
+ debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
1266
+ }
1267
+ else if (buf && strcmp(tok, "POST") == 0) {
1268
+ w->keepalive = 0;
1269
+ tok = strsep(&buf, " \r\n");
1270
+ url = url_decode(tok);
1271
+
1272
+ 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);
1273
+ }
1274
+
1275
+ if(url) {
1276
+ tok = mystrsep(&url, "/?&");
1277
+
1278
+ debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
1279
+
1280
+ if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
1281
+ // the client is requesting rrd data
1282
+
1283
+ // get the name of the data to show
1284
+ tok = mystrsep(&url, "/?&");
1285
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1286
+
1287
+ // do we have such a data set?
1288
+ RRD_STATS *st = rrd_stats_find_byname(tok);
1289
+ if(!st) st = rrd_stats_find(tok);
1290
+ if(!st) {
1291
+ // we don't have it
1292
+ // try to send a file with that name
1293
+ code = mysendfile(w, tok);
1294
+ }
1295
+ else {
1296
+ // we have it
1297
+ debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
1298
+
1299
+ // how many entries does the client want?
1300
+ size_t lines = save_history;
1301
+ size_t group_count = 1;
1302
+ int group_method = GROUP_AVERAGE;
1303
+
1304
+ if(url) {
1305
+ // parse the lines required
1306
+ tok = mystrsep(&url, "/?&");
1307
+ if(tok) lines = atoi(tok);
1308
+ if(lines < 5) lines = save_history;
1309
+ }
1310
+ if(url) {
1311
+ // parse the group count required
1312
+ tok = mystrsep(&url, "/?&");
1313
+ if(tok) group_count = atoi(tok);
1314
+ if(group_count < 1) group_count = 1;
1315
+ if(group_count > save_history / 20) group_count = save_history / 20;
1316
+ }
1317
+ if(url) {
1318
+ // parse the grouping method required
1319
+ tok = mystrsep(&url, "/?&");
1320
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
1321
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
1322
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
1323
+ }
1324
+
1325
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group_count, %d group_method).", w->id, st->name, st->id, lines, group_count, group_method);
1326
+
1327
+ code = 200;
1328
+ w->data->contenttype = CT_APPLICATION_JSON;
1329
+ w->data->bytes = rrd_stats_json(st, w->data->buffer, w->data->size, lines, group_count, group_method);
1330
+ }
1331
+ }
1332
+ else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
1333
+ // the client is requesting an rrd graph
1334
+
1335
+ // get the name of the data to show
1336
+ tok = mystrsep(&url, "/?&");
1337
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
1338
+
1339
+ // do we have such a data set?
1340
+ RRD_STATS *st = rrd_stats_find_byname(tok);
1341
+ if(!st) {
1342
+ // we don't have it
1343
+ // try to send a file with that name
1344
+ code = mysendfile(w, tok);
1345
+ }
1346
+ else {
1347
+ code = 200;
1348
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
1349
+ w->data->contenttype = CT_APPLICATION_JSON;
1350
+ w->data->bytes = rrd_stats_graph_json(st, url, w->data->buffer, w->data->size);
1351
+ }
1352
+ }
1353
+ else if(strcmp(tok, "mirror") == 0) {
1354
+ code = 200;
1355
+
1356
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
1357
+
1358
+ // just leave the buffer as is
1359
+ // it will be copied back to the client
1360
+ }
1361
+ else if(strcmp(tok, "list") == 0) {
1362
+ code = 200;
1363
+
1364
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
1365
+
1366
+ w->data->bytes = 0;
1367
+ RRD_STATS *st = root;
1368
+
1369
+ for ( ; st ; st = st->next )
1370
+ w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s\n", st->name);
1371
+ }
1372
+ else if(strcmp(tok, "envlist") == 0) {
1373
+ code = 200;
1374
+
1375
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending envlist of RRD_STATS...", w->id);
1376
+
1377
+ w->data->bytes = 0;
1378
+ RRD_STATS *st = root;
1379
+
1380
+ for ( ; st ; st = st->next ) {
1381
+ w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s=%s\n", st->envtitle, st->usertitle);
1382
+ w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s=%s\n", st->envpriority, st->userpriority);
1383
+ }
1384
+ }
1385
+ else if(strcmp(tok, "all.json") == 0) {
1386
+ code = 200;
1387
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending all.json of RRD_STATS...", w->id);
1388
+ w->data->contenttype = CT_APPLICATION_JSON;
1389
+ w->data->bytes = rrd_stats_all_json(w->data->buffer, w->data->size);
1390
+ }
1391
+ else if(strcmp(tok, WEB_PATH_FILE) == 0) { // "file"
1392
+ tok = mystrsep(&url, "/?&");
1393
+ if(tok && *tok) code = mysendfile(w, tok);
1394
+ else {
1395
+ code = 400;
1396
+ strcpy(w->data->buffer, "You have to give a filename to get.\r\n");
1397
+ w->data->bytes = strlen(w->data->buffer);
1398
+ }
1399
+ }
1400
+ else if(!tok[0]) {
1401
+ code = mysendfile(w, "index.html");
1402
+ }
1403
+ else {
1404
+ code = mysendfile(w, tok);
1405
+ }
1406
+ }
1407
+ else {
1408
+ if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
1409
+
1410
+ code = 500;
1411
+ strcpy(w->data->buffer, "I don't understand you...\r\n");
1412
+ w->data->bytes = strlen(w->data->buffer);
1413
+ }
1414
+ }
1415
+ else if(w->data->bytes > 8192) {
1416
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big.", w->id);
1417
+
1418
+ code = 400;
1419
+ strcpy(w->data->buffer, "Received request is too big.\r\n");
1420
+ w->data->bytes = strlen(w->data->buffer);
1421
+ }
1422
+ else {
1423
+ // wait for more data
1424
+ w->wait_receive = 1;
1425
+ return;
1426
+ }
1427
+
1428
+ w->data->date = time(NULL);
1429
+ w->data->sent = 0;
1430
+
1431
+ // prepare the HTTP response header
1432
+ debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
1433
+
1434
+ char *content_type_string = "";
1435
+ switch(w->data->contenttype) {
1436
+ case CT_TEXT_HTML:
1437
+ content_type_string = "text/html";
1438
+ break;
1439
+
1440
+ case CT_APPLICATION_XML:
1441
+ content_type_string = "application/xml";
1442
+ break;
1443
+
1444
+ case CT_APPLICATION_JSON:
1445
+ content_type_string = "application/json";
1446
+ break;
1447
+
1448
+ case CT_APPLICATION_X_JAVASCRIPT:
1449
+ content_type_string = "application/x-javascript";
1450
+ break;
1451
+
1452
+ case CT_TEXT_CSS:
1453
+ content_type_string = "text/css";
1454
+ break;
1455
+
1456
+ case CT_TEXT_XML:
1457
+ content_type_string = "text/xml";
1458
+ break;
1459
+
1460
+ case CT_TEXT_XSL:
1461
+ content_type_string = "text/xsl";
1462
+ break;
1463
+
1464
+ case CT_APPLICATION_OCTET_STREAM:
1465
+ content_type_string = "application/octet-stream";
1466
+ break;
1467
+
1468
+ case CT_IMAGE_SVG_XML:
1469
+ content_type_string = "image/svg+xml";
1470
+ break;
1471
+
1472
+ case CT_APPLICATION_X_FONT_TRUETYPE:
1473
+ content_type_string = "application/x-font-truetype";
1474
+ break;
1475
+
1476
+ case CT_APPLICATION_X_FONT_OPENTYPE:
1477
+ content_type_string = "application/x-font-opentype";
1478
+ break;
1479
+
1480
+ case CT_APPLICATION_FONT_WOFF:
1481
+ content_type_string = "application/font-woff";
1482
+ break;
1483
+
1484
+ case CT_APPLICATION_VND_MS_FONTOBJ:
1485
+ content_type_string = "application/vnd.ms-fontobject";
1486
+ break;
1487
+
1488
+ default:
1489
+ case CT_TEXT_PLAIN:
1490
+ content_type_string = "text/plain";
1491
+ break;
1492
+ }
1493
+
1494
+ char *code_msg = "";
1495
+ switch(code) {
1496
+ case 200:
1497
+ code_msg = "OK";
1498
+ break;
1499
+
1500
+ case 307:
1501
+ code_msg = "Temporary Redirect";
1502
+ break;
1503
+
1504
+ case 400:
1505
+ code_msg = "Bad Request";
1506
+ break;
1507
+
1508
+ case 403:
1509
+ code_msg = "Forbidden";
1510
+ break;
1511
+
1512
+ case 404:
1513
+ code_msg = "Not Found";
1514
+ break;
1515
+
1516
+ default:
1517
+ code_msg = "Internal Server Error";
1518
+ break;
1519
+ }
1520
+
1521
+ char date[100];
1522
+ struct tm tm = *gmtime(&w->data->date);
1523
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm);
1524
+
1525
+ char custom_header[MAX_HTTP_HEADER_SIZE + 1] = "";
1526
+ if(w->response_header[0])
1527
+ strcpy(custom_header, w->response_header);
1528
+
1529
+ size_t headerlen = 0;
1530
+ headerlen += sprintf(&w->response_header[headerlen],
1531
+ "HTTP/1.1 %d %s\r\n"
1532
+ "Connection: %s\r\n"
1533
+ "Server: Data Collector HTTP Server\r\n"
1534
+ "Content-Type: %s\r\n"
1535
+ "Access-Control-Allow-Origin: *\r\n"
1536
+ "Date: %s\r\n"
1537
+ , code, code_msg
1538
+ , w->keepalive?"keep-alive":"close"
1539
+ , content_type_string
1540
+ , date
1541
+ );
1542
+
1543
+ if(custom_header[0])
1544
+ headerlen += sprintf(&w->response_header[headerlen], "%s", custom_header);
1545
+
1546
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) {
1547
+ headerlen += sprintf(&w->response_header[headerlen],
1548
+ "Expires: %s\r\n"
1549
+ "Cache-Control: no-cache\r\n"
1550
+ , date
1551
+ );
1552
+ }
1553
+ else {
1554
+ headerlen += sprintf(&w->response_header[headerlen],
1555
+ "Cache-Control: public\r\n"
1556
+ );
1557
+ }
1558
+
1559
+ // if we know the content length, put it
1560
+ if(!w->zoutput && (w->data->bytes || w->data->rbytes))
1561
+ headerlen += sprintf(&w->response_header[headerlen],
1562
+ "Content-Length: %ld\r\n"
1563
+ , w->data->bytes?w->data->bytes:w->data->rbytes
1564
+ );
1565
+ else if(!w->zoutput)
1566
+ w->keepalive = 0; // content-length is required for keep-alive
1567
+
1568
+ if(w->zoutput) {
1569
+ headerlen += sprintf(&w->response_header[headerlen],
1570
+ "Content-Encoding: gzip\r\n"
1571
+ "Transfer-Encoding: chunked\r\n"
1572
+ );
1573
+ }
1574
+
1575
+ headerlen += sprintf(&w->response_header[headerlen], "\r\n");
1576
+
1577
+ // disable TCP_NODELAY, to buffer the header
1578
+ int flag = 0;
1579
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
1580
+
1581
+ // sent the HTTP header
1582
+ debug(D_WEB_CLIENT, "%llu: Sending response HTTP header of size %d: '%s'", w->id, headerlen, w->response_header);
1583
+
1584
+ bytes = send(w->ofd, w->response_header, headerlen, 0);
1585
+ if(bytes != headerlen)
1586
+ error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes);
1587
+
1588
+ // enable TCP_NODELAY, to send all data immediately at the next send()
1589
+ flag = 1;
1590
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
1591
+
1592
+ // enable sending immediately if we have data
1593
+ if(w->data->bytes) w->wait_send = 1;
1594
+ else w->wait_send = 0;
1595
+
1596
+ // pretty logging
1597
+ switch(w->mode) {
1598
+ case WEB_CLIENT_MODE_NORMAL:
1599
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->bytes);
1600
+ break;
1601
+
1602
+ case WEB_CLIENT_MODE_FILECOPY:
1603
+ if(w->data->rbytes) {
1604
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rbytes);
1605
+ w->wait_receive = 1;
1606
+
1607
+ /*
1608
+ // utilize the kernel sendfile() for copying the file to the socket.
1609
+ // this block of code can be commented, without anything missing.
1610
+ // when it is commented, the program will copy the data using async I/O.
1611
+ {
1612
+ ssize_t len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes);
1613
+ 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);
1614
+ else web_client_reset(w);
1615
+ }
1616
+ */
1617
+ }
1618
+ else
1619
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
1620
+ break;
1621
+
1622
+ default:
1623
+ fatal("%llu: Unknown client mode %d.", w->id, w->mode);
1624
+ break;
1625
+ }
1626
+}
1627
+
1628
+void web_client_send_chunk_header(struct web_client *w, int len)
1629
+{
1630
+ debug(D_DEFLATE, "%llu: OPEN CHUNK of %d bytes (hex: %x).", w->id, len, len);
1631
+ char buf[1024];
1632
+ sprintf(buf, "%X\r\n", len);
1633
+ int bytes = send(w->ofd, buf, strlen(buf), MSG_DONTWAIT);
1634
+
1635
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk header %d bytes.", w->id, bytes);
1636
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk header to the client.", w->id);
1637
+ else debug(D_DEFLATE, "%llu: Failed to send chunk header to client.", w->id);
1638
+}
1639
+
1640
+void web_client_send_chunk_close(struct web_client *w)
1641
+{
1642
+ debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
1643
+
1644
+ int bytes = send(w->ofd, "\r\n", 2, MSG_DONTWAIT);
1645
+
1646
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
1647
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
1648
+ else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client.", w->id);
1649
+}
1650
+
1651
+void web_client_send_chunk_finalize(struct web_client *w)
1652
+{
1653
+ debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
1654
+
1655
+ int bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, MSG_DONTWAIT);
1656
+
1657
+ if(bytes > 0) debug(D_DEFLATE, "%llu: Sent chunk suffix %d bytes.", w->id, bytes);
1658
+ else if(bytes == 0) debug(D_DEFLATE, "%llu: Did not send chunk suffix to the client.", w->id);
1659
+ else debug(D_DEFLATE, "%llu: Failed to send chunk suffix to client.", w->id);
1660
+}
1661
+
1662
+ssize_t web_client_send_deflate(struct web_client *w)
1663
+{
1664
+ ssize_t bytes;
1665
+
1666
+ // when using compression,
1667
+ // w->data->sent is the amount of bytes passed through compression
1668
+
1669
+ 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);
1670
+
1671
+ if(w->data->bytes - w->data->sent == 0 && w->zstream.avail_in == 0 && w->zhave == w->zsent && w->zstream.avail_out != 0) {
1672
+ // there is nothing to send
1673
+
1674
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
1675
+
1676
+ // finalize the chunk
1677
+ if(w->data->sent != 0)
1678
+ web_client_send_chunk_finalize(w);
1679
+
1680
+ // there can be two cases for this
1681
+ // A. we have done everything
1682
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
1683
+
1684
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rbytes && w->data->rbytes > w->data->bytes) {
1685
+ // we have to wait, more data will come
1686
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
1687
+ w->wait_send = 0;
1688
+ return(0);
1689
+ }
1690
+
1691
+ if(w->keepalive == 0) {
1692
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent);
1693
+ errno = 0;
1694
+ return(-1);
1695
+ }
1696
+
1697
+ // reset the client
1698
+ web_client_reset(w);
1699
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
1700
+ return(0);
1701
+ }
1702
+
1703
+ if(w->zstream.avail_out == 0 && w->zhave == w->zsent) {
1704
+ // compress more input data
1705
+
1706
+ // close the previous open chunk
1707
+ if(w->data->sent != 0) web_client_send_chunk_close(w);
1708
+
1709
+ debug(D_DEFLATE, "%llu: Compressing %d bytes starting from %d.", w->id, (w->data->bytes - w->data->sent), w->data->sent);
1710
+
1711
+ // give the compressor all the data not passed through the compressor yet
1712
+ if(w->data->bytes > w->data->sent) {
1713
+ w->zstream.next_in = (Bytef *)&w->data->buffer[w->data->sent];
1714
+ w->zstream.avail_in = (w->data->bytes - w->data->sent);
1715
+ }
1716
+
1717
+ // reset the compressor output buffer
1718
+ w->zstream.next_out = w->zbuffer;
1719
+ w->zstream.avail_out = ZLIB_CHUNK;
1720
+
1721
+ // ask for FINISH if we have all the input
1722
+ int flush = Z_SYNC_FLUSH;
1723
+ if(w->mode == WEB_CLIENT_MODE_NORMAL
1724
+ || (w->mode == WEB_CLIENT_MODE_FILECOPY && w->data->bytes == w->data->rbytes)) {
1725
+ flush = Z_FINISH;
1726
+ debug(D_DEFLATE, "%llu: Requesting Z_FINISH.", w->id);
1727
+ }
1728
+ else {
1729
+ debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
1730
+ }
1731
+
1732
+ // compress
1733
+ if(deflate(&w->zstream, flush) == Z_STREAM_ERROR) {
1734
+ error("%llu: Compression failed. Closing down client.", w->id);
1735
+ web_client_reset(w);
1736
+ return(-1);
1737
+ }
1738
+
1739
+ w->zhave = ZLIB_CHUNK - w->zstream.avail_out;
1740
+ w->zsent = 0;
1741
+
1742
+ // keep track of the bytes passed through the compressor
1743
+ w->data->sent = w->data->bytes;
1744
+
1745
+ debug(D_DEFLATE, "%llu: Compression produced %d bytes.", w->id, w->zhave);
1746
+
1747
+ // open a new chunk
1748
+ web_client_send_chunk_header(w, w->zhave);
1749
+ }
1750
+
1751
+ bytes = send(w->ofd, &w->zbuffer[w->zsent], w->zhave - w->zsent, MSG_DONTWAIT);
1752
+ if(bytes > 0) {
1753
+ w->zsent += bytes;
1754
+ debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
1755
+ }
1756
+ else if(bytes == 0) debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
1757
+ else debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
1758
+
1759
+ return(bytes);
1760
+}
1761
+
1762
+ssize_t web_client_send(struct web_client *w)
1763
+{
1764
+ if(w->zoutput) return web_client_send_deflate(w);
1765
+
1766
+ ssize_t bytes;
1767
+
1768
+ if(w->data->bytes - w->data->sent == 0) {
1769
+ // there is nothing to send
1770
+
1771
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
1772
+
1773
+ // there can be two cases for this
1774
+ // A. we have done everything
1775
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
1776
+
This file is too large to show in full.
netdata.start
+2
-2
@@ -39,9 +39,9 @@ echo "Compiling netdata"
39
if [ $NETDATA_CONFIG_DEBUG -eq 1 ]
40
then
41
ulimit -c unlimited
42
- gcc -Wall -ggdb -o netdata netdata.c -lpthread || exit 1
42
+ gcc -Wall -ggdb -o netdata netdata.c -lpthread -lz || exit 1
43
else
44
- gcc -Wall -O3 -o netdata netdata.c -lpthread || exit 1
44
+ gcc -Wall -O3 -o netdata netdata.c -lpthread -lz || exit 1
45
fi
46
47
# exporting NETDATA variables to netdata
web/index.html
+3
-8
@@ -168,19 +168,14 @@
168
169
width = thumbWidth();
170
$.each(mycharts, function(i, c) {
171
- var now = new Date().getTime();
172
-
171
if(c.enabled && c.chartOptions.width != width) {
172
if(c.chart) c.chart.clearChart();
173
c.chart = null;
176
- c.last_updated = now + (c.update_every * 1000);
174
c.chartOptions.width = width;
175
showChartIsLoading(c.div, c.chartOptions.width, c.chartOptions.height);
176
c.last_updated = 0;
177
}
178
});
182
-
183
- chartsRefresh();
179
}
180
181
// load the charts from the server
@@ -389,7 +384,7 @@
384
<tr><td align="center">
385
<div class="btn-group">
386
<form id="mainchartform">
392
- <div class="btn-group" data-toggle="buttons">
387
+ <div class="btn-group btn-group-xs" data-toggle="buttons">
388
<label class="btn btn-primary" data-toggle="tooltip" title="show the MAX point among all points grouped">
389
<input type="radio" name="groupmethod" id="groupmax" onChange="setMainChartGroupMethod('max');">
390
<span class="glyphicon glyphicon-signal"></span>
@@ -399,7 +394,7 @@
394
<span class="glyphicon glyphicon-align-justify"></span>
395
</label>
396
</div>
402
- <div class="btn-group" data-toggle="buttons" >
397
+ <div class="btn-group btn-group-xs" data-toggle="buttons" >
398
<label class="btn btn-primary" data-toggle="tooltip" title="do not group points, show the raw data">
399
<input type="radio" name="group" id="group1" onChange="setMainChartGroup(1);">1
400
</label>
@@ -430,7 +425,7 @@
425
<input type="radio" name="group" id="group60" onChange="setMainChartGroup(60);">60
426
</label>
427
</div>
433
- <div class="btn-group" data-toggle="buttons" >
428
+ <div class="btn-group btn-group-xs" data-toggle="buttons" >
429
<label class="btn btn-primary" data-toggle="tooltip" title="NORMAL chart size">
430
<input type="radio" name="chartmax" id="chartnormal" onChange="setMainChartMax('normal');">
431
<span class="glyphicon glyphicon-resize-small"></span>