root/luci/branches/luci-0.10/modules/admin-full/src/luci-bwc.c

Revision 8145, 14.6 KB (checked in by jow, 17 months ago)

luci-0.10: merge r8144

Line 
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#include <signal.h>
29
30#include <sys/stat.h>
31#include <sys/mman.h>
32#include <arpa/inet.h>
33
34#include <dlfcn.h>
35
36#define STEP_COUNT  60
37#define STEP_TIME   1
38#define TIMEOUT     10
39
40#define PID_PATH    "/var/run/luci-bwc.pid"
41
42#define DB_PATH     "/var/lib/luci-bwc"
43#define DB_IF_FILE  DB_PATH "/if/%s"
44#define DB_RD_FILE  DB_PATH "/radio/%s"
45#define DB_CN_FILE  DB_PATH "/connections"
46#define DB_LD_FILE  DB_PATH "/load"
47
48#define IF_SCAN_PATTERN \
49    " %[^ :]:%u %u" \
50    " %*d %*d %*d %*d %*d %*d" \
51    " %u %u"
52
53#define LD_SCAN_PATTERN \
54    "%f %f %f"
55
56
57struct file_map {
58    int fd;
59    int size;
60    char *mmap;
61};
62
63struct traffic_entry {
64    uint32_t time;
65    uint32_t rxb;
66    uint32_t rxp;
67    uint32_t txb;
68    uint32_t txp;
69};
70
71struct conn_entry {
72    uint32_t time;
73    uint32_t udp;
74    uint32_t tcp;
75    uint32_t other;
76};
77
78struct load_entry {
79    uint32_t time;
80    uint16_t load1;
81    uint16_t load5;
82    uint16_t load15;
83};
84
85struct radio_entry {
86    uint32_t time;
87    uint16_t rate;
88    uint8_t  rssi;
89    uint8_t  noise;
90};
91
92static int readpid(void)
93{
94    int fd;
95    int pid = -1;
96    char buf[9] = { 0 };
97
98    if ((fd = open(PID_PATH, O_RDONLY)) > -1)
99    {
100        if (read(fd, buf, sizeof(buf)))
101        {
102            buf[8] = 0;
103            pid = atoi(buf);
104        }
105
106        close(fd);
107    }
108
109    return pid;
110}
111
112static int writepid(void)
113{
114    int fd;
115    int wlen;
116    char buf[9] = { 0 };
117
118    if ((fd = open(PID_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0600)) > -1)
119    {
120        wlen = snprintf(buf, sizeof(buf), "%i", getpid());
121        write(fd, buf, wlen);
122        close(fd);
123
124        return 0;
125    }
126
127    return -1;
128}
129
130static int timeout = TIMEOUT;
131static int countdown = -1;
132
133static void reset_countdown(int sig)
134{
135    countdown = timeout;
136
137}
138
139
140static char *progname;
141static int prognamelen;
142
143static int (*iw_get_rate)(const char *, int *) = NULL;
144static int (*iw_get_rssi)(const char *, int *) = NULL;
145static int (*iw_get_noise)(const char *, int *) = NULL;
146
147
148static int init_directory(char *path)
149{
150    char *p = path;
151
152    for (p = &path[1]; *p; p++)
153    {
154        if (*p == '/')
155        {
156            *p = 0;
157
158            if (mkdir(path, 0700) && (errno != EEXIST))
159                return -1;
160
161            *p = '/';
162        }
163    }
164
165    return 0;
166}
167
168static int init_file(char *path, int esize)
169{
170    int i, file;
171    char buf[sizeof(struct traffic_entry)] = { 0 };
172
173    if (init_directory(path))
174        return -1;
175
176    if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
177    {
178        for (i = 0; i < STEP_COUNT; i++)
179        {
180            if (write(file, buf, esize) < 0)
181                break;
182        }
183
184        close(file);
185
186        return 0;
187    }
188
189    return -1;
190}
191
192static inline uint32_t timeof(void *entry)
193{
194    return ntohl(((struct traffic_entry *)entry)->time);
195}
196
197static int update_file(const char *path, void *entry, int esize)
198{
199    int rv = -1;
200    int file;
201    char *map;
202
203    if ((file = open(path, O_RDWR)) >= 0)
204    {
205        map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
206                   MAP_SHARED | MAP_LOCKED, file, 0);
207
208        if ((map != NULL) && (map != MAP_FAILED))
209        {
210            if (timeof(entry) > timeof(map + esize * (STEP_COUNT-1)))
211            {
212                memmove(map, map + esize, esize * (STEP_COUNT-1));
213                memcpy(map + esize * (STEP_COUNT-1), entry, esize);
214            }
215
216            munmap(map, esize * STEP_COUNT);
217
218            rv = 0;
219        }
220
221        close(file);
222    }
223
224    return rv;
225}
226
227static int mmap_file(const char *path, int esize, struct file_map *m)
228{
229    m->fd   = -1;
230    m->size = -1;
231    m->mmap = NULL;
232
233    if ((m->fd = open(path, O_RDONLY)) >= 0)
234    {
235        m->size = STEP_COUNT * esize;
236        m->mmap = mmap(NULL, m->size, PROT_READ,
237                       MAP_SHARED | MAP_LOCKED, m->fd, 0);
238
239        if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
240            return 0;
241    }
242
243    return -1;
244}
245
246static void umap_file(struct file_map *m)
247{
248    if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
249        munmap(m->mmap, m->size);
250
251    if (m->fd > -1)
252        close(m->fd);
253}
254
255static void * iwinfo_open(void)
256{
257    return dlopen("/usr/lib/libiwinfo.so", RTLD_LAZY);
258}
259
260static int iwinfo_update(
261    void *iw, const char *ifname, uint16_t *rate, uint8_t *rssi, uint8_t *noise
262) {
263    int (*probe)(const char *);
264    int val;
265
266    if (!iw_get_rate)
267    {
268        if ((probe = dlsym(iw, "nl80211_probe")) != NULL && probe(ifname))
269        {
270            iw_get_rate  = dlsym(iw, "nl80211_get_bitrate");
271            iw_get_rssi  = dlsym(iw, "nl80211_get_signal");
272            iw_get_noise = dlsym(iw, "nl80211_get_noise");
273        }
274        else if ((probe = dlsym(iw, "madwifi_probe")) != NULL && probe(ifname))
275        {
276            iw_get_rate  = dlsym(iw, "madwifi_get_bitrate");
277            iw_get_rssi  = dlsym(iw, "madwifi_get_signal");
278            iw_get_noise = dlsym(iw, "madwifi_get_noise");
279        }
280        else if ((probe = dlsym(iw, "wl_probe")) != NULL && probe(ifname))
281        {
282            iw_get_rate  = dlsym(iw, "wl_get_bitrate");
283            iw_get_rssi  = dlsym(iw, "wl_get_signal");
284            iw_get_noise = dlsym(iw, "wl_get_noise");
285        }
286        else
287        {
288            return 0;
289        }
290    }
291
292    *rate = (iw_get_rate && !iw_get_rate(ifname, &val)) ? val : 0;
293    *rssi = (iw_get_rssi && !iw_get_rssi(ifname, &val)) ? val : 0;
294    *noise = (iw_get_noise && !iw_get_noise(ifname, &val)) ? val : 0;
295
296    return 1;
297}
298
299static void iwinfo_close(void *iw)
300{
301    void (*do_close)(void);
302
303    if ((do_close = dlsym(iw, "nl80211_close")) != NULL) do_close();
304    if ((do_close = dlsym(iw, "madwifi_close")) != NULL) do_close();
305    if ((do_close = dlsym(iw, "wl_close"))      != NULL) do_close();
306    if ((do_close = dlsym(iw, "wext_close"))    != NULL) do_close();
307    if ((do_close = dlsym(iw, "iwinfo_close"))  != NULL) do_close();
308
309    dlclose(iw);
310}
311
312
313static int update_ifstat(
314    const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
315) {
316    char path[1024];
317
318    struct stat s;
319    struct traffic_entry e;
320
321    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
322
323    if (stat(path, &s))
324    {
325        if (init_file(path, sizeof(struct traffic_entry)))
326        {
327            fprintf(stderr, "Failed to init %s: %s\n",
328                    path, strerror(errno));
329
330            return -1;
331        }
332    }
333
334    e.time = htonl(time(NULL));
335    e.rxb  = htonl(rxb);
336    e.rxp  = htonl(rxp);
337    e.txb  = htonl(txb);
338    e.txp  = htonl(txp);
339
340    return update_file(path, &e, sizeof(struct traffic_entry));
341}
342
343static int update_radiostat(
344    const char *ifname, uint16_t rate, uint8_t rssi, uint8_t noise
345) {
346    char path[1024];
347
348    struct stat s;
349    struct radio_entry e;
350
351    snprintf(path, sizeof(path), DB_RD_FILE, ifname);
352
353    if (stat(path, &s))
354    {
355        if (init_file(path, sizeof(struct radio_entry)))
356        {
357            fprintf(stderr, "Failed to init %s: %s\n",
358                    path, strerror(errno));
359
360            return -1;
361        }
362    }
363
364    e.time  = htonl(time(NULL));
365    e.rate  = htons(rate);
366    e.rssi  = rssi;
367    e.noise = noise;
368
369    return update_file(path, &e, sizeof(struct radio_entry));
370}
371
372static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
373{
374    char path[1024];
375
376    struct stat s;
377    struct conn_entry e;
378
379    snprintf(path, sizeof(path), DB_CN_FILE);
380
381    if (stat(path, &s))
382    {
383        if (init_file(path, sizeof(struct conn_entry)))
384        {
385            fprintf(stderr, "Failed to init %s: %s\n",
386                    path, strerror(errno));
387
388            return -1;
389        }
390    }
391
392    e.time  = htonl(time(NULL));
393    e.udp   = htonl(udp);
394    e.tcp   = htonl(tcp);
395    e.other = htonl(other);
396
397    return update_file(path, &e, sizeof(struct conn_entry));
398}
399
400static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
401{
402    char path[1024];
403
404    struct stat s;
405    struct load_entry e;
406
407    snprintf(path, sizeof(path), DB_LD_FILE);
408
409    if (stat(path, &s))
410    {
411        if (init_file(path, sizeof(struct load_entry)))
412        {
413            fprintf(stderr, "Failed to init %s: %s\n",
414                    path, strerror(errno));
415
416            return -1;
417        }
418    }
419
420    e.time   = htonl(time(NULL));
421    e.load1  = htons(load1);
422    e.load5  = htons(load5);
423    e.load15 = htons(load15);
424
425    return update_file(path, &e, sizeof(struct load_entry));
426}
427
428static int run_daemon(void)
429{
430    FILE *info;
431    uint32_t rxb, txb, rxp, txp;
432    uint32_t udp, tcp, other;
433    uint16_t rate;
434    uint8_t rssi, noise;
435    float lf1, lf5, lf15;
436    char line[1024];
437    char ifname[16];
438    int i;
439    void *iw;
440    struct sigaction sa;
441
442    struct stat s;
443    const char *ipc = stat("/proc/net/nf_conntrack", &s)
444        ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
445
446    switch (fork())
447    {
448        case -1:
449            perror("fork()");
450            return -1;
451
452        case 0:
453            if (chdir("/") < 0)
454            {
455                perror("chdir()");
456                exit(1);
457            }
458
459            close(0);
460            close(1);
461            close(2);
462            break;
463
464        default:
465            return 0;
466    }
467
468    /* setup USR1 signal handler to reset timer */
469    sa.sa_handler = reset_countdown;
470    sa.sa_flags   = SA_RESTART;
471    sigemptyset(&sa.sa_mask);
472    sigaction(SIGUSR1, &sa, NULL);
473
474    /* write pid */
475    if (writepid())
476    {
477        fprintf(stderr, "Failed to write pid file: %s\n", strerror(errno));
478        return 1;
479    }
480
481    /* initialize iwinfo */
482    iw = iwinfo_open();
483
484    /* go */
485    for (reset_countdown(0); countdown >= 0; countdown--)
486    {
487        /* alter progname for ps, top */
488        memset(progname, 0, prognamelen);
489        snprintf(progname, prognamelen, "luci-bwc %d", countdown);
490
491        if ((info = fopen("/proc/net/dev", "r")) != NULL)
492        {
493            while (fgets(line, sizeof(line), info))
494            {
495                if (strchr(line, '|'))
496                    continue;
497
498                if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
499                {
500                    if (strncmp(ifname, "lo", sizeof(ifname)))
501                        update_ifstat(ifname, rxb, rxp, txb, txp);
502                }
503            }
504
505            fclose(info);
506        }
507
508        if (iw)
509        {
510            for (i = 0; i < 5; i++)
511            {
512#define iwinfo_checkif(pattern) \
513                do {                                                      \
514                    snprintf(ifname, sizeof(ifname), pattern, i);         \
515                    if (iwinfo_update(iw, ifname, &rate, &rssi, &noise))  \
516                    {                                                     \
517                        update_radiostat(ifname, rate, rssi, noise);      \
518                        continue;                                         \
519                    }                                                     \
520                } while(0)
521
522                iwinfo_checkif("wlan%d");
523                iwinfo_checkif("ath%d");
524                iwinfo_checkif("wl%d");
525            }
526        }
527
528        if ((info = fopen(ipc, "r")) != NULL)
529        {
530            udp   = 0;
531            tcp   = 0;
532            other = 0;
533
534            while (fgets(line, sizeof(line), info))
535            {
536                if (strstr(line, "TIME_WAIT"))
537                    continue;
538
539                if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
540                {
541                    if (!strcmp(ifname, "tcp"))
542                        tcp++;
543                    else if (!strcmp(ifname, "udp"))
544                        udp++;
545                    else
546                        other++;
547                }
548            }
549
550            update_cnstat(udp, tcp, other);
551
552            fclose(info);
553        }
554
555        if ((info = fopen("/proc/loadavg", "r")) != NULL)
556        {
557            if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
558            {
559                update_ldstat((uint16_t)(lf1  * 100),
560                              (uint16_t)(lf5  * 100),
561                              (uint16_t)(lf15 * 100));
562            }
563
564            fclose(info);
565        }
566
567        sleep(STEP_TIME);
568    }
569
570    unlink(PID_PATH);
571
572    if (iw)
573        iwinfo_close(iw);
574
575    return 0;
576}
577
578static void check_daemon(void)
579{
580    int pid;
581
582    if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
583    {
584        /* daemon ping failed, try to start it up */
585        if (run_daemon())
586        {
587            fprintf(stderr,
588                "Failed to ping daemon and unable to start it up: %s\n",
589                strerror(errno));
590
591            exit(1);
592        }
593    }
594    else if (kill(pid, SIGUSR1))
595    {
596        fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
597        exit(2);
598    }
599}
600
601static int run_dump_ifname(const char *ifname)
602{
603    int i;
604    char path[1024];
605    struct file_map m;
606    struct traffic_entry *e;
607
608    check_daemon();
609    snprintf(path, sizeof(path), DB_IF_FILE, ifname);
610
611    if (mmap_file(path, sizeof(struct traffic_entry), &m))
612    {
613        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
614        return 1;
615    }
616
617    for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
618    {
619        e = (struct traffic_entry *) &m.mmap[i];
620
621        if (!e->time)
622            continue;
623
624        printf("[ %u, %u, %" PRIu32
625               ", %u, %u ]%s\n",
626            ntohl(e->time),
627            ntohl(e->rxb), ntohl(e->rxp),
628            ntohl(e->txb), ntohl(e->txp),
629            ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
630    }
631
632    umap_file(&m);
633
634    return 0;
635}
636
637static int run_dump_radio(const char *ifname)
638{
639    int i;
640    char path[1024];
641    struct file_map m;
642    struct radio_entry *e;
643
644    check_daemon();
645    snprintf(path, sizeof(path), DB_RD_FILE, ifname);
646
647    if (mmap_file(path, sizeof(struct radio_entry), &m))
648    {
649        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
650        return 1;
651    }
652
653    for (i = 0; i < m.size; i += sizeof(struct radio_entry))
654    {
655        e = (struct radio_entry *) &m.mmap[i];
656
657        if (!e->time)
658            continue;
659
660        printf("[ %u, %d, %d, %d ]%s\n",
661            ntohl(e->time),
662            e->rate, e->rssi, e->noise,
663            ((i + sizeof(struct radio_entry)) < m.size) ? "," : "");
664    }
665
666    umap_file(&m);
667
668    return 0;
669}
670
671static int run_dump_conns(void)
672{
673    int i;
674    char path[1024];
675    struct file_map m;
676    struct conn_entry *e;
677
678    check_daemon();
679    snprintf(path, sizeof(path), DB_CN_FILE);
680
681    if (mmap_file(path, sizeof(struct conn_entry), &m))
682    {
683        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
684        return 1;
685    }
686
687    for (i = 0; i < m.size; i += sizeof(struct conn_entry))
688    {
689        e = (struct conn_entry *) &m.mmap[i];
690
691        if (!e->time)
692            continue;
693
694        printf("[ %u, %u, %u, %u ]%s\n",
695            ntohl(e->time), ntohl(e->udp),
696            ntohl(e->tcp), ntohl(e->other),
697            ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
698    }
699
700    umap_file(&m);
701
702    return 0;
703}
704
705static int run_dump_load(void)
706{
707    int i;
708    char path[1024];
709    struct file_map m;
710    struct load_entry *e;
711
712    check_daemon();
713    snprintf(path, sizeof(path), DB_LD_FILE);
714
715    if (mmap_file(path, sizeof(struct load_entry), &m))
716    {
717        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
718        return 1;
719    }
720
721    for (i = 0; i < m.size; i += sizeof(struct load_entry))
722    {
723        e = (struct load_entry *) &m.mmap[i];
724
725        if (!e->time)
726            continue;
727
728        printf("[ %u, %u, %u, %u ]%s\n",
729            ntohl(e->time),
730            ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
731            ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
732    }
733
734    umap_file(&m);
735
736    return 0;
737}
738
739
740int main(int argc, char *argv[])
741{
742    int opt;
743
744    progname = argv[0];
745    prognamelen = -1;
746
747    for (opt = 0; opt < argc; opt++)
748        prognamelen += 1 + strlen(argv[opt]);
749
750    while ((opt = getopt(argc, argv, "t:i:r:cl")) > -1)
751    {
752        switch (opt)
753        {
754            case 't':
755                timeout = atoi(optarg);
756                break;
757
758            case 'i':
759                if (optarg)
760                    return run_dump_ifname(optarg);
761                break;
762
763            case 'r':
764                if (optarg)
765                    return run_dump_radio(optarg);
766                break;
767
768            case 'c':
769                return run_dump_conns();
770
771            case 'l':
772                return run_dump_load();
773
774            default:
775                break;
776        }
777    }
778
779    fprintf(stderr,
780        "Usage:\n"
781        "   %s [-t timeout] -i ifname\n"
782        "   %s [-t timeout] -r radiodev\n"
783        "   %s [-t timeout] -c\n"
784        "   %s [-t timeout] -l\n",
785            argv[0], argv[0], argv[0], argv[0]
786    );
787
788    return 1;
789}
Note: See TracBrowser for help on using the browser.