total rewrite, now with an embeded web server
Costa Tsaousis (ktsaou) committed
Mar 22, 2014 at 09:49 UTC
427005fc7fc805797fac27e1922ce9386c581c1e
1 file changed
+1416
-300
netdata.c
+1416
-300
@@ -1,300 +1,1416 @@
1
-#include <stdio.h>
2
-#include <stdlib.h>
3
-#include <string.h>
4
-#include <time.h>
5
-#include <unistd.h>
6
-#include <sys/types.h>
7
-#include <sys/time.h>
8
-
9
-// internal defaults
10
-#define UPDATE_EVERY 1
11
-#define HISTORY 3600
12
-#define SAVE_PATH "/tmp"
13
-
14
-#define DEBUG 0
15
-
16
-// configuration
17
-#define MAX_LINE 4096
18
-#define MAX_IFACE_NAME 1024
19
-char save_path[FILENAME_MAX+1] = SAVE_PATH;
20
-int update_every = UPDATE_EVERY;
21
-int save_history = 60;
22
-
23
-struct iface_history {
24
- time_t time;
25
- unsigned long long usec;
26
-
27
- unsigned long long rbytes;
28
- unsigned long long tbytes;
29
-};
30
-
31
-struct iface_stats {
32
- char name[MAX_IFACE_NAME + 1];
33
-
34
- int last_history_id;
35
- struct iface_history history[HISTORY];
36
-
37
- struct iface_stats *next;
38
-} *interfaces = NULL;
39
-
40
-void update_iface_history(unsigned long long usec, char *name, unsigned long long rbytes, unsigned long long tbytes) {
41
- struct iface_stats *iface = NULL;
42
-
43
- for(iface = interfaces; iface != NULL; iface = iface->next)
44
- if(strcmp(iface->name, name) == 0) break;
45
-
46
- if(!iface) {
47
- int i;
48
-
49
- if(DEBUG) printf("Creating new interface for %s\n", name);
50
-
51
- iface = malloc(sizeof(struct iface_stats));
52
- if(!iface) return;
53
-
54
- iface->last_history_id = HISTORY + 1;
55
-
56
- // link it to beginning
57
- iface->next = interfaces;
58
- interfaces = iface;
59
-
60
- for(i = 0; i < HISTORY ;i++) {
61
- iface->history[i].time = time(NULL) - (HISTORY * update_every) + (i * update_every);
62
- iface->history[i].rbytes = rbytes;
63
- iface->history[i].tbytes = tbytes;
64
- iface->history[i].usec = usec;
65
- }
66
- }
67
-
68
- iface->last_history_id++;
69
- if(iface->last_history_id >= HISTORY) iface->last_history_id = 0;
70
-
71
- if(DEBUG) printf("Updating values for interface %s at position %d, rbytes = %llu, tbytes = %llu\n", iface->name, iface->last_history_id, rbytes, tbytes);
72
-
73
- strcpy(iface->name, name);
74
- iface->history[iface->last_history_id].time = time(NULL);
75
- iface->history[iface->last_history_id].rbytes = rbytes;
76
- iface->history[iface->last_history_id].tbytes = tbytes;
77
- iface->history[iface->last_history_id].usec = usec;
78
-}
79
-
80
-void save_proc_net_dev() {
81
- struct iface_stats *iface = NULL;
82
- int r;
83
-
84
- for(iface = interfaces; iface != NULL; iface = iface->next) {
85
- char tmp[FILENAME_MAX+1];
86
- char filename[FILENAME_MAX+1];
87
- FILE *fp;
88
- int i, ld;
89
-
90
- sprintf(tmp, "%s/%s.json.tmp.%d", save_path, iface->name, getpid());
91
- sprintf(filename, "%s/%s.json", save_path, iface->name);
92
- fp = fopen(tmp, "w");
93
- if(!fp) {
94
- perror(filename);
95
- return;
96
- }
97
-
98
-
99
- fprintf(fp, "{\n \"cols\":\n [\n");
100
- fprintf(fp, " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"timeofday\"},\n");
101
- fprintf(fp, " {\"id\":\"\",\"label\":\"received\",\"pattern\":\"\",\"type\":\"number\"},\n");
102
- fprintf(fp, " {\"id\":\"\",\"label\":\"sent\",\"pattern\":\"\",\"type\":\"number\"}\n");
103
- fprintf(fp, " ],\n \"rows\":\n [\n");
104
-
105
- ld = iface->last_history_id;
106
- for(i = 1; i < save_history ; i++) {
107
- int dt, s, d;
108
- long long rb, tb;
109
- struct tm *tm;
110
- char dtm[1025];
111
-
112
- d = iface->last_history_id - i;
113
- if(d < 0) d += HISTORY;
114
-
115
- dt = iface->history[ld].time - iface->history[d].time;
116
- if(dt == 0) dt = 1;
117
- //rb = (iface->history[ld].rbytes - iface->history[d].rbytes) * 8 / dt / 1024;
118
- //tb = (iface->history[ld].tbytes - iface->history[d].tbytes) * 8 / dt / 1024;
119
- rb = (iface->history[ld].rbytes - iface->history[d].rbytes) * 1000000 * 8 / iface->history[ld].usec / 1024;
120
- tb = (iface->history[ld].tbytes - iface->history[d].tbytes) * 1000000 * 8 / iface->history[ld].usec / 1024;
121
-
122
- s = time(NULL) - iface->history[ld].time;
123
-
124
- tm = localtime(&iface->history[d].time);
125
- if(!tm) { perror("localtime"); continue; }
126
- // strftime(dtm, 1024, "%T", tm);
127
- // strftime(dtm, 1024, "%H:%M %B %d, %Y %z", tm);
128
- // strftime(dtm, 1024, "new Date(\"%B %d, %Y %T\")", tm);
129
- strftime(dtm, 1024, "[%H, %M, %S, 0]", tm);
130
-
131
- // fprintf(fp, " {\"c\":[{\"v\":\"%ds\",\"f\":null},{\"v\":%llu,\"f\":\"%llu\"},{\"v\":%llu,\"f\":\"%llu\"}]}", -s, rb, rb, tb, tb);
132
- // fprintf(fp, " {\"c\":[{\"v\":%s,\"f\":null},{\"v\":%lld,\"f\":null},{\"v\":%lld,\"f\":null}]}", dtm, rb, tb);
133
- fprintf(fp, " {\"c\":[{\"v\":%s},{\"v\":%lld},{\"v\":%lld}]}", dtm, rb, tb);
134
- if(i == save_history - 1) fprintf(fp, "\n");
135
- else fprintf(fp, ",\n");
136
-
137
- ld = d;
138
- }
139
- fprintf(fp, " ]\n}\n");
140
- fclose(fp);
141
-
142
- r = rename(tmp, filename);
143
- if(r == -1) perror("Cannot rename JSON file.");
144
- //unlink(filename);
145
- //r = link(tmp, filename);
146
- //unlink(tmp);
147
- }
148
-}
149
-
150
-int do_proc_net_dev(unsigned long long usec) {
151
- char buffer[MAX_LINE+1] = "";
152
- char iface[MAX_IFACE_NAME + 1] = "";
153
- unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
154
- unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
155
-
156
- int r;
157
- char *p;
158
-
159
- FILE *fp = fopen("/proc/net/dev", "r");
160
- if(!fp) {
161
- perror("/proc/net/dev");
162
- return 1;
163
- }
164
-
165
- // skip the first two lines
166
- p = fgets(buffer, MAX_LINE, fp);
167
- p = fgets(buffer, MAX_LINE, fp);
168
-
169
- // read the rest of the lines
170
- for(;1;) {
171
- char *c;
172
- p = fgets(buffer, MAX_LINE, fp);
173
- if(!p) break;
174
-
175
- c = strchr(buffer, ':');
176
- if(c) *c = '\t';
177
-
178
- // if(DEBUG) printf("%s\n", buffer);
179
- 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",
180
- iface,
181
- &rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,
182
- &tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);
183
- if(r == EOF) break;
184
- if(r != 17) fprintf(stderr, "Cannot read line. Expected 17 params, read %d\n", r);
185
- else {
186
- // update our data
187
- update_iface_history(usec, iface, rbytes, tbytes);
188
- }
189
- }
190
-
191
- // done reading, close it
192
- fclose(fp);
193
-
194
- // save statistics
195
- save_proc_net_dev();
196
-
197
- return 0;
198
-}
199
-
200
-unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
201
- return ((((now->tv_sec * 1000000) + now->tv_usec) - ((last->tv_sec * 1000000) + last->tv_usec)));
202
-}
203
-
204
-int main(int argc, char **argv) {
205
- struct timeval last, now, tmp;
206
- int i;
207
- int daemon = 0;
208
-
209
- // parse the arguments
210
- for(i = 1; i < argc ; i++) {
211
- if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
212
- save_history = atoi(argv[i+1]);
213
- if(save_history <= 0 || save_history > HISTORY) {
214
- fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", save_history, HISTORY);
215
- save_history = HISTORY;
216
- }
217
- else {
218
- fprintf(stderr, "save lines set to %d.\n", save_history);
219
- }
220
- i++;
221
- }
222
- else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) {
223
- update_every = atoi(argv[i+1]);
224
- if(update_every <= 0 || update_every > 600) {
225
- fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY);
226
- update_every = UPDATE_EVERY;
227
- }
228
- else {
229
- fprintf(stderr, "update timer set to %d.\n", update_every);
230
- }
231
- i++;
232
- }
233
- else if(strcmp(argv[i], "-o") == 0 && (i+1) < argc) {
234
- strncpy(save_path, argv[i+1], FILENAME_MAX);
235
- save_path[FILENAME_MAX]='\0';
236
- fprintf(stderr, "Saving files to '%s'.\n", save_path);
237
- i++;
238
- }
239
- else if(strcmp(argv[i], "-d") == 0) {
240
- daemon = 1;
241
- fprintf(stderr, "Enabled daemon mode.\n");
242
- }
243
- else {
244
- fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
245
- fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-o PATH_TO_SAVE_FILES].\n\n", argv[0]);
246
- fprintf(stderr, " -d enabled daemon mode.\n");
247
- fprintf(stderr, " -l LINES_TO_SAVE can be from 0 to %d lines in JSON data. Default: %d.\n", HISTORY, save_history);
248
- fprintf(stderr, " -u UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", 600, update_every);
249
- fprintf(stderr, " -o PATH_TO_SAVE_FILES is a directory to place the JSON files. Default: '%s'.\n", save_path);
250
- exit(1);
251
- }
252
- }
253
-
254
- if(daemon) {
255
- i = fork();
256
- if(i == -1) {
257
- perror("cannot fork");
258
- exit(1);
259
- }
260
- if(i != 0) exit(0); // the parent
261
- close(0);
262
- close(1);
263
- close(2);
264
- }
265
-
266
- // main loop
267
- gettimeofday(&last, NULL);
268
- last.tv_sec -= update_every;
269
-
270
- for(;1;) {
271
- unsigned long long usec, susec;
272
- gettimeofday(&now, NULL);
273
-
274
- // calculate the time it took for a full loop
275
- usec = usecdiff(&now, &last);
276
- if(DEBUG) printf("Last loop took %llu usec\n", usec);
277
-
278
- do_proc_net_dev(usec);
279
-
280
- // find the time to sleep in order to wait exactly update_every seconds
281
- gettimeofday(&tmp, NULL);
282
- usec = usecdiff(&tmp, &now);
283
- if(DEBUG) printf("This loop took %llu usec\n", usec);
284
-
285
- if(usec < (update_every * 1000000)) susec = (update_every * 1000000) - usec;
286
- else susec = 0;
287
-
288
- // make sure we will wait at least 100ms
289
- if(susec < 100000) susec = 100000;
290
-
291
- if(DEBUG) printf("Sleeping for %llu usec\n", susec);
292
- usleep(susec);
293
-
294
- // copy now to last
295
- last.tv_sec = now.tv_sec;
296
- last.tv_usec = now.tv_usec;
297
-
298
- //exit(1);
299
- }
300
-}
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 <sys/types.h>
10
+#include <sys/time.h>
11
+
12
+#include <sys/socket.h>
13
+#include <sys/select.h>
14
+#include <sys/resource.h>
15
+#include <sys/stat.h>
16
+#include <sys/sendfile.h>
17
+
18
+#include <arpa/inet.h>
19
+#include <netinet/in.h>
20
+#include <netinet/tcp.h>
21
+
22
+#include <errno.h>
23
+#include <stdarg.h>
24
+#include <locale.h>
25
+#include <signal.h>
26
+#include <ctype.h>
27
+#include <time.h>
28
+#include <fcntl.h>
29
+
30
+#include <pthread.h>
31
+
32
+
33
+
34
+// internal defaults
35
+#define UPDATE_EVERY 2
36
+#define UPDATE_EVERY_MAX 3600
37
+#define LISTEN_PORT 19999
38
+#define HISTORY 120
39
+#define HISTORY_MAX 86400
40
+#define SAVE_PATH "/tmp"
41
+
42
+#define D_WEB_BUFFER 0x00000001
43
+#define D_WEB_CLIENT 0x00000002
44
+#define D_LISTENER 0x00000004
45
+#define D_WEB_DATA 0x00000008
46
+#define D_OPTIONS 0x00000010
47
+#define D_PROCNETDEV_LOOP 0x00000020
48
+#define D_RRD_STATS 0x00000040
49
+#define D_WEB_CLIENT_ACCESS 0x00000080
50
+
51
+#define CT_APPLICATION_JSON 1
52
+#define CT_TEXT_PLAIN 2
53
+#define CT_TEXT_HTML 3
54
+#define CT_APPLICATION_X_JAVASCRIPT 4
55
+#define CT_TEXT_CSS 5
56
+#define CT_TEXT_XML 6
57
+#define CT_APPLICATION_XML 7
58
+#define CT_TEXT_XSL 8
59
+
60
+// configuration
61
+#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
62
+//#define DEBUG (0)
63
+
64
+#define EXIT_FAILURE 1
65
+#define LISTEN_BACKLOG 100
66
+
67
+#define MAX_SOCKET_INPUT_DATA 65536
68
+#define MAX_SOCKET_OUTPUT_DATA 65536
69
+
70
+#define MIN_SOCKET_INPUT_DATA 16384
71
+#define DEFAULT_DATA_BUFFER 65536
72
+
73
+#define MAX_HTTP_HEADER_SIZE 1024
74
+
75
+#define MAX_PROC_NET_DEV_LINE 4096
76
+#define MAX_PROC_NET_DEV_IFACE_NAME 1024
77
+
78
+int silent = 0;
79
+int save_history = HISTORY;
80
+int update_every = UPDATE_EVERY;
81
+int listen_port = LISTEN_PORT;
82
+
83
+
84
+// ----------------------------------------------------------------------------
85
+// helpers
86
+
87
+unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
88
+ return ((((now->tv_sec * 1000000) + now->tv_usec) - ((last->tv_sec * 1000000) + last->tv_usec)));
89
+}
90
+
91
+// ----------------------------------------------------------------------------
92
+// LOG
93
+
94
+void log_date()
95
+{
96
+ char outstr[200];
97
+ time_t t;
98
+ struct tm *tmp;
99
+
100
+ t = time(NULL);
101
+ tmp = localtime(&t);
102
+
103
+ if (tmp == NULL) return;
104
+ if (strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0) return;
105
+
106
+ fprintf(stderr, "%s: ", outstr);
107
+}
108
+
109
+#define debug(args...) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args)
110
+
111
+void debug_int( const char *file, const char *function, const unsigned long line, unsigned long type, const char *fmt, ... )
112
+{
113
+ if(silent) return;
114
+
115
+ va_list args;
116
+
117
+ if(DEBUG & type) {
118
+ log_date();
119
+ va_start( args, fmt );
120
+ fprintf(stderr, "DEBUG (%04lu@%-15.15s): ", line, function);
121
+ vfprintf( stderr, fmt, args );
122
+ va_end( args );
123
+ fprintf(stderr, "\n");
124
+ }
125
+}
126
+
127
+#define error(args...) error_int(__FILE__, __FUNCTION__, __LINE__, ##args)
128
+
129
+void error_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
130
+{
131
+ if(silent) return;
132
+
133
+ va_list args;
134
+
135
+ log_date();
136
+
137
+ va_start( args, fmt );
138
+ fprintf(stderr, "ERROR (%04lu@%-15.15s): ", line, function);
139
+ vfprintf( stderr, fmt, args );
140
+ va_end( args );
141
+
142
+ if(errno) {
143
+ fprintf(stderr, " (errno %d, %s)\n", errno, strerror(errno));
144
+ errno = 0;
145
+ }
146
+ else fprintf(stderr, "\n");
147
+}
148
+
149
+#define fatal(args...) fatal_int(__FILE__, __FUNCTION__, __LINE__, ##args)
150
+
151
+void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
152
+{
153
+ if(silent) exit(EXIT_FAILURE);
154
+
155
+ va_list args;
156
+
157
+ log_date();
158
+
159
+ va_start( args, fmt );
160
+ fprintf(stderr, "FATAL (%04lu@%-15.15s): ", line, function);
161
+ vfprintf( stderr, fmt, args );
162
+ va_end( args );
163
+
164
+ perror(" # ");
165
+ fprintf(stderr, "\n");
166
+
167
+ exit(EXIT_FAILURE);
168
+}
169
+
170
+
171
+// ----------------------------------------------------------------------------
172
+// URL encode / decode
173
+// code from: http://www.geekhideout.com/urlcode.shtml
174
+
175
+/* Converts a hex character to its integer value */
176
+char from_hex(char ch) {
177
+ return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
178
+}
179
+
180
+/* Converts an integer value to its hex character*/
181
+char to_hex(char code) {
182
+ static char hex[] = "0123456789abcdef";
183
+ return hex[code & 15];
184
+}
185
+
186
+/* Returns a url-encoded version of str */
187
+/* IMPORTANT: be sure to free() the returned string after use */
188
+char *url_encode(char *str) {
189
+ char *pstr = str,
190
+ *buf = malloc(strlen(str) * 3 + 1),
191
+ *pbuf = buf;
192
+
193
+ while (*pstr) {
194
+ if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
195
+ *pbuf++ = *pstr;
196
+
197
+ else if (*pstr == ' ')
198
+ *pbuf++ = '+';
199
+
200
+ else
201
+ *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
202
+
203
+ pstr++;
204
+ }
205
+
206
+ *pbuf = '\0';
207
+
208
+ return buf;
209
+}
210
+
211
+/* Returns a url-decoded version of str */
212
+/* IMPORTANT: be sure to free() the returned string after use */
213
+char *url_decode(char *str) {
214
+ char *pstr = str,
215
+ *buf = malloc(strlen(str) + 1),
216
+ *pbuf = buf;
217
+
218
+ if(!buf) fatal("Cannot allocate memory.");
219
+
220
+ while (*pstr) {
221
+ if (*pstr == '%') {
222
+ if (pstr[1] && pstr[2]) {
223
+ *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
224
+ pstr += 2;
225
+ }
226
+ }
227
+ else if (*pstr == '+')
228
+ *pbuf++ = ' ';
229
+
230
+ else
231
+ *pbuf++ = *pstr;
232
+
233
+ pstr++;
234
+ }
235
+
236
+ *pbuf = '\0';
237
+
238
+ return buf;
239
+}
240
+
241
+
242
+// ----------------------------------------------------------------------------
243
+// socket
244
+
245
+int create_listen_socket(int port)
246
+{
247
+ int sock=-1;
248
+ int sockopt=1;
249
+ struct sockaddr_in name;
250
+
251
+ debug(D_LISTENER, "Creating new listening socket on port %d", port);
252
+
253
+ sock = socket(AF_INET, SOCK_STREAM, 0);
254
+ if (sock < 0)
255
+ fatal("socket() failed, errno=%d", errno);
256
+
257
+ /* avoid "address already in use" */
258
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt));
259
+
260
+ memset(&name, 0, sizeof(struct sockaddr_in));
261
+ name.sin_family = AF_INET;
262
+ name.sin_port = htons (port);
263
+ name.sin_addr.s_addr = htonl (INADDR_ANY);
264
+ if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
265
+ fatal("bind() failed, errno=%d", errno);
266
+
267
+ if (listen(sock, LISTEN_BACKLOG) < 0)
268
+ fatal("listen() failed, errno=%d", errno);
269
+
270
+ debug(D_LISTENER, "Listening Port %d created", port);
271
+ return sock;
272
+}
273
+
274
+
275
+// ----------------------------------------------------------------------------
276
+// RRD STATS
277
+
278
+#define RRD_STATS_NAME_MAX 1024
279
+
280
+struct rrd_dimension {
281
+ char name[RRD_STATS_NAME_MAX + 1];
282
+ size_t bytes;
283
+ size_t entries;
284
+
285
+ long multiplier;
286
+ long divisor;
287
+
288
+ void *values;
289
+
290
+ struct rrd_dimension *next;
291
+};
292
+typedef struct rrd_dimension RRD_DIMENSION;
293
+
294
+struct rrd_stats {
295
+ char name[RRD_STATS_NAME_MAX + 1];
296
+
297
+ size_t entries;
298
+ size_t last_entry;
299
+
300
+ struct timeval *times;
301
+ RRD_DIMENSION *dimensions;
302
+ struct rrd_stats *next;
303
+};
304
+typedef struct rrd_stats RRD_STATS;
305
+
306
+RRD_STATS *root = NULL;
307
+
308
+RRD_STATS *rrd_stats_create(const char *name, unsigned long entries)
309
+{
310
+ RRD_STATS *st = NULL;
311
+
312
+ debug(D_RRD_STATS, "Creating RRD_STATS for '%s'.", name);
313
+
314
+ st = malloc(sizeof(RRD_STATS));
315
+ if(!st) return NULL;
316
+
317
+ st->times = calloc(entries, sizeof(struct timeval));
318
+ if(!st->times) {
319
+ error("Cannot allocate %lu entries of %lu bytes each for RRD_STATS.", st->entries, sizeof(struct timeval));
320
+ free(st);
321
+ return NULL;
322
+ }
323
+
324
+ strncpy(st->name, name, RRD_STATS_NAME_MAX);
325
+ st->name[RRD_STATS_NAME_MAX] = '\0';
326
+
327
+ st->entries = entries;
328
+ st->last_entry = entries;
329
+
330
+ st->dimensions = NULL;
331
+ st->next = root;
332
+ root = st;
333
+
334
+ return(st);
335
+}
336
+
337
+RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *name, size_t bytes, size_t multiplier, size_t divisor)
338
+{
339
+ RRD_DIMENSION *rd = NULL;
340
+
341
+ debug(D_RRD_STATS, "Adding dimension '%s' to RRD_STATS '%s'.", name, st->name);
342
+
343
+ rd = malloc(sizeof(RRD_DIMENSION));
344
+ if(!rd) return NULL;
345
+
346
+ rd->bytes = bytes;
347
+ rd->entries = st->entries;
348
+ rd->multiplier = multiplier;
349
+ rd->divisor = divisor;
350
+ rd->values = calloc(rd->entries, rd->bytes);
351
+ if(!rd->values) {
352
+ error("Cannot allocate %lu entries of %lu bytes each for RRD_DIMENSION.", rd->entries, rd->bytes);
353
+ free(rd);
354
+ return NULL;
355
+ }
356
+
357
+ strncpy(rd->name, name, RRD_STATS_NAME_MAX);
358
+ rd->name[RRD_STATS_NAME_MAX] = '\0';
359
+
360
+ rd->next = st->dimensions;
361
+ st->dimensions = rd;
362
+
363
+ return(rd);
364
+}
365
+
366
+RRD_STATS *rrd_stats_find(const char *name)
367
+{
368
+ RRD_STATS *st = root;
369
+
370
+ for ( ; st ; st = st->next ) {
371
+ if(strcmp(st->name, name) == 0) break;
372
+ }
373
+
374
+ return(st);
375
+}
376
+
377
+RRD_DIMENSION *rrd_stats_dimension_find(RRD_STATS *st, const char *dimension)
378
+{
379
+ RRD_DIMENSION *rd = st->dimensions;
380
+
381
+ for ( ; rd ; rd = rd->next ) {
382
+ if(strcmp(rd->name, dimension) == 0) break;
383
+ }
384
+
385
+ return(rd);
386
+}
387
+
388
+void rrd_stats_next(RRD_STATS *st)
389
+{
390
+ struct timeval *now;
391
+
392
+ st->last_entry++;
393
+ if(st->last_entry >= st->entries) st->last_entry = 0;
394
+
395
+ now = &st->times[st->last_entry];
396
+ gettimeofday(now, NULL);
397
+}
398
+
399
+void rrd_stats_dimension_set(RRD_STATS *st, const char *dimension, void *data)
400
+{
401
+ RRD_DIMENSION *rd = rrd_stats_dimension_find(st, dimension);
402
+ if(!rd) {
403
+ error("Cannot find dimension '%s' on stats '%s'.", dimension, st->name);
404
+ return;
405
+ }
406
+
407
+ if(rd->bytes == sizeof(unsigned long long)) {
408
+ unsigned long long *dimension = rd->values, *value = data;
409
+
410
+ dimension[st->last_entry] = (*value);
411
+ }
412
+ else if(rd->bytes == sizeof(unsigned long)) {
413
+ unsigned long *dimension = rd->values, *value = data;
414
+
415
+ dimension[st->last_entry] = (*value);
416
+ }
417
+ else if(rd->bytes == sizeof(unsigned int)) {
418
+ unsigned int *dimension = rd->values, *value = data;
419
+
420
+ dimension[st->last_entry] = (*value);
421
+ }
422
+ else if(rd->bytes == sizeof(unsigned char)) {
423
+ unsigned char *dimension = rd->values, *value = data;
424
+
425
+ dimension[st->last_entry] = (*value);
426
+ }
427
+ else fatal("I don't know how to handle data of length %d bytes.", rd->bytes);
428
+}
429
+
430
+size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show)
431
+{
432
+ size_t i = 0, t, lt, c;
433
+ long count = st->entries;
434
+ RRD_DIMENSION *rd;
435
+ unsigned long long usec = 0;
436
+ char dtm[1025];
437
+
438
+ i += sprintf(&b[i], "{\n \"cols\":\n [\n");
439
+ i += sprintf(&b[i], " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"timeofday\"},\n");
440
+
441
+ for( rd = st->dimensions ; rd ; rd = rd->next)
442
+ i += sprintf(&b[i], " {\"id\":\"\",\"label\":\"%s\",\"pattern\":\"\",\"type\":\"number\"}%s\n", rd->name, rd->next?",":"");
443
+
444
+ i += sprintf(&b[i], " ],\n \"rows\":\n [\n");
445
+
446
+ // find the old entry
447
+ t = st->last_entry + 1;
448
+ if(t >= st->entries) t = 0;
449
+ lt = t;
450
+
451
+ // find the current entry
452
+ t++;
453
+ if(t >= st->entries) t = 0;
454
+
455
+ count -= 2;
456
+ // the loop in dimension data
457
+ for ( c = 0 ; t != st->last_entry ; lt = t++, count--) {
458
+ if(t >= st->entries) t = 0;
459
+
460
+ // if the last is empty, loop again
461
+ if(!st->times[lt].tv_sec) continue;
462
+
463
+ // check if we may exceed the buffer provided
464
+ if((length - i) < 1024) break;
465
+
466
+ if(count > entries_to_show) continue;
467
+
468
+ // find how much usec since the previous entry
469
+ usec = usecdiff(&st->times[t], &st->times[lt]);
470
+
471
+ // generate the local date time
472
+ struct tm *tm = localtime(&st->times[t].tv_sec);
473
+ if(!tm) { error("localtime() failed."); continue; }
474
+
475
+ strftime(dtm, 1024, "[%H, %M, %S, 0]", tm);
476
+ i += sprintf(&b[i], "%s {\"c\":[{\"v\":%s},", c?",\n":"", dtm);
477
+
478
+ for( rd = st->dimensions ; rd ; rd = rd->next) {
479
+ if(rd->bytes == sizeof(unsigned long long)) {
480
+ unsigned long long *dimension = rd->values, v;
481
+ v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
482
+
483
+ i += sprintf(&b[i], "{\"v\":%lld}%s", v, rd->next?",":"");
484
+ }
485
+ else if(rd->bytes == sizeof(unsigned long)) {
486
+ unsigned long *dimension = rd->values, v;
487
+ v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
488
+
489
+ i += sprintf(&b[i], "{\"v\":%ld}%s", v, rd->next?",":"");
490
+ }
491
+ else if(rd->bytes == sizeof(unsigned int)) {
492
+ unsigned int *dimension = rd->values, v;
493
+ v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
494
+
495
+ i += sprintf(&b[i], "{\"v\":%d}%s", v, rd->next?",":"");
496
+ }
497
+ else if(rd->bytes == sizeof(unsigned char)) {
498
+ unsigned char *dimension = rd->values, v;
499
+ v = (dimension[t] - dimension[lt]) * 1000000 * rd->multiplier / usec / rd->divisor;
500
+
501
+ i += sprintf(&b[i], "{\"v\":%d}%s", (int)v, rd->next?",":"");
502
+ }
503
+ else fatal("Cannot produce JSON for size %d bytes data.", rd->bytes);
504
+ }
505
+ i += sprintf(&b[i], "]}");
506
+ c++;
507
+ }
508
+ i += sprintf(&b[i], "\n ]\n}\n");
509
+
510
+ if(count != 0) error("RRD count should be zero, but found to be %d.", count);
511
+ return(i);
512
+}
513
+
514
+// ----------------------------------------------------------------------------
515
+// listener (web server)
516
+
517
+struct web_buffer {
518
+ size_t size; // allocation size of buffer
519
+ size_t bytes; // current data length in buffer
520
+ size_t sent; // current data length sent to output
521
+ char *buffer; // the buffer
522
+ int contenttype;
523
+ size_t rbytes; // if non-zero, the excepted size of ifd
524
+ time_t date; // the date this content has been generated
525
+};
526
+
527
+struct web_buffer *web_buffer_create(size_t size)
528
+{
529
+ struct web_buffer *b;
530
+
531
+ debug(D_WEB_BUFFER, "Creating new web buffer of size %d.", size);
532
+
533
+ b = calloc(1, sizeof(struct web_buffer));
534
+ if(!b) {
535
+ error("Cannot allocate a web_buffer.");
536
+ return NULL;
537
+ }
538
+
539
+ b->buffer = calloc(1, size);
540
+ if(!b->buffer) {
541
+ error("Cannot allocate a buffer of size %u.", size);
542
+ free(b);
543
+ return NULL;
544
+ }
545
+ b->size = size;
546
+ b->contenttype = CT_TEXT_PLAIN;
547
+ return(b);
548
+}
549
+
550
+void web_buffer_free(struct web_buffer *b)
551
+{
552
+ debug(D_WEB_BUFFER, "Freeing web buffer of size %d.", b->size);
553
+
554
+ if(b->buffer) free(b->buffer);
555
+ free(b);
556
+}
557
+
558
+void web_buffer_increase(struct web_buffer *b, size_t free_size_required)
559
+{
560
+ size_t left = b->size - b->bytes;
561
+
562
+ if(left >= free_size_required) return;
563
+ size_t increase = free_size_required - left;
564
+
565
+ debug(D_WEB_BUFFER, "Increasing data buffer from size %d to %d.", b->size, b->size + increase);
566
+
567
+ b->buffer = realloc(b->buffer, b->size + free_size_required - left);
568
+ if(!b->buffer) fatal("Failed to increase data buffer from size %d to %d.", b->size, b->size + increase);
569
+
570
+ b->size += increase;
571
+}
572
+
573
+#define WEB_CLIENT_MODE_NORMAL 0
574
+#define WEB_CLIENT_MODE_FILECOPY 1
575
+
576
+struct web_client {
577
+ unsigned long long id;
578
+
579
+ int mode;
580
+ int keepalive;
581
+
582
+ struct sockaddr_in clientaddr;
583
+
584
+ pthread_t thread; // the thread servicing this client
585
+ int obsolete; // if set to 1, the listener will remove this client
586
+
587
+ int ifd;
588
+ int ofd;
589
+
590
+ struct web_buffer *data;
591
+
592
+ int wait_receive;
593
+ int wait_send;
594
+
595
+ struct web_client *prev;
596
+ struct web_client *next;
597
+} *web_clients = NULL;
598
+
599
+unsigned long long web_clients_count = 0;
600
+
601
+struct web_client *web_client_create(int listener)
602
+{
603
+ struct web_client *w;
604
+ socklen_t addrlen;
605
+
606
+ w = calloc(1, sizeof(struct web_client));
607
+ if(!w) {
608
+ error("Cannot allocate new web_client memory.");
609
+ return NULL;
610
+ }
611
+
612
+ w->id = ++web_clients_count;
613
+ w->mode = WEB_CLIENT_MODE_NORMAL;
614
+
615
+ addrlen = sizeof(w->clientaddr);
616
+ w->ifd = accept(listener, (struct sockaddr *)&w->clientaddr, &addrlen);
617
+ if (w->ifd == -1) {
618
+ error("%llu: Cannot accept new incoming connection.", w->id);
619
+ free(w);
620
+ return NULL;
621
+ }
622
+ w->ofd = w->ifd;
623
+
624
+ debug(D_WEB_CLIENT_ACCESS, "%llu: New web client from %s on socket %d.", w->id, inet_ntoa(w->clientaddr.sin_addr), w->ifd);
625
+
626
+ {
627
+ int flag = 1;
628
+ if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0) error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
629
+ }
630
+
631
+ w->data = web_buffer_create(DEFAULT_DATA_BUFFER);
632
+ if(!w->data) {
633
+ close(w->ifd);
634
+ free(w);
635
+ return NULL;
636
+ }
637
+
638
+ w->wait_receive = 1;
639
+
640
+ if(web_clients) web_clients->prev = w;
641
+ w->next = web_clients;
642
+ web_clients = w;
643
+
644
+ return(w);
645
+}
646
+
647
+struct web_client *web_client_free(struct web_client *w)
648
+{
649
+ struct web_client *n = w->next;
650
+
651
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s.", w->id, inet_ntoa(w->clientaddr.sin_addr));
652
+
653
+ if(w->prev) w->prev->next = w->next;
654
+ if(w->next) w->next->prev = w->prev;
655
+
656
+ if(w == web_clients) web_clients = w->next;
657
+
658
+ if(w->data) web_buffer_free(w->data);
659
+ close(w->ifd);
660
+ if(w->ofd != w->ifd) close(w->ofd);
661
+ free(w);
662
+
663
+ return(n);
664
+}
665
+
666
+int mysendfile(struct web_client *w, char *filename)
667
+{
668
+ debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
669
+
670
+ // check if the file exists
671
+ struct stat stat;
672
+ if(lstat(filename, &stat) != 0) {
673
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, filename);
674
+ w->data->bytes = sprintf(w->data->buffer, "File '%s' is not found.", filename);
675
+ return 404;
676
+ }
677
+
678
+ if(stat.st_uid != getuid() && stat.st_uid != geteuid()) {
679
+ debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not mine.", w->id, filename);
680
+ w->data->bytes = sprintf(w->data->buffer, "File '%s' is not mine.", filename);
681
+ return 404;
682
+ }
683
+
684
+ w->ifd = open(filename, O_NONBLOCK, O_RDONLY);
685
+ if(w->ifd < 0) {
686
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot open file '%s'.", w->id, filename);
687
+ w->data->bytes = sprintf(w->data->buffer, "Cannot open file '%s'.", filename);
688
+ w->ifd = w->ofd;
689
+ return 404;
690
+ }
691
+
692
+ // pick a Content-Type for the file
693
+ if(strstr(filename, ".html") != NULL) w->data->contenttype = CT_TEXT_HTML;
694
+ else if(strstr(filename, ".js") != NULL) w->data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
695
+ else if(strstr(filename, ".css") != NULL) w->data->contenttype = CT_TEXT_CSS;
696
+ else if(strstr(filename, ".xml") != NULL) w->data->contenttype = CT_TEXT_XML;
697
+ else if(strstr(filename, ".xsl") != NULL) w->data->contenttype = CT_TEXT_XSL;
698
+
699
+ 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);
700
+
701
+ w->mode = WEB_CLIENT_MODE_FILECOPY;
702
+ w->wait_receive = 1;
703
+ w->wait_send = 0;
704
+ w->data->bytes = 0;
705
+ w->data->buffer[0] = '\0';
706
+ w->data->rbytes = stat.st_size;
707
+ w->data->date = stat.st_mtim.tv_sec;
708
+ return 200;
709
+}
710
+
711
+char *mystrsep(char **ptr, char *s)
712
+{
713
+ char *p = "";
714
+ while ( !p[0] && *ptr ) p = strsep(ptr, s);
715
+ return(p);
716
+}
717
+
718
+void web_client_reset(struct web_client *w)
719
+{
720
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
721
+ close(w->ifd);
722
+ w->ifd = w->ofd;
723
+ }
724
+
725
+ w->data->contenttype = CT_TEXT_PLAIN;
726
+ w->mode = WEB_CLIENT_MODE_NORMAL;
727
+
728
+ w->data->rbytes = 0;
729
+ w->data->bytes = 0;
730
+ w->data->sent = 0;
731
+
732
+ w->data->buffer[0] = '\0';
733
+
734
+ w->wait_receive = 1;
735
+ w->wait_send = 0;
736
+}
737
+
738
+void web_client_process(struct web_client *w)
739
+{
740
+ int code = 500;
741
+ int bytes;
742
+
743
+ // check if we have an empty line (end of HTTP header)
744
+ if(!strstr(w->data->buffer, "\r\n\r\n")) return;
745
+
746
+ w->data->date = time(NULL);
747
+ w->wait_receive = 0;
748
+ w->data->sent = 0;
749
+
750
+ debug(D_WEB_DATA, "%llu: Processing data buffer of %d bytes: '%s'.", w->id, w->data->bytes, w->data->buffer);
751
+
752
+ // check if the client requested keep-alive HTTP
753
+ if(strcasestr(w->data->buffer, "Connection: keep-alive") == 0) w->keepalive = 0;
754
+ else w->keepalive = 1;
755
+
756
+ char *buf = w->data->buffer;
757
+ char *tok = strsep(&buf, " \r\n");
758
+ char *url = NULL;
759
+
760
+ if(buf && strcmp(tok, "GET") == 0) {
761
+ tok = strsep(&buf, " \r\n");
762
+ url = url_decode(tok);
763
+ debug(D_WEB_CLIENT, "%llu: Processing HTTP GET on url '%s'.", w->id, url);
764
+ }
765
+ else if (buf && strcmp(tok, "POST") == 0) {
766
+ w->keepalive = 0;
767
+ tok = strsep(&buf, " \r\n");
768
+ url = url_decode(tok);
769
+
770
+ 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);
771
+ }
772
+
773
+ if(url) {
774
+ tok = mystrsep(&url, "/?");
775
+
776
+ debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
777
+
778
+ if(strcmp(tok, "data") == 0) {
779
+ // the client is requesting rrd data
780
+
781
+ // get the name of the data to show
782
+ tok = mystrsep(&url, "/?");
783
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
784
+
785
+ // do we have such a data set?
786
+ RRD_STATS *st = rrd_stats_find(tok);
787
+ if(!st) {
788
+ // we don't have it
789
+ code = 404;
790
+ w->data->bytes = sprintf(w->data->buffer, "There are not statistics for '%s'\r\n", tok);
791
+ }
792
+ else {
793
+ // we have it
794
+ debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
795
+
796
+ // how many entries does the client want?
797
+ size_t lines = save_history;
798
+ if(url) {
799
+ tok = mystrsep(&url, "/?");
800
+ if(tok) lines = atoi(tok);
801
+ if(lines < 5) lines = save_history;
802
+ }
803
+
804
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (%d lines).", w->id, st->name, lines);
805
+
806
+ code = 200;
807
+ w->data->contenttype = CT_APPLICATION_JSON;
808
+ w->data->bytes = rrd_stats_json(st, w->data->buffer, w->data->size, lines);
809
+ }
810
+ }
811
+ else if(strcmp(tok, "mirror") == 0) {
812
+ code = 200;
813
+
814
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
815
+
816
+ // just leave the buffer as is
817
+ // it will be copied back to the client
818
+ }
819
+ else if(strcmp(tok, "list") == 0) {
820
+ code = 200;
821
+
822
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
823
+
824
+ w->data->bytes = 0;
825
+ RRD_STATS *st = root;
826
+
827
+ for ( ; st ; st = st->next )
828
+ w->data->bytes += sprintf(&w->data->buffer[w->data->bytes], "%s\n", st->name);
829
+ }
830
+ else if(strcmp(tok, "file") == 0) {
831
+ code = mysendfile(w, url);
832
+ }
833
+ else if(strcmp(tok, "favicon.ico") == 0) {
834
+ code = mysendfile(w, "favicon.ico");
835
+ }
836
+ else if(!tok[0]) {
837
+ code = mysendfile(w, "index.html");
838
+ }
839
+ else {
840
+ code = mysendfile(w, tok);
841
+ }
842
+ }
843
+ else {
844
+ if(buf) debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, buf);
845
+
846
+ code = 500;
847
+ strcpy(w->data->buffer, "I don't understand you...\r\n");
848
+ w->data->bytes = strlen(w->data->buffer);
849
+ }
850
+
851
+ // prepare the HTTP response header
852
+ debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
853
+
854
+ char *content_type_string = "";
855
+ switch(w->data->contenttype) {
856
+ case CT_TEXT_HTML:
857
+ content_type_string = "text/html";
858
+ break;
859
+
860
+ case CT_APPLICATION_XML:
861
+ content_type_string = "application/xml";
862
+ break;
863
+
864
+ case CT_APPLICATION_JSON:
865
+ content_type_string = "application/json";
866
+ break;
867
+
868
+ case CT_APPLICATION_X_JAVASCRIPT:
869
+ content_type_string = "application/x-javascript";
870
+ break;
871
+
872
+ case CT_TEXT_CSS:
873
+ content_type_string = "text/css";
874
+ break;
875
+
876
+ case CT_TEXT_XML:
877
+ content_type_string = "text/xml";
878
+ break;
879
+
880
+ case CT_TEXT_XSL:
881
+ content_type_string = "text/xsl";
882
+ break;
883
+
884
+ default:
885
+ case CT_TEXT_PLAIN:
886
+ content_type_string = "text/plain";
887
+ break;
888
+ }
889
+
890
+ char *code_msg = "";
891
+ switch(code) {
892
+ case 200:
893
+ code_msg = "OK";
894
+ break;
895
+
896
+ case 404:
897
+ code_msg = "Not Found";
898
+ break;
899
+
900
+ default:
901
+ code_msg = "Internal Server Error";
902
+ break;
903
+ }
904
+
905
+ char date[100];
906
+ struct tm tm = *gmtime(&w->data->date);
907
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", &tm);
908
+
909
+ char header[MAX_HTTP_HEADER_SIZE+1] = "";
910
+ size_t headerlen = 0;
911
+ headerlen += sprintf(&header[headerlen],
912
+ "HTTP/1.1 %d %s\r\n"
913
+ "Connection: %s\r\n"
914
+ "Server: Data Collector HTTP Server\r\n"
915
+ "Content-Type: %s\r\n"
916
+ "Access-Control-Allow-Origin: *\r\n"
917
+ "Date: %s\r\n"
918
+ , code, code_msg
919
+ , w->keepalive?"keep-alive":"close"
920
+ , content_type_string
921
+ , date
922
+ );
923
+
924
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) {
925
+ headerlen += sprintf(&header[headerlen],
926
+ "Expires: %s\r\n"
927
+ "Cache-Control: private\r\n"
928
+ , date
929
+ );
930
+ }
931
+
932
+ // if we know the content length, put it
933
+ if(w->data->bytes || w->data->rbytes)
934
+ headerlen += sprintf(&header[headerlen],
935
+ "Content-Length: %ld\r\n"
936
+ , w->data->bytes?w->data->bytes:w->data->rbytes
937
+ );
938
+ else w->keepalive = 0; // content-length is required for keep-alive
939
+
940
+ headerlen += sprintf(&header[headerlen], "\r\n");
941
+
942
+ // disable TCP_NODELAY, to buffer the header
943
+ int flag = 0;
944
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to disable TCP_NODELAY on socket.", w->id);
945
+
946
+ // sent the HTTP header
947
+ debug(D_WEB_CLIENT, "%llu: Sending response HTTP header of size %d.", w->id, headerlen);
948
+
949
+ bytes = send(w->ofd, header, headerlen, 0);
950
+ if(bytes != headerlen)
951
+ error("%llu: HTTP Header failed to be sent (I sent %d bytes but the system sent %d bytes).", w->id, headerlen, bytes);
952
+
953
+ // enable TCP_NODELAY, to send all data immediately at the next send()
954
+ flag = 1;
955
+ if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0) error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
956
+
957
+ // enable sending immediately if we have data
958
+ if(w->data->bytes) w->wait_send = 1;
959
+ else w->wait_send = 0;
960
+
961
+ // pretty logging
962
+ switch(w->mode) {
963
+ case WEB_CLIENT_MODE_NORMAL:
964
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%d bytes) to client.", w->id, w->data->bytes);
965
+ break;
966
+
967
+ case WEB_CLIENT_MODE_FILECOPY:
968
+ if(w->data->rbytes) {
969
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %d bytes to client.", w->id, w->data->rbytes);
970
+
971
+ // utilize the kernel sendfile() for copying the file to the socket.
972
+ // this block of code can be commented, without anything missing.
973
+ // when it is commented, the program will copy the data using async I/O.
974
+ {
975
+ ssize_t len = sendfile(w->ofd, w->ifd, NULL, w->data->rbytes);
976
+ 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);
977
+ else web_client_reset(w);
978
+ }
979
+ }
980
+ else
981
+ debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
982
+ break;
983
+
984
+ default:
985
+ fatal("%llu: Unknown client mode %d.", w->id, w->mode);
986
+ break;
987
+ }
988
+}
989
+
990
+ssize_t web_client_send(struct web_client *w)
991
+{
992
+ ssize_t bytes;
993
+
994
+ if(w->data->bytes - w->data->sent == 0) {
995
+ // there is nothing to send
996
+
997
+ debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
998
+
999
+ // there can be two cases for this
1000
+ // A. we have done everything
1001
+ // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
1002
+
1003
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->ifd != w->ofd && w->data->rbytes && w->data->rbytes > w->data->bytes) {
1004
+ // we have to wait, more data will come
1005
+ debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
1006
+ w->wait_send = 0;
1007
+ return(0);
1008
+ }
1009
+
1010
+ if(w->keepalive == 0) {
1011
+ debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %ld bytes sent.", w->id, w->data->sent);
1012
+ errno = 0;
1013
+ return(-1);
1014
+ }
1015
+
1016
+ web_client_reset(w);
1017
+ debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
1018
+ return(0);
1019
+ }
1020
+
1021
+ bytes = send(w->ofd, &w->data->buffer[w->data->sent], w->data->bytes - w->data->sent, MSG_DONTWAIT);
1022
+ if(bytes > 0) {
1023
+ w->data->sent += bytes;
1024
+ debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
1025
+ }
1026
+ else if(bytes == 0) debug(D_WEB_CLIENT, "%llu: Sent %d bytes.", w->id, bytes);
1027
+
1028
+ return(bytes);
1029
+}
1030
+
1031
+ssize_t web_client_receive(struct web_client *w)
1032
+{
1033
+ // do we have any space for more data?
1034
+ web_buffer_increase(w->data, MIN_SOCKET_INPUT_DATA);
1035
+
1036
+ ssize_t left = w->data->size - w->data->bytes;
1037
+ ssize_t bytes;
1038
+
1039
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY)
1040
+ bytes = read(w->ifd, &w->data->buffer[w->data->bytes], (left-1));
1041
+ else
1042
+ bytes = recv(w->ifd, &w->data->buffer[w->data->bytes], left-1, MSG_DONTWAIT);
1043
+
1044
+ if(bytes > 0) {
1045
+ int old = w->data->bytes;
1046
+ w->data->bytes += bytes;
1047
+ w->data->buffer[w->data->bytes] = '\0';
1048
+
1049
+ debug(D_WEB_CLIENT, "%llu: Received %d bytes.", w->id, bytes);
1050
+ debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->data->buffer[old]);
1051
+
1052
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1053
+ w->wait_send = 1;
1054
+ if(w->data->rbytes && w->data->bytes >= w->data->rbytes) w->wait_receive = 0;
1055
+ }
1056
+ }
1057
+ else if(bytes == 0) {
1058
+ debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
1059
+
1060
+ // if we cannot read, it means we have an error on input.
1061
+ // if however, we are copying a file from ifd to ofd, we should not return an error.
1062
+ // in this case, the error should be generated when the file has been sent to the client.
1063
+
1064
+ if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
1065
+ // we are copying data fron ifd to ofd
1066
+ // let it finish copying...
1067
+ w->wait_receive = 0;
1068
+ debug(D_WEB_CLIENT, "%llu: Disabling input.", w->id);
1069
+ }
1070
+ else {
1071
+ bytes = -1;
1072
+ errno = 0;
1073
+ }
1074
+ }
1075
+
1076
+ return(bytes);
1077
+}
1078
+
1079
+
1080
+// --------------------------------------------------------------------------------------
1081
+// the thread of a single client
1082
+
1083
+// 1. waits for input and output, using async I/O
1084
+// 2. it processes HTTP requests
1085
+// 3. it generates HTTP responses
1086
+// 4. it copies data from input to output if mode is FILECOPY
1087
+
1088
+void *new_client(void *ptr)
1089
+{
1090
+ struct timeval tv;
1091
+ struct web_client *w = ptr;
1092
+ int retval;
1093
+ fd_set ifds, ofds, efds;
1094
+ int fdmax = 0;
1095
+
1096
+ for(;;) {
1097
+ FD_ZERO (&ifds);
1098
+ FD_ZERO (&ofds);
1099
+ FD_ZERO (&efds);
1100
+
1101
+ FD_SET(w->ifd, &efds);
1102
+ if(w->ifd != w->ofd) FD_SET(w->ofd, &efds);
1103
+ if (w->wait_receive) {
1104
+ debug(D_WEB_CLIENT, "%llu: Waiting for input (fd %d).", w->id, w->ifd);
1105
+ FD_SET(w->ifd, &ifds);
1106
+ if(w->ifd > fdmax) fdmax = w->ifd;
1107
+ }
1108
+ if (w->wait_send) {
1109
+ debug(D_WEB_CLIENT, "%llu: Waiting for output (fd %d).", w->id, w->ofd);
1110
+ FD_SET(w->ofd, &ofds);
1111
+ if(w->ofd > fdmax) fdmax = w->ofd;
1112
+ }
1113
+
1114
+ tv.tv_sec = 10;
1115
+ tv.tv_usec = 0;
1116
+
1117
+ debug(D_WEB_CLIENT, "%llu: Waiting...", w->id);
1118
+ retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1119
+
1120
+ if(retval == -1)
1121
+ fatal("LISTENER: select() failed.");
1122
+ else if(!retval) {
1123
+ // timeout
1124
+ w->obsolete = 1;
1125
+ return NULL;
1126
+ }
1127
+
1128
+ if(FD_ISSET(w->ifd, &efds)) {
1129
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on input socket (%s).", w->id, strerror(errno));
1130
+ w->obsolete = 1;
1131
+ return NULL;
1132
+ }
1133
+ if(FD_ISSET(w->ofd, &efds)) {
1134
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on output socket (%s).", w->id, strerror(errno));
1135
+ w->obsolete = 1;
1136
+ return NULL;
1137
+ }
1138
+
1139
+ if(w->wait_send && FD_ISSET(w->ofd, &ofds)) {
1140
+ if(web_client_send(w) < 0) {
1141
+ debug(D_WEB_CLIENT, "%llu: Closing client (input: %s).", w->id, strerror(errno));
1142
+ errno = 0;
1143
+ w->obsolete = 1;
1144
+ return NULL;
1145
+ }
1146
+ }
1147
+
1148
+ if(w->wait_receive && FD_ISSET(w->ifd, &ifds)) {
1149
+ if(web_client_receive(w) < 0) {
1150
+ debug(D_WEB_CLIENT, "%llu: Closing client (output: %s).", w->id, strerror(errno));
1151
+ errno = 0;
1152
+ w->obsolete = 1;
1153
+ return NULL;
1154
+ }
1155
+
1156
+ if(w->mode == WEB_CLIENT_MODE_NORMAL) web_client_process(w);
1157
+ }
1158
+ }
1159
+
1160
+ return NULL;
1161
+}
1162
+
1163
+// --------------------------------------------------------------------------------------
1164
+// the main socket listener
1165
+
1166
+// 1. it accepts new incoming requests on our port
1167
+// 2. creates a new web_client for each connection received
1168
+// 3. spawns a new pthread to serve the client (this is optimal for keep-alive clients)
1169
+// 4. cleans up old web_clients that their pthreads have been exited
1170
+
1171
+void *socket_listen_main(void *ptr)
1172
+{
1173
+ struct web_client *w;
1174
+ struct timeval tv;
1175
+ int retval;
1176
+
1177
+ int listener = create_listen_socket(listen_port);
1178
+ if(listener == -1) fatal("LISTENER: Cannot create listening socket on port 19999.");
1179
+
1180
+ fd_set ifds, ofds, efds;
1181
+ int fdmax = listener;
1182
+
1183
+ FD_ZERO (&ifds);
1184
+ FD_ZERO (&ofds);
1185
+ FD_ZERO (&efds);
1186
+
1187
+ for(;;) {
1188
+ tv.tv_sec = 1;
1189
+ tv.tv_usec = 0;
1190
+
1191
+ FD_SET(listener, &ifds);
1192
+ FD_SET(listener, &efds);
1193
+
1194
+ // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
1195
+ retval = select(fdmax+1, &ifds, &ofds, &efds, &tv);
1196
+
1197
+ if(retval == -1)
1198
+ fatal("LISTENER: select() failed.");
1199
+ else if(retval) {
1200
+ // check for new incoming connections
1201
+ if(FD_ISSET(listener, &ifds)) {
1202
+ w = web_client_create(listener);
1203
+
1204
+ w->thread = pthread_create(&w->thread, NULL, new_client, w);
1205
+ }
1206
+ }
1207
+ // else timeout
1208
+
1209
+ // cleanup unused clients
1210
+ for(w = web_clients; w ; w = w?w->next:NULL) {
1211
+ if(w->obsolete) {
1212
+ debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
1213
+ w = web_client_free(w);
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ close(listener);
1219
+ exit(2);
1220
+
1221
+ return NULL;
1222
+}
1223
+
1224
+// ----------------------------------------------------------------------------
1225
+// /proc/net/dev processor
1226
+
1227
+void update_iface_history(char *name, unsigned long long rbytes, unsigned long long tbytes) {
1228
+
1229
+ RRD_STATS *st = rrd_stats_find(name);
1230
+ if(!st) {
1231
+ st = rrd_stats_create(name, save_history);
1232
+ if(!st) {
1233
+ error("Cannot create RRD_STATS for interface %s.", name);
1234
+ return;
1235
+ }
1236
+
1237
+ if(!rrd_stats_dimension_add(st, "received", sizeof(unsigned long long), 8, 1024)) {
1238
+ error("Cannot add RRD_STATS dimension %s.", "received");
1239
+ return;
1240
+ }
1241
+
1242
+ if(!rrd_stats_dimension_add(st, "sent", sizeof(unsigned long long), 8, 1024)) {
1243
+ error("Cannot add RRD_STATS dimension %s.", "sent");
1244
+ return;
1245
+ }
1246
+ }
1247
+
1248
+ rrd_stats_next(st);
1249
+ rrd_stats_dimension_set(st, "received", &rbytes);
1250
+ rrd_stats_dimension_set(st, "sent", &tbytes);
1251
+}
1252
+
1253
+int do_proc_net_dev() {
1254
+ char buffer[MAX_PROC_NET_DEV_LINE+1] = "";
1255
+ char iface[MAX_PROC_NET_DEV_IFACE_NAME + 1] = "net.";
1256
+ unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
1257
+ unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
1258
+
1259
+ int r;
1260
+ char *p;
1261
+
1262
+ FILE *fp = fopen("/proc/net/dev", "r");
1263
+ if(!fp) {
1264
+ error("Cannot read /proc/net/dev.");
1265
+ return 1;
1266
+ }
1267
+
1268
+ // skip the first two lines
1269
+ p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1270
+ p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1271
+
1272
+ // read the rest of the lines
1273
+ for(;1;) {
1274
+ char *c;
1275
+ p = fgets(buffer, MAX_PROC_NET_DEV_LINE, fp);
1276
+ if(!p) break;
1277
+
1278
+ c = strchr(buffer, ':');
1279
+ if(c) *c = '\t';
1280
+
1281
+ // if(DEBUG) printf("%s\n", buffer);
1282
+ 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",
1283
+ &iface[4],
1284
+ &rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,
1285
+ &tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);
1286
+ if(r == EOF) break;
1287
+ if(r != 17) error("Cannot read /proc/net/dev line. Expected 17 params, read %d.", r);
1288
+ else update_iface_history(iface, rbytes, tbytes);
1289
+ }
1290
+
1291
+ fclose(fp);
1292
+ return 0;
1293
+}
1294
+
1295
+void *proc_net_dev_main(void *ptr)
1296
+{
1297
+ struct timeval last, now, tmp;
1298
+
1299
+ gettimeofday(&last, NULL);
1300
+ last.tv_sec -= update_every;
1301
+
1302
+ for(;1;) {
1303
+ unsigned long long usec, susec;
1304
+ gettimeofday(&now, NULL);
1305
+
1306
+ // calculate the time it took for a full loop
1307
+ usec = usecdiff(&now, &last);
1308
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Last loop took %llu usec.", usec);
1309
+
1310
+ do_proc_net_dev(usec);
1311
+
1312
+ // find the time to sleep in order to wait exactly update_every seconds
1313
+ gettimeofday(&tmp, NULL);
1314
+ usec = usecdiff(&tmp, &now);
1315
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: This loop took %llu usec.", usec);
1316
+
1317
+ if(usec < (update_every * 1000000)) susec = (update_every * 1000000) - usec;
1318
+ else susec = 0;
1319
+
1320
+ // make sure we will wait at least 100ms
1321
+ if(susec < 100000) susec = 100000;
1322
+
1323
+ debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Sleeping for %llu usec.", susec);
1324
+ usleep(susec);
1325
+
1326
+ // copy now to last
1327
+ last.tv_sec = now.tv_sec;
1328
+ last.tv_usec = now.tv_usec;
1329
+ }
1330
+
1331
+ return NULL;
1332
+}
1333
+
1334
+int main(int argc, char **argv)
1335
+{
1336
+ int i, daemon = 0;
1337
+
1338
+ // parse the arguments
1339
+ for(i = 1; i < argc ; i++) {
1340
+ if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
1341
+ save_history = atoi(argv[i+1]);
1342
+ if(save_history < 5 || save_history > HISTORY_MAX) {
1343
+ error("Invalid save lines %d given. Defaulting to %d.", save_history, HISTORY);
1344
+ save_history = HISTORY;
1345
+ }
1346
+ else {
1347
+ debug(D_OPTIONS, "save lines set to %d.", save_history);
1348
+ }
1349
+ i++;
1350
+ }
1351
+ else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) {
1352
+ update_every = atoi(argv[i+1]);
1353
+ if(update_every < 1 || update_every > 600) {
1354
+ error("Invalid update timer %d given. Defaulting to %d.", update_every, UPDATE_EVERY_MAX);
1355
+ update_every = UPDATE_EVERY;
1356
+ }
1357
+ else {
1358
+ debug(D_OPTIONS, "update timer set to %d.", update_every);
1359
+ }
1360
+ i++;
1361
+ }
1362
+ else if(strcmp(argv[i], "-p") == 0 && (i+1) < argc) {
1363
+ listen_port = atoi(argv[i+1]);
1364
+ if(listen_port < 1 || listen_port > 65535) {
1365
+ error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
1366
+ listen_port = LISTEN_PORT;
1367
+ }
1368
+ else {
1369
+ debug(D_OPTIONS, "update timer set to %d.", update_every);
1370
+ }
1371
+ i++;
1372
+ }
1373
+ else if(strcmp(argv[i], "-d") == 0) {
1374
+ daemon = 1;
1375
+ debug(D_OPTIONS, "Enabled daemon mode.");
1376
+ }
1377
+ else {
1378
+ fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
1379
+ fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-p LISTEN_PORT].\n\n", argv[0]);
1380
+ fprintf(stderr, " -d enable daemon mode (run in background).\n");
1381
+ fprintf(stderr, " -l LINES_TO_SAVE can be from 5 to %d lines in JSON data. Default: %d.\n", HISTORY_MAX, HISTORY);
1382
+ fprintf(stderr, " -u UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", UPDATE_EVERY_MAX, UPDATE_EVERY);
1383
+ fprintf(stderr, " -p LISTEN_PORT can be from 1 to %d. Default: %d.\n", 65535, LISTEN_PORT);
1384
+ exit(1);
1385
+ }
1386
+ }
1387
+
1388
+ if(daemon) {
1389
+ i = fork();
1390
+ if(i == -1) {
1391
+ perror("cannot fork");
1392
+ exit(1);
1393
+ }
1394
+ if(i != 0) exit(0); // the parent
1395
+ close(0);
1396
+ close(1);
1397
+ close(2);
1398
+ silent = 1;
1399
+ }
1400
+
1401
+ pthread_t p_proc_net_dev;
1402
+ int r_proc_net_dev;
1403
+
1404
+ // spawn a child to collect data
1405
+ r_proc_net_dev = pthread_create(&p_proc_net_dev, NULL, proc_net_dev_main, NULL);
1406
+
1407
+ // the main process - the web server listener
1408
+ socket_listen_main(NULL);
1409
+
1410
+ // wait for the childs to finish
1411
+ pthread_join(p_proc_net_dev, NULL);
1412
+
1413
+ printf("PROC NET DEV thread returns: %d\n", r_proc_net_dev);
1414
+
1415
+ exit(0);
1416
+}