modules/admin-full: rework luci-bwc/libiwinfo integration
[project/luci.git] / modules / admin-full / src / luci-bwc.c
index 4067c57..05e4667 100644 (file)
 #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 \
-       " %[^ :]:%" SCNu64 " %" SCNu64 \
+       " %[^ :]:%u %u" \
        " %*d %*d %*d %*d %*d %*d" \
-       " %" SCNu64 " %" SCNu64
+       " %u %u"
 
 #define LD_SCAN_PATTERN \
        "%f %f %f"
@@ -55,40 +62,86 @@ struct file_map {
 };
 
 struct traffic_entry {
-       uint64_t time;
-       uint64_t rxb;
-       uint64_t rxp;
-       uint64_t txb;
-       uint64_t txp;
+       uint32_t time;
+       uint32_t rxb;
+       uint32_t rxp;
+       uint32_t txb;
+       uint32_t txp;
 };
 
 struct conn_entry {
-       uint64_t time;
+       uint32_t time;
        uint32_t udp;
        uint32_t tcp;
        uint32_t other;
 };
 
 struct load_entry {
-       uint64_t time;
+       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);
+       }
 
-static uint64_t htonll(uint64_t value)
+       return pid;
+}
+
+static int writepid(void)
 {
-       int num = 1;
+       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;
+       }
 
-       if (*(char *)&num == 1)
-               return htonl((uint32_t)(value & 0xFFFFFFFF)) |
-                      htonl((uint32_t)(value >> 32));
+       return -1;
+}
+
+static int timeout = TIMEOUT;
+static int countdown = -1;
+
+static void reset_countdown(int sig)
+{
+       countdown = timeout;
 
-       return value;
 }
 
-#define ntohll htonll
+
+static char *progname;
+static int prognamelen;
+
+static struct iwinfo_ops *backend = NULL;
 
 
 static int init_directory(char *path)
@@ -135,6 +188,11 @@ static int init_file(char *path, int esize)
        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;
@@ -148,8 +206,11 @@ static int update_file(const char *path, void *entry, int esize)
 
                if ((map != NULL) && (map != MAP_FAILED))
                {
-                       memmove(map, map + esize, esize * (STEP_COUNT-1));
-                       memcpy(map + esize * (STEP_COUNT-1), entry, esize);
+                       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);
 
@@ -190,9 +251,52 @@ static void umap_file(struct file_map *m)
                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, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
+       const char *ifname, uint32_t rxb, uint32_t rxp, uint32_t txb, uint32_t txp
 ) {
        char path[1024];
 
@@ -212,15 +316,44 @@ static int update_ifstat(
                }
        }
 
-       e.time = htonll(time(NULL));
-       e.rxb  = htonll(rxb);
-       e.rxp  = htonll(rxp);
-       e.txb  = htonll(txb);
-       e.txp  = htonll(txp);
+       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];
@@ -241,7 +374,7 @@ static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
                }
        }
 
-       e.time  = htonll(time(NULL));
+       e.time  = htonl(time(NULL));
        e.udp   = htonl(udp);
        e.tcp   = htonl(tcp);
        e.other = htonl(other);
@@ -269,7 +402,7 @@ static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
                }
        }
 
-       e.time   = htonll(time(NULL));
+       e.time   = htonl(time(NULL));
        e.load1  = htons(load1);
        e.load5  = htons(load5);
        e.load15 = htons(load15);
@@ -277,48 +410,69 @@ static int update_ldstat(uint16_t load1, uint16_t load5, uint16_t load15)
        return update_file(path, &e, sizeof(struct load_entry));
 }
 
-static int run_daemon(int nofork)
+static int run_daemon(void)
 {
        FILE *info;
-       uint64_t rxb, txb, rxp, txp;
+       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";
 
-       if (!nofork)
+       switch (fork())
        {
-               switch (fork())
-               {
-                       case -1:
-                               perror("fork()");
-                               return -1;
+               case -1:
+                       perror("fork()");
+                       return -1;
 
-                       case 0:
-                               if (chdir("/") < 0)
-                               {
-                                       perror("chdir()");
-                                       exit(1);
-                               }
+               case 0:
+                       if (chdir("/") < 0)
+                       {
+                               perror("chdir()");
+                               exit(1);
+                       }
 
-                               close(0);
-                               close(1);
-                               close(2);
-                               break;
+                       close(0);
+                       close(1);
+                       close(2);
+                       break;
 
-                       default:
-                               exit(0);
-               }
+               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 */
-       while (1)
+       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))
@@ -336,6 +490,26 @@ static int run_daemon(int nofork)
                        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;
@@ -347,6 +521,10 @@ static int run_daemon(int nofork)
                                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"))
@@ -377,6 +555,36 @@ static int run_daemon(int nofork)
 
                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)
@@ -386,6 +594,7 @@ static int run_dump_ifname(const char *ifname)
        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))
@@ -401,11 +610,11 @@ static int run_dump_ifname(const char *ifname)
                if (!e->time)
                        continue;
 
-               printf("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
-                          ", %" PRIu64 ", %" PRIu64 " ]%s\n",
-                       ntohll(e->time),
-                       ntohll(e->rxb), ntohll(e->rxp),
-                       ntohll(e->txb), ntohll(e->txp),
+               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) ? "," : "");
        }
 
@@ -414,6 +623,40 @@ static int run_dump_ifname(const char *ifname)
        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;
@@ -421,6 +664,7 @@ static int run_dump_conns(void)
        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))
@@ -436,8 +680,8 @@ static int run_dump_conns(void)
                if (!e->time)
                        continue;
 
-               printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
-                       ntohll(e->time), ntohl(e->udp),
+               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) ? "," : "");
        }
@@ -454,6 +698,7 @@ static int run_dump_load(void)
        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))
@@ -469,8 +714,8 @@ static int run_dump_load(void)
                if (!e->time)
                        continue;
 
-               printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
-                       ntohll(e->time),
+               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) ? "," : "");
        }
@@ -484,19 +729,19 @@ static int run_dump_load(void)
 int main(int argc, char *argv[])
 {
        int opt;
-       int daemon = 0;
-       int nofork = 0;
 
-       while ((opt = getopt(argc, argv, "dfi:cl")) > -1)
+       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 'd':
-                               daemon = 1;
-                               break;
-
-                       case 'f':
-                               nofork = 1;
+                       case 't':
+                               timeout = atoi(optarg);
                                break;
 
                        case 'i':
@@ -504,6 +749,11 @@ int main(int argc, char *argv[])
                                        return run_dump_ifname(optarg);
                                break;
 
+                       case 'r':
+                               if (optarg)
+                                       return run_dump_radio(optarg);
+                               break;
+
                        case 'c':
                                return run_dump_conns();
 
@@ -515,18 +765,14 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (daemon)
-               return run_daemon(nofork);
-
-       else
-               fprintf(stderr,
-                       "Usage:\n"
-                       "       %s -d [-f]\n"
-                       "       %s -i ifname\n"
-                       "       %s -c\n"
-                       "       %s -l\n",
-                               argv[0], argv[0], argv[0], argv[0]
-               );
+       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;
 }