123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- /*
- * luci-bwc - Very simple bandwidth collector cache for LuCI realtime graphs
- *
- * Copyright (C) 2010 Jo-Philipp Wich <jow@openwrt.org>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdint.h>
- #include <inttypes.h>
- #include <fcntl.h>
- #include <time.h>
- #include <errno.h>
- #include <unistd.h>
- #include <signal.h>
- #include <sys/stat.h>
- #include <sys/mman.h>
- #include <arpa/inet.h>
- #include <dlfcn.h>
- #include <iwinfo.h>
- #define STEP_COUNT 60
- #define STEP_TIME 1
- #define TIMEOUT 10
- #define PID_PATH "/var/run/luci-bwc.pid"
- #define DB_PATH "/var/lib/luci-bwc"
- #define DB_IF_FILE DB_PATH "/if/%s"
- #define DB_RD_FILE DB_PATH "/radio/%s"
- #define DB_CN_FILE DB_PATH "/connections"
- #define DB_LD_FILE DB_PATH "/load"
- #define IF_SCAN_PATTERN \
- " %[^ :]:%u %u" \
- " %*d %*d %*d %*d %*d %*d" \
- " %u %u"
- #define LD_SCAN_PATTERN \
- "%f %f %f"
- struct file_map {
- int fd;
- int size;
- char *mmap;
- };
- struct traffic_entry {
- uint32_t time;
- uint32_t rxb;
- uint32_t rxp;
- uint32_t txb;
- uint32_t txp;
- };
- struct conn_entry {
- uint32_t time;
- uint32_t udp;
- uint32_t tcp;
- uint32_t other;
- };
- struct load_entry {
- uint32_t time;
- uint16_t load1;
- uint16_t load5;
- uint16_t load15;
- };
- struct radio_entry {
- uint32_t time;
- uint16_t rate;
- uint8_t rssi;
- uint8_t noise;
- };
- static int readpid(void)
- {
- int fd;
- int pid = -1;
- char buf[9] = { 0 };
- if ((fd = open(PID_PATH, O_RDONLY)) > -1)
- {
- if (read(fd, buf, sizeof(buf)))
- {
- buf[8] = 0;
- pid = atoi(buf);
- }
- close(fd);
- }
- return pid;
- }
- static int writepid(void)
- {
- int fd;
- int wlen;
- char buf[9] = { 0 };
- if ((fd = open(PID_PATH, O_WRONLY | O_CREAT | O_TRUNC, 0600)) > -1)
- {
- wlen = snprintf(buf, sizeof(buf), "%i", getpid());
- write(fd, buf, wlen);
- close(fd);
- return 0;
- }
- return -1;
- }
- static int timeout = TIMEOUT;
- static int countdown = -1;
- static void reset_countdown(int sig)
- {
- countdown = timeout;
- }
- static char *progname;
- static int prognamelen;
- static struct iwinfo_ops *backend = NULL;
- static int init_directory(char *path)
- {
- char *p = path;
- for (p = &path[1]; *p; p++)
- {
- if (*p == '/')
- {
- *p = 0;
- if (mkdir(path, 0700) && (errno != EEXIST))
- return -1;
- *p = '/';
- }
- }
- return 0;
- }
- static int init_file(char *path, int esize)
- {
- int i, file;
- char buf[sizeof(struct traffic_entry)] = { 0 };
- if (init_directory(path))
- return -1;
- if ((file = open(path, O_WRONLY | O_CREAT, 0600)) >= 0)
- {
- for (i = 0; i < STEP_COUNT; i++)
- {
- if (write(file, buf, esize) < 0)
- break;
- }
- close(file);
- return 0;
- }
- return -1;
- }
- static inline uint32_t timeof(void *entry)
- {
- return ntohl(((struct traffic_entry *)entry)->time);
- }
- static int update_file(const char *path, void *entry, int esize)
- {
- int rv = -1;
- int file;
- char *map;
- if ((file = open(path, O_RDWR)) >= 0)
- {
- map = mmap(NULL, esize * STEP_COUNT, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_LOCKED, file, 0);
- if ((map != NULL) && (map != MAP_FAILED))
- {
- if (timeof(entry) > timeof(map + esize * (STEP_COUNT-1)))
- {
- memmove(map, map + esize, esize * (STEP_COUNT-1));
- memcpy(map + esize * (STEP_COUNT-1), entry, esize);
- }
- munmap(map, esize * STEP_COUNT);
- rv = 0;
- }
- close(file);
- }
- return rv;
- }
- static int mmap_file(const char *path, int esize, struct file_map *m)
- {
- m->fd = -1;
- m->size = -1;
- m->mmap = NULL;
- if ((m->fd = open(path, O_RDONLY)) >= 0)
- {
- m->size = STEP_COUNT * esize;
- m->mmap = mmap(NULL, m->size, PROT_READ,
- MAP_SHARED | MAP_LOCKED, m->fd, 0);
- if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
- return 0;
- }
- return -1;
- }
- static void umap_file(struct file_map *m)
- {
- if ((m->mmap != NULL) && (m->mmap != MAP_FAILED))
- munmap(m->mmap, m->size);
- if (m->fd > -1)
- close(m->fd);
- }
- static void * iw_open(void)
- {
- return dlopen("/usr/lib/libiwinfo.so", RTLD_LAZY);
- }
- static int iw_update(
- void *iw, const char *ifname, uint16_t *rate, uint8_t *rssi, uint8_t *noise
- ) {
- struct iwinfo_ops *(*probe)(const char *);
- int val;
- if (!backend)
- {
- probe = dlsym(iw, "iwinfo_backend");
- if (!probe)
- return 0;
- backend = probe(ifname);
- if (!backend)
- return 0;
- }
- *rate = (backend->bitrate && !backend->bitrate(ifname, &val)) ? val : 0;
- *rssi = (backend->signal && !backend->signal(ifname, &val)) ? val : 0;
- *noise = (backend->noise && !backend->noise(ifname, &val)) ? val : 0;
- return 1;
- }
- static void iw_close(void *iw)
- {
- void (*finish)(void);
- finish = dlsym(iw, "iwinfo_finish");
- if (finish)
- finish();
- dlclose(iw);
- }
- static int update_ifstat(
- const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
- ) {
- char path[1024];
- struct stat s;
- struct traffic_entry e;
- snprintf(path, sizeof(path), DB_IF_FILE, ifname);
- if (stat(path, &s))
- {
- if (init_file(path, sizeof(struct traffic_entry)))
- {
- fprintf(stderr, "Failed to init %s: %s\n",
- path, strerror(errno));
- return -1;
- }
- }
- e.time = htonl(time(NULL));
- e.rxb = htonl(rxb);
- e.rxp = htonl(rxp);
- e.txb = htonl(txb);
- e.txp = htonl(txp);
- return update_file(path, &e, sizeof(struct traffic_entry));
- }
- static int update_radiostat(
- const char *ifname, uint16_t rate, uint8_t rssi, uint8_t noise
- ) {
- char path[1024];
- struct stat s;
- struct radio_entry e;
- snprintf(path, sizeof(path), DB_RD_FILE, ifname);
- if (stat(path, &s))
- {
- if (init_file(path, sizeof(struct radio_entry)))
- {
- fprintf(stderr, "Failed to init %s: %s\n",
- path, strerror(errno));
- return -1;
- }
- }
- e.time = htonl(time(NULL));
- e.rate = htons(rate);
- e.rssi = rssi;
- e.noise = noise;
- return update_file(path, &e, sizeof(struct radio_entry));
- }
- static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
- {
- char path[1024];
- struct stat s;
- struct conn_entry e;
- snprintf(path, sizeof(path), DB_CN_FILE);
- if (stat(path, &s))
- {
- if (init_file(path, sizeof(struct conn_entry)))
- {
- fprintf(stderr, "Failed to init %s: %s\n",
- path, strerror(errno));
- return -1;
- }
- }
- e.time = htonl(time(NULL));
- e.udp = htonl(udp);
- e.tcp = htonl(tcp);
- e.other = htonl(other);
- return update_file(path, &e, sizeof(struct conn_entry));
- }
- static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
- {
- char path[1024];
- struct stat s;
- struct load_entry e;
- snprintf(path, sizeof(path), DB_LD_FILE);
- if (stat(path, &s))
- {
- if (init_file(path, sizeof(struct load_entry)))
- {
- fprintf(stderr, "Failed to init %s: %s\n",
- path, strerror(errno));
- return -1;
- }
- }
- e.time = htonl(time(NULL));
- e.load1 = htons(load1);
- e.load5 = htons(load5);
- e.load15 = htons(load15);
- return update_file(path, &e, sizeof(struct load_entry));
- }
- static int run_daemon(void)
- {
- FILE *info;
- uint32_t rxb, txb, rxp, txp;
- uint32_t udp, tcp, other;
- uint16_t rate;
- uint8_t rssi, noise;
- float lf1, lf5, lf15;
- char line[1024];
- char ifname[16];
- int i;
- void *iw;
- struct sigaction sa;
- struct stat s;
- const char *ipc = stat("/proc/net/nf_conntrack", &s)
- ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
- switch (fork())
- {
- case -1:
- perror("fork()");
- return -1;
- case 0:
- if (chdir("/") < 0)
- {
- perror("chdir()");
- exit(1);
- }
- close(0);
- close(1);
- close(2);
- break;
- default:
- return 0;
- }
- /* setup USR1 signal handler to reset timer */
- sa.sa_handler = reset_countdown;
- sa.sa_flags = SA_RESTART;
- sigemptyset(&sa.sa_mask);
- sigaction(SIGUSR1, &sa, NULL);
- /* write pid */
- if (writepid())
- {
- fprintf(stderr, "Failed to write pid file: %s\n", strerror(errno));
- return 1;
- }
- /* initialize iwinfo */
- iw = iw_open();
- /* go */
- for (reset_countdown(0); countdown >= 0; countdown--)
- {
- /* alter progname for ps, top */
- memset(progname, 0, prognamelen);
- snprintf(progname, prognamelen, "luci-bwc %d", countdown);
- if ((info = fopen("/proc/net/dev", "r")) != NULL)
- {
- while (fgets(line, sizeof(line), info))
- {
- if (strchr(line, '|'))
- continue;
- if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
- {
- if (strncmp(ifname, "lo", sizeof(ifname)))
- update_ifstat(ifname, rxb, rxp, txb, txp);
- }
- }
- fclose(info);
- }
- if (iw)
- {
- for (i = 0; i < 5; i++)
- {
- #define iw_checkif(pattern) \
- do { \
- snprintf(ifname, sizeof(ifname), pattern, i); \
- if (iw_update(iw, ifname, &rate, &rssi, &noise)) \
- { \
- update_radiostat(ifname, rate, rssi, noise); \
- continue; \
- } \
- } while(0)
- iw_checkif("wlan%d");
- iw_checkif("ath%d");
- iw_checkif("wl%d");
- }
- }
- if ((info = fopen(ipc, "r")) != NULL)
- {
- udp = 0;
- tcp = 0;
- other = 0;
- while (fgets(line, sizeof(line), info))
- {
- if (strstr(line, "TIME_WAIT"))
- continue;
- if (strstr(line, "src=127.0.0.1 ") &&
- strstr(line, "dst=127.0.0.1 "))
- continue;
- if (sscanf(line, "%*s %*d %s", ifname) || sscanf(line, "%s %*d", ifname))
- {
- if (!strcmp(ifname, "tcp"))
- tcp++;
- else if (!strcmp(ifname, "udp"))
- udp++;
- else
- other++;
- }
- }
- update_cnstat(udp, tcp, other);
- fclose(info);
- }
- if ((info = fopen("/proc/loadavg", "r")) != NULL)
- {
- if (fscanf(info, LD_SCAN_PATTERN, &lf1, &lf5, &lf15))
- {
- update_ldstat((uint16_t)(lf1 * 100),
- (uint16_t)(lf5 * 100),
- (uint16_t)(lf15 * 100));
- }
- fclose(info);
- }
- sleep(STEP_TIME);
- }
- unlink(PID_PATH);
- if (iw)
- iw_close(iw);
- return 0;
- }
- static void check_daemon(void)
- {
- int pid;
- if ((pid = readpid()) < 0 || kill(pid, 0) < 0)
- {
- /* daemon ping failed, try to start it up */
- if (run_daemon())
- {
- fprintf(stderr,
- "Failed to ping daemon and unable to start it up: %s\n",
- strerror(errno));
- exit(1);
- }
- }
- else if (kill(pid, SIGUSR1))
- {
- fprintf(stderr, "Failed to send signal: %s\n", strerror(errno));
- exit(2);
- }
- }
- static int run_dump_ifname(const char *ifname)
- {
- int i;
- char path[1024];
- struct file_map m;
- struct traffic_entry *e;
- check_daemon();
- snprintf(path, sizeof(path), DB_IF_FILE, ifname);
- if (mmap_file(path, sizeof(struct traffic_entry), &m))
- {
- fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
- return 1;
- }
- for (i = 0; i < m.size; i += sizeof(struct traffic_entry))
- {
- e = (struct traffic_entry *) &m.mmap[i];
- if (!e->time)
- continue;
- printf("[ %u, %u, %" PRIu32
- ", %u, %u ]%s\n",
- ntohl(e->time),
- ntohl(e->rxb), ntohl(e->rxp),
- ntohl(e->txb), ntohl(e->txp),
- ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
- }
- umap_file(&m);
- return 0;
- }
- static int run_dump_radio(const char *ifname)
- {
- int i;
- char path[1024];
- struct file_map m;
- struct radio_entry *e;
- check_daemon();
- snprintf(path, sizeof(path), DB_RD_FILE, ifname);
- if (mmap_file(path, sizeof(struct radio_entry), &m))
- {
- fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
- return 1;
- }
- for (i = 0; i < m.size; i += sizeof(struct radio_entry))
- {
- e = (struct radio_entry *) &m.mmap[i];
- if (!e->time)
- continue;
- printf("[ %u, %d, %d, %d ]%s\n",
- ntohl(e->time),
- e->rate, e->rssi, e->noise,
- ((i + sizeof(struct radio_entry)) < m.size) ? "," : "");
- }
- umap_file(&m);
- return 0;
- }
- static int run_dump_conns(void)
- {
- int i;
- char path[1024];
- struct file_map m;
- struct conn_entry *e;
- check_daemon();
- snprintf(path, sizeof(path), DB_CN_FILE);
- if (mmap_file(path, sizeof(struct conn_entry), &m))
- {
- fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
- return 1;
- }
- for (i = 0; i < m.size; i += sizeof(struct conn_entry))
- {
- e = (struct conn_entry *) &m.mmap[i];
- if (!e->time)
- continue;
- printf("[ %u, %u, %u, %u ]%s\n",
- ntohl(e->time), ntohl(e->udp),
- ntohl(e->tcp), ntohl(e->other),
- ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
- }
- umap_file(&m);
- return 0;
- }
- static int run_dump_load(void)
- {
- int i;
- char path[1024];
- struct file_map m;
- struct load_entry *e;
- check_daemon();
- snprintf(path, sizeof(path), DB_LD_FILE);
- if (mmap_file(path, sizeof(struct load_entry), &m))
- {
- fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
- return 1;
- }
- for (i = 0; i < m.size; i += sizeof(struct load_entry))
- {
- e = (struct load_entry *) &m.mmap[i];
- if (!e->time)
- continue;
- printf("[ %u, %u, %u, %u ]%s\n",
- ntohl(e->time),
- ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
- ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
- }
- umap_file(&m);
- return 0;
- }
- int main(int argc, char *argv[])
- {
- int opt;
- progname = argv[0];
- prognamelen = -1;
- for (opt = 0; opt < argc; opt++)
- prognamelen += 1 + strlen(argv[opt]);
- while ((opt = getopt(argc, argv, "t:i:r:cl")) > -1)
- {
- switch (opt)
- {
- case 't':
- timeout = atoi(optarg);
- break;
- case 'i':
- if (optarg)
- return run_dump_ifname(optarg);
- break;
- case 'r':
- if (optarg)
- return run_dump_radio(optarg);
- break;
- case 'c':
- return run_dump_conns();
- case 'l':
- return run_dump_load();
- default:
- break;
- }
- }
- fprintf(stderr,
- "Usage:\n"
- " %s [-t timeout] -i ifname\n"
- " %s [-t timeout] -r radiodev\n"
- " %s [-t timeout] -c\n"
- " %s [-t timeout] -l\n",
- argv[0], argv[0], argv[0], argv[0]
- );
- return 1;
- }
|