themes/base: fix error r7144, location.host already includes the port number and...
[project/luci.git] / modules / admin-full / src / luci-bwc.c
index 6b6c0c7..4067c57 100644 (file)
 #define STEP_TIME      1
 
 #define DB_PATH                "/var/lib/luci-bwc"
-#define DB_FILE                DB_PATH "/%s"
+#define DB_IF_FILE     DB_PATH "/if/%s"
+#define DB_CN_FILE     DB_PATH "/connections"
+#define DB_LD_FILE     DB_PATH "/load"
 
-#define SCAN_PATTERN \
+#define IF_SCAN_PATTERN \
        " %[^ :]:%" SCNu64 " %" SCNu64 \
        " %*d %*d %*d %*d %*d %*d" \
        " %" SCNu64 " %" SCNu64
 
+#define LD_SCAN_PATTERN \
+       "%f %f %f"
+
+
+struct file_map {
+       int fd;
+       int size;
+       char *mmap;
+};
 
 struct traffic_entry {
        uint64_t time;
@@ -51,6 +62,21 @@ struct traffic_entry {
        uint64_t txp;
 };
 
+struct conn_entry {
+       uint64_t time;
+       uint32_t udp;
+       uint32_t tcp;
+       uint32_t other;
+};
+
+struct load_entry {
+       uint64_t time;
+       uint16_t load1;
+       uint16_t load5;
+       uint16_t load15;
+};
+
+
 static uint64_t htonll(uint64_t value)
 {
        int num = 1;
@@ -65,14 +91,9 @@ static uint64_t htonll(uint64_t value)
 #define ntohll htonll
 
 
-static int init_minutely(const char *ifname)
+static int init_directory(char *path)
 {
-       int i, file;
-       char path[1024];
-       char *p;
-       struct traffic_entry e = { 0 };
-
-       snprintf(path, sizeof(path), DB_FILE, ifname);
+       char *p = path;
 
        for (p = &path[1]; *p; p++)
        {
@@ -87,11 +108,22 @@ static int init_minutely(const char *ifname)
                }
        }
 
+       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, &e, sizeof(struct traffic_entry)) < 0)
+                       if (write(file, buf, esize) < 0)
                                break;
                }
 
@@ -103,68 +135,160 @@ static int init_minutely(const char *ifname)
        return -1;
 }
 
-static int update_minutely(
-       const char *ifname, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
-) {
+static int update_file(const char *path, void *entry, int esize)
+{
        int rv = -1;
-
        int file;
-       int entrysize = sizeof(struct traffic_entry);
-       int mapsize = STEP_COUNT * entrysize;
+       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))
+               {
+                       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 int update_ifstat(
+       const char *ifname, uint64_t rxb, uint64_t rxp, uint64_t txb, uint64_t txp
+) {
        char path[1024];
-       char *map;
 
        struct stat s;
        struct traffic_entry e;
 
-       snprintf(path, sizeof(path), DB_FILE, ifname);
+       snprintf(path, sizeof(path), DB_IF_FILE, ifname);
 
        if (stat(path, &s))
        {
-               if (init_minutely(ifname))
+               if (init_file(path, sizeof(struct traffic_entry)))
                {
                        fprintf(stderr, "Failed to init %s: %s\n",
                                        path, strerror(errno));
 
-                       return rv;
+                       return -1;
                }
        }
 
-       if ((file = open(path, O_RDWR)) >= 0)
-       {
-               map = mmap(NULL, mapsize, PROT_READ | PROT_WRITE,
-                                  MAP_SHARED | MAP_LOCKED, file, 0);
+       e.time = htonll(time(NULL));
+       e.rxb  = htonll(rxb);
+       e.rxp  = htonll(rxp);
+       e.txb  = htonll(txb);
+       e.txp  = htonll(txp);
 
-               if ((map != NULL) && (map != MAP_FAILED))
-               {
-                       e.time = htonll(time(NULL));
-                       e.rxb  = htonll(rxb);
-                       e.rxp  = htonll(rxp);
-                       e.txb  = htonll(txb);
-                       e.txp  = htonll(txp);
+       return update_file(path, &e, sizeof(struct traffic_entry));
+}
+
+static int update_cnstat(uint32_t udp, uint32_t tcp, uint32_t other)
+{
+       char path[1024];
 
-                       memmove(map, map + entrysize, mapsize - entrysize);
-                       memcpy(map + mapsize - entrysize, &e, entrysize);
+       struct stat s;
+       struct conn_entry e;
 
-                       munmap(map, mapsize);
+       snprintf(path, sizeof(path), DB_CN_FILE);
 
-                       rv = 0;
+       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;
                }
+       }
 
-               close(file);
+       e.time  = htonll(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;
+               }
        }
 
-       return rv;
+       e.time   = htonll(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(int nofork)
 {
        FILE *info;
        uint64_t rxb, txb, rxp, txp;
+       uint32_t udp, tcp, other;
+       float lf1, lf5, lf15;
        char line[1024];
        char ifname[16];
 
+       struct stat s;
+       const char *ipc = stat("/proc/net/nf_conntrack", &s)
+               ? "/proc/net/ip_conntrack" : "/proc/net/nf_conntrack";
 
        if (!nofork)
        {
@@ -202,68 +326,158 @@ static int run_daemon(int nofork)
                                if (strchr(line, '|'))
                                        continue;
 
-                               if (sscanf(line, SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
+                               if (sscanf(line, IF_SCAN_PATTERN, ifname, &rxb, &rxp, &txb, &txp))
                                {
                                        if (strncmp(ifname, "lo", sizeof(ifname)))
-                                               update_minutely(ifname, rxb, rxp, txb, txp);
+                                               update_ifstat(ifname, rxb, rxp, txb, txp);
                                }
                        }
 
                        fclose(info);
                }
 
+               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 (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);
        }
 }
 
-static int run_dump(const char *ifname)
+static int run_dump_ifname(const char *ifname)
 {
-       int rv = 1;
+       int i;
+       char path[1024];
+       struct file_map m;
+       struct traffic_entry *e;
 
-       int i, file;
-       int entrysize = sizeof(struct traffic_entry);
-       int mapsize = STEP_COUNT * entrysize;
+       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("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
+                          ", %" PRIu64 ", %" PRIu64 " ]%s\n",
+                       ntohll(e->time),
+                       ntohll(e->rxb), ntohll(e->rxp),
+                       ntohll(e->txb), ntohll(e->txp),
+                       ((i + sizeof(struct traffic_entry)) < m.size) ? "," : "");
+       }
 
+       umap_file(&m);
+
+       return 0;
+}
+
+static int run_dump_conns(void)
+{
+       int i;
        char path[1024];
-       char *map;
+       struct file_map m;
+       struct conn_entry *e;
 
-       struct traffic_entry *e;
+       snprintf(path, sizeof(path), DB_CN_FILE);
 
-       snprintf(path, sizeof(path), DB_FILE, ifname);
+       if (mmap_file(path, sizeof(struct conn_entry), &m))
+       {
+               fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+               return 1;
+       }
 
-       if ((file = open(path, O_RDONLY)) >= 0)
+       for (i = 0; i < m.size; i += sizeof(struct conn_entry))
        {
-               map = mmap(NULL, mapsize, PROT_READ, MAP_SHARED | MAP_LOCKED, file, 0);
+               e = (struct conn_entry *) &m.mmap[i];
 
-               if ((map != NULL) && (map != MAP_FAILED))
-               {
-                       for (i = 0; i < mapsize; i += entrysize)
-                       {
-                               e = (struct traffic_entry *) &map[i];
+               if (!e->time)
+                       continue;
 
-                               if (!e->time)
-                                       continue;
+               printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
+                       ntohll(e->time), ntohl(e->udp),
+                       ntohl(e->tcp), ntohl(e->other),
+                       ((i + sizeof(struct conn_entry)) < m.size) ? "," : "");
+       }
 
-                               printf("[ %" PRIu64 ", %" PRIu64 ", %" PRIu64
-                                          ", %" PRIu64 ", %" PRIu64 " ]%s\n",
-                                       ntohll(e->time),
-                                       ntohll(e->rxb), ntohll(e->rxp),
-                                       ntohll(e->txb), ntohll(e->txp),
-                                       ((i + entrysize) < mapsize) ? "," : "");
-                       }
+       umap_file(&m);
 
-                       munmap(map, mapsize);
-                       rv = 0;
-               }
+       return 0;
+}
 
-               close(file);
-       }
-       else
+static int run_dump_load(void)
+{
+       int i;
+       char path[1024];
+       struct file_map m;
+       struct load_entry *e;
+
+       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;
        }
 
-       return rv;
+       for (i = 0; i < m.size; i += sizeof(struct load_entry))
+       {
+               e = (struct load_entry *) &m.mmap[i];
+
+               if (!e->time)
+                       continue;
+
+               printf("[ %" PRIu64 ", %u, %u, %u ]%s\n",
+                       ntohll(e->time),
+                       ntohs(e->load1), ntohs(e->load5), ntohs(e->load15),
+                       ((i + sizeof(struct load_entry)) < m.size) ? "," : "");
+       }
+
+       umap_file(&m);
+
+       return 0;
 }
 
 
@@ -272,10 +486,8 @@ int main(int argc, char *argv[])
        int opt;
        int daemon = 0;
        int nofork = 0;
-       int dprint = 0;
-       char *ifname = NULL;
 
-       while ((opt = getopt(argc, argv, "dfp:")) > -1)
+       while ((opt = getopt(argc, argv, "dfi:cl")) > -1)
        {
                switch (opt)
                {
@@ -287,11 +499,17 @@ int main(int argc, char *argv[])
                                nofork = 1;
                                break;
 
-                       case 'p':
-                               dprint = 1;
-                               ifname = optarg;
+                       case 'i':
+                               if (optarg)
+                                       return run_dump_ifname(optarg);
                                break;
 
+                       case 'c':
+                               return run_dump_conns();
+
+                       case 'l':
+                               return run_dump_load();
+
                        default:
                                break;
                }
@@ -300,15 +518,14 @@ int main(int argc, char *argv[])
        if (daemon)
                return run_daemon(nofork);
 
-       else if (dprint && ifname)
-               return run_dump(ifname);
-
        else
                fprintf(stderr,
                        "Usage:\n"
                        "       %s -d [-f]\n"
-                       "       %s -p ifname\n",
-                               argv[0], argv[0]
+                       "       %s -i ifname\n"
+                       "       %s -c\n"
+                       "       %s -l\n",
+                               argv[0], argv[0], argv[0], argv[0]
                );
 
        return 1;