themes/base: fix error r7144, location.host already includes the port number and...
[project/luci.git] / modules / admin-full / src / luci-bwc.c
1 /*
2  * luci-bwc - Very simple bandwidth collector cache for LuCI realtime graphs
3  *
4  *   Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdint.h>
23 #include <inttypes.h>
24 #include <fcntl.h>
25 #include <time.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <arpa/inet.h>
32
33
34 #define STEP_COUNT      60
35 #define STEP_TIME       1
36
37 #define DB_PATH         "/var/lib/luci-bwc"
38 #define DB_IF_FILE      DB_PATH "/if/%s"
39 #define DB_CN_FILE      DB_PATH "/connections"
40 #define DB_LD_FILE      DB_PATH "/load"
41
42 #define IF_SCAN_PATTERN \
43         " %[^ :]:%" SCNu64 " %" SCNu64 \
44         " %*d %*d %*d %*d %*d %*d" \
45         " %" SCNu64 " %" SCNu64
46
47 #define LD_SCAN_PATTERN \
48         "%f %f %f"
49
50
51 struct file_map {
52         int fd;
53         int size;
54         char *mmap;
55 };
56
57 struct traffic_entry {
58         uint64_t time;
59         uint64_t rxb;
60         uint64_t rxp;
61         uint64_t txb;
62         uint64_t txp;
63 };
64
65 struct conn_entry {
66         uint64_t time;
67         uint32_t udp;
68         uint32_t tcp;
69         uint32_t other;
70 };
71
72 struct load_entry {
73         uint64_t time;
74         uint16_t load1;
75         uint16_t load5;
76         uint16_t load15;
77 };
78
79
80 static uint64_t htonll(uint64_t value)
81 {
82         int num = 1;
83
84         if (*(char *)&num == 1)
85                 return htonl((uint32_t)(value & 0xFFFFFFFF)) |
86                        htonl((uint32_t)(value >> 32));
87
88         return value;
89 }
90
91 #define ntohll htonll
92
93
94 static int init_directory(char *path)
95 {
96         char *p = path;
97
98         for (p = &path[1]; *p; p++)
99         {
100                 if (*p == '/')
101                 {
102                         *p = 0;
103
104                         if (mkdir(path, 0700) && (errno != EEXIST))
105                                 return -1;
106
107                         *p = '/';
108                 }
109         }
110
111         return 0;
112 }
113
114 static int init_file(char *path, int esize)
115 {
116         int i, file;
117         char buf[sizeof(struct traffic_entry)] = { 0 };
118
119         if (init_directory(path))
120                 return -1;
121
122         if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
123         {
124                 for (i = 0; i < STEP_COUNT; i++)
125                 {
126                         if (write(file, buf, esize) < 0)
127                                 break;
128                 }
129
130                 close(file);
131
132                 return 0;
133         }
134
135         return -1;
136 }
137
138 static int update_file(const char *path, void *entry, int esize)
139 {
140         int rv = -1;
141         int file;
142         char *map;
143
144         if ((file = open(path, O_RDWR)) >= 0)
145         {
146                 map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
147                                    MAP_SHARED | MAP_LOCKED, file, 0);
148
149                 if ((map != NULL) && (map != MAP_FAILED))
150                 {
151                         memmove(map, map + esize, esize * (STEP_COUNT-1));
152                         memcpy(map + esize * (STEP_COUNT-1), entry, esize);
153
154                         munmap(map, esize * STEP_COUNT);
155
156                         rv = 0;
157                 }
158
159                 close(file);
160         }
161
162         return rv;
163 }
164
165 static int mmap_file(const char *path, int esize, struct file_map *m)
166 {
167         m->fd   = -1;
168         m->size = -1;
169         m->mmap = NULL;
170
171         if ((m->fd = open(path, O_RDONLY)) >= 0)
172         {
173                 m->size = STEP_COUNT * esize;
174                 m->mmap = mmap(NULL, m->size, PROT_READ,
175                                            MAP_SHARED | MAP_LOCKED, m->fd, 0);
176
177                 if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
178                         return 0;
179         }
180
181         return -1;
182 }
183
184 static void umap_file(struct file_map *m)
185 {
186         if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
187                 munmap(m->mmap, m->size);
188
189         if (m->fd > -1)
190                 close(m->fd);
191 }
192
193
194 static int update_ifstat(
195         const char *ifname, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
196 ) {
197         char path[1024];
198
199         struct stat s;
200         struct traffic_entry e;
201
202         snprintf(path, sizeof(path), DB_IF_FILE, ifname);
203
204         if (stat(path, &s))
205         {
206                 if (init_file(path, sizeof(struct traffic_entry)))
207                 {
208                         fprintf(stderr, "Failed to init %s: %s\n",
209                                         path, strerror(errno));
210
211                         return -1;
212                 }
213         }
214
215         e.time = htonll(time(NULL));
216         e.rxb  = htonll(rxb);
217         e.rxp  = htonll(rxp);
218         e.txb  = htonll(txb);
219         e.txp  = htonll(txp);
220
221         return update_file(path, &e, sizeof(struct traffic_entry));
222 }
223
224 static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
225 {
226         char path[1024];
227
228         struct stat s;
229         struct conn_entry e;
230
231         snprintf(path, sizeof(path), DB_CN_FILE);
232
233         if (stat(path, &s))
234         {
235                 if (init_file(path, sizeof(struct conn_entry)))
236                 {
237                         fprintf(stderr, "Failed to init %s: %s\n",
238                                         path, strerror(errno));
239
240                         return -1;
241                 }
242         }
243
244         e.time  = htonll(time(NULL));
245         e.udp   = htonl(udp);
246         e.tcp   = htonl(tcp);
247         e.other = htonl(other);
248
249         return update_file(path, &e, sizeof(struct conn_entry));
250 }
251
252 static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
253 {
254         char path[1024];
255
256         struct stat s;
257         struct load_entry e;
258
259         snprintf(path, sizeof(path), DB_LD_FILE);
260
261         if (stat(path, &s))
262         {
263                 if (init_file(path, sizeof(struct load_entry)))
264                 {
265                         fprintf(stderr, "Failed to init %s: %s\n",
266                                         path, strerror(errno));
267
268                         return -1;
269                 }
270         }
271
272         e.time   = htonll(time(NULL));
273         e.load1  = htons(load1);
274         e.load5  = htons(load5);
275         e.load15 = htons(load15);
276
277         return update_file(path, &e, sizeof(struct load_entry));
278 }
279
280 static int run_daemon(int nofork)
281 {
282         FILE *info;
283         uint64_t rxb, txb, rxp, txp;
284         uint32_t udp, tcp, other;
285         float lf1, lf5, lf15;
286         char line[1024];
287         char ifname[16];
288
289         struct stat s;
290         const char *ipc = stat("/proc/net/nf_conntrack", &s)
291                 ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
292
293         if (!nofork)
294         {
295                 switch (fork())
296                 {
297                         case -1:
298                                 perror("fork()");
299                                 return -1;
300
301                         case 0:
302                                 if (chdir("/") < 0)
303                                 {
304                                         perror("chdir()");
305                                         exit(1);
306                                 }
307
308                                 close(0);
309                                 close(1);
310                                 close(2);
311                                 break;
312
313                         default:
314                                 exit(0);
315                 }
316         }
317
318
319         /* go */
320         while (1)
321         {
322                 if ((info = fopen("/proc/net/dev", "r")) != NULL)
323                 {
324                         while (fgets(line, sizeof(line), info))
325                         {
326                                 if (strchr(line, '|'))
327                                         continue;
328
329                                 if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
330                                 {
331                                         if (strncmp(ifname, "lo", sizeof(ifname)))
332                                                 update_ifstat(ifname, rxb, rxp, txb, txp);
333                                 }
334                         }
335
336                         fclose(info);
337                 }
338
339                 if ((info = fopen(ipc, "r")) != NULL)
340                 {
341                         udp   = 0;
342                         tcp   = 0;
343                         other = 0;
344
345                         while (fgets(line, sizeof(line), info))
346                         {
347                                 if (strstr(line, "TIME_WAIT"))
348                                         continue;
349
350                                 if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
351                                 {
352                                         if (!strcmp(ifname, "tcp"))
353                                                 tcp++;
354                                         else if (!strcmp(ifname, "udp"))
355                                                 udp++;
356                                         else
357                                                 other++;
358                                 }
359                         }
360
361                         update_cnstat(udp, tcp, other);
362
363                         fclose(info);
364                 }
365
366                 if ((info = fopen("/proc/loadavg", "r")) != NULL)
367                 {
368                         if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
369                         {
370                                 update_ldstat((uint16_t)(lf1  * 100),
371                                                           (uint16_t)(lf5  * 100),
372                                                           (uint16_t)(lf15 * 100));
373                         }
374
375                         fclose(info);
376                 }
377
378                 sleep(STEP_TIME);
379         }
380 }
381
382 static int run_dump_ifname(const char *ifname)
383 {
384         int i;
385         char path[1024];
386         struct file_map m;
387         struct traffic_entry *e;
388
389         snprintf(path, sizeof(path), DB_IF_FILE, ifname);
390
391         if (mmap_file(path, sizeof(struct traffic_entry), &m))
392         {
393                 fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
394                 return 1;
395         }
396
397         for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
398         {
399                 e = (struct traffic_entry *) &m.mmap[i];
400
401                 if (!e->time)
402                         continue;
403
404                 printf("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
405                            ", %" PRIu64 ", %" PRIu64 " ]%s\n",
406                         ntohll(e->time),
407                         ntohll(e->rxb), ntohll(e->rxp),
408                         ntohll(e->txb), ntohll(e->txp),
409                         ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
410         }
411
412         umap_file(&m);
413
414         return 0;
415 }
416
417 static int run_dump_conns(void)
418 {
419         int i;
420         char path[1024];
421         struct file_map m;
422         struct conn_entry *e;
423
424         snprintf(path, sizeof(path), DB_CN_FILE);
425
426         if (mmap_file(path, sizeof(struct conn_entry), &m))
427         {
428                 fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
429                 return 1;
430         }
431
432         for (i = 0; i < m.size; i += sizeof(struct conn_entry))
433         {
434                 e = (struct conn_entry *) &m.mmap[i];
435
436                 if (!e->time)
437                         continue;
438
439                 printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
440                         ntohll(e->time), ntohl(e->udp),
441                         ntohl(e->tcp), ntohl(e->other),
442                         ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
443         }
444
445         umap_file(&m);
446
447         return 0;
448 }
449
450 static int run_dump_load(void)
451 {
452         int i;
453         char path[1024];
454         struct file_map m;
455         struct load_entry *e;
456
457         snprintf(path, sizeof(path), DB_LD_FILE);
458
459         if (mmap_file(path, sizeof(struct load_entry), &m))
460         {
461                 fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
462                 return 1;
463         }
464
465         for (i = 0; i < m.size; i += sizeof(struct load_entry))
466         {
467                 e = (struct load_entry *) &m.mmap[i];
468
469                 if (!e->time)
470                         continue;
471
472                 printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
473                         ntohll(e->time),
474                         ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
475                         ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
476         }
477
478         umap_file(&m);
479
480         return 0;
481 }
482
483
484 int main(int argc, char *argv[])
485 {
486         int opt;
487         int daemon = 0;
488         int nofork = 0;
489
490         while ((opt = getopt(argc, argv, "dfi:cl")) > -1)
491         {
492                 switch (opt)
493                 {
494                         case 'd':
495                                 daemon = 1;
496                                 break;
497
498                         case 'f':
499                                 nofork = 1;
500                                 break;
501
502                         case 'i':
503                                 if (optarg)
504                                         return run_dump_ifname(optarg);
505                                 break;
506
507                         case 'c':
508                                 return run_dump_conns();
509
510                         case 'l':
511                                 return run_dump_load();
512
513                         default:
514                                 break;
515                 }
516         }
517
518         if (daemon)
519                 return run_daemon(nofork);
520
521         else
522                 fprintf(stderr,
523                         "Usage:\n"
524                         "       %s -d [-f]\n"
525                         "       %s -i ifname\n"
526                         "       %s -c\n"
527                         "       %s -l\n",
528                                 argv[0], argv[0], argv[0], argv[0]
529                 );
530
531         return 1;
532 }