c665fd5ced4ddc9f7bac3b00d13263dfb800a0a3
[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_LD_FILE DB_PATH "/load"
40
41 #define IF_SCAN_PATTERN \
42 " %[^ :]:%" SCNu64 " %" SCNu64 \
43 " %*d %*d %*d %*d %*d %*d" \
44 " %" SCNu64 " %" SCNu64
45
46 #define LD_SCAN_PATTERN \
47 "%f %f %f"
48
49
50 struct traffic_entry {
51 uint64_t time;
52 uint64_t rxb;
53 uint64_t rxp;
54 uint64_t txb;
55 uint64_t txp;
56 };
57
58 struct load_entry {
59 uint64_t time;
60 uint16_t load1;
61 uint16_t load5;
62 uint16_t load15;
63 };
64
65
66 static uint64_t htonll(uint64_t value)
67 {
68 int num = 1;
69
70 if (*(char *)&num == 1)
71 return htonl((uint32_t)(value & 0xFFFFFFFF)) |
72 htonl((uint32_t)(value >> 32));
73
74 return value;
75 }
76
77 #define ntohll htonll
78
79
80 static int init_directory(char *path)
81 {
82 char *p = path;
83
84 for (p = &path[1]; *p; p++)
85 {
86 if (*p == '/')
87 {
88 *p = 0;
89
90 if (mkdir(path, 0700) && (errno != EEXIST))
91 return -1;
92
93 *p = '/';
94 }
95 }
96
97 return 0;
98 }
99
100 static int update_file(const char *path, void *entry, int esize)
101 {
102 int rv = -1;
103 int file;
104 char *map;
105
106 if ((file = open(path, O_RDWR)) >= 0)
107 {
108 map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
109 MAP_SHARED | MAP_LOCKED, file, 0);
110
111 if ((map != NULL) && (map != MAP_FAILED))
112 {
113 memmove(map, map + esize, esize * (STEP_COUNT-1));
114 memcpy(map + esize * (STEP_COUNT-1), entry, esize);
115
116 munmap(map, esize * STEP_COUNT);
117
118 rv = 0;
119 }
120
121 close(file);
122 }
123
124 return rv;
125 }
126
127
128 static int init_ifstat(const char *ifname)
129 {
130 int i, file;
131 char path[1024];
132 struct traffic_entry e = { 0 };
133
134 snprintf(path, sizeof(path), DB_IF_FILE, ifname);
135
136 if (init_directory(path))
137 return -1;
138
139 if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
140 {
141 for (i = 0; i < STEP_COUNT; i++)
142 {
143 if (write(file, &e, sizeof(struct traffic_entry)) < 0)
144 break;
145 }
146
147 close(file);
148
149 return 0;
150 }
151
152 return -1;
153 }
154
155 static int update_ifstat(
156 const char *ifname, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
157 ) {
158 char path[1024];
159
160 struct stat s;
161 struct traffic_entry e;
162
163 snprintf(path, sizeof(path), DB_IF_FILE, ifname);
164
165 if (stat(path, &s))
166 {
167 if (init_ifstat(ifname))
168 {
169 fprintf(stderr, "Failed to init %s: %s\n",
170 path, strerror(errno));
171
172 return -1;
173 }
174 }
175
176 e.time = htonll(time(NULL));
177 e.rxb = htonll(rxb);
178 e.rxp = htonll(rxp);
179 e.txb = htonll(txb);
180 e.txp = htonll(txp);
181
182 return update_file(path, &e, sizeof(struct traffic_entry));
183 }
184
185 static int init_ldstat(void)
186 {
187 int i, file;
188 char path[1024];
189 struct load_entry e = { 0 };
190
191 snprintf(path, sizeof(path), DB_LD_FILE);
192
193 if (init_directory(path))
194 return -1;
195
196 if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
197 {
198 for (i = 0; i < STEP_COUNT; i++)
199 {
200 if (write(file, &e, sizeof(struct load_entry)) < 0)
201 break;
202 }
203
204 close(file);
205
206 return 0;
207 }
208
209 return -1;
210 }
211
212 static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
213 {
214 char path[1024];
215
216 struct stat s;
217 struct load_entry e;
218
219 snprintf(path, sizeof(path), DB_LD_FILE);
220
221 if (stat(path, &s))
222 {
223 if (init_ldstat())
224 {
225 fprintf(stderr, "Failed to init %s: %s\n",
226 path, strerror(errno));
227
228 return -1;
229 }
230 }
231
232 e.time = htonll(time(NULL));
233 e.load1 = htons(load1);
234 e.load5 = htons(load5);
235 e.load15 = htons(load15);
236
237 return update_file(path, &e, sizeof(struct load_entry));
238 }
239
240 static int run_daemon(int nofork)
241 {
242 FILE *info;
243 uint64_t rxb, txb, rxp, txp;
244 float lf1, lf5, lf15;
245 char line[1024];
246 char ifname[16];
247
248
249 if (!nofork)
250 {
251 switch (fork())
252 {
253 case -1:
254 perror("fork()");
255 return -1;
256
257 case 0:
258 if (chdir("/") < 0)
259 {
260 perror("chdir()");
261 exit(1);
262 }
263
264 close(0);
265 close(1);
266 close(2);
267 break;
268
269 default:
270 exit(0);
271 }
272 }
273
274
275 /* go */
276 while (1)
277 {
278 if ((info = fopen("/proc/net/dev", "r")) != NULL)
279 {
280 while (fgets(line, sizeof(line), info))
281 {
282 if (strchr(line, '|'))
283 continue;
284
285 if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
286 {
287 if (strncmp(ifname, "lo", sizeof(ifname)))
288 update_ifstat(ifname, rxb, rxp, txb, txp);
289 }
290 }
291
292 fclose(info);
293 }
294
295 if ((info = fopen("/proc/loadavg", "r")) != NULL)
296 {
297 if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
298 {
299 update_ldstat((uint16_t)(lf1 * 100),
300 (uint16_t)(lf5 * 100),
301 (uint16_t)(lf15 * 100));
302 }
303
304 fclose(info);
305 }
306
307 sleep(STEP_TIME);
308 }
309 }
310
311 static int run_dump_ifname(const char *ifname)
312 {
313 int rv = 1;
314
315 int i, file;
316 int entrysize = sizeof(struct traffic_entry);
317 int mapsize = STEP_COUNT * entrysize;
318
319 char path[1024];
320 char *map;
321
322 struct traffic_entry *e;
323
324 snprintf(path, sizeof(path), DB_IF_FILE, ifname);
325
326 if ((file = open(path, O_RDONLY)) >= 0)
327 {
328 map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED | MAP_LOCKED, file, 0);
329
330 if ((map != NULL) && (map != MAP_FAILED))
331 {
332 for (i = 0; i < mapsize; i += entrysize)
333 {
334 e = (struct traffic_entry *) &map[i];
335
336 if (!e->time)
337 continue;
338
339 printf("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
340 ", %" PRIu64 ", %" PRIu64 " ]%s\n",
341 ntohll(e->time),
342 ntohll(e->rxb), ntohll(e->rxp),
343 ntohll(e->txb), ntohll(e->txp),
344 ((i + entrysize) < mapsize) ? "," : "");
345 }
346
347 munmap(map, mapsize);
348 rv = 0;
349 }
350
351 close(file);
352 }
353 else
354 {
355 fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
356 }
357
358 return rv;
359 }
360
361 static int run_dump_load(void)
362 {
363 int rv = 1;
364
365 int i, file;
366 int entrysize = sizeof(struct load_entry);
367 int mapsize = STEP_COUNT * entrysize;
368
369 char path[1024];
370 char *map;
371
372 struct load_entry *e;
373
374 snprintf(path, sizeof(path), DB_LD_FILE);
375
376 if ((file = open(path, O_RDONLY)) >= 0)
377 {
378 map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED | MAP_LOCKED, file, 0);
379
380 if ((map != NULL) && (map != MAP_FAILED))
381 {
382 for (i = 0; i < mapsize; i += entrysize)
383 {
384 e = (struct load_entry *) &map[i];
385
386 if (!e->time)
387 continue;
388
389 printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
390 ntohll(e->time),
391 ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
392 ((i + entrysize) < mapsize) ? "," : "");
393 }
394
395 munmap(map, mapsize);
396 rv = 0;
397 }
398
399 close(file);
400 }
401 else
402 {
403 fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
404 }
405
406 return rv;
407 }
408
409
410 int main(int argc, char *argv[])
411 {
412 int opt;
413 int daemon = 0;
414 int nofork = 0;
415 int iprint = 0;
416 int lprint = 0;
417 char *ifname = NULL;
418
419 while ((opt = getopt(argc, argv, "dfi:l")) > -1)
420 {
421 switch (opt)
422 {
423 case 'd':
424 daemon = 1;
425 break;
426
427 case 'f':
428 nofork = 1;
429 break;
430
431 case 'i':
432 iprint = 1;
433 ifname = optarg;
434 break;
435
436 case 'l':
437 lprint = 1;
438 break;
439
440 default:
441 break;
442 }
443 }
444
445 if (daemon)
446 return run_daemon(nofork);
447
448 else if (iprint && ifname)
449 return run_dump_ifname(ifname);
450
451 else if (lprint)
452 return run_dump_load();
453
454 else
455 fprintf(stderr,
456 "Usage:\n"
457 " %s -d [-f]\n"
458 " %s -i ifname\n"
459 " %s -l\n",
460 argv[0], argv[0], argv[0]
461 );
462
463 return 1;
464 }