@cryptotaxi247 / netdata-1 / commits / 427005fc7

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 +}