]> granicus.if.org Git - vnstat/commitdiff
automatic interface selection when the Interface configuration setting is left empty...
authorTeemu Toivola <git@humdi.net>
Sun, 16 Jun 2019 13:58:26 +0000 (16:58 +0300)
committerTeemu Toivola <git@humdi.net>
Sun, 16 Jun 2019 13:58:26 +0000 (16:58 +0300)
13 files changed:
CHANGES
cfg/vnstat.conf
man/vnstat.conf.5
src/cfgoutput.c
src/common.h
src/daemon.c
src/dbsql.c
src/dbsql.h
src/iflist.c
src/iflist.h
src/vnstat.c
src/vnstat.h
src/vnstati.c

diff --git a/CHANGES b/CHANGES
index 712291ea16f924efc9435e83b732104bb878dcf3..571dfc83e2e0ad02dd7baa805d4fd3e05b30b620 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,8 @@
      support StateDirectory (issue seen at least with systemd 232 in Debian 9)
    - Debian and Red Hat init.d example files had wrong path for the pid file
  - New
+   - Automatic interface selection when the Interface configuration setting
+     is left empty (new default)
    - Add configuration option DatabaseWriteAheadLogging to enable SQLite
      Write-Ahead Logging mode which may provide some disk i/o benefits,
      see https://www.sqlite.org/wal.html for more details and note that
@@ -23,7 +25,7 @@
      fail due to disk being full
 
 
-2.2 / 28-Apr-2018
+2.2 / 28-Apr-2019
 
  - Fixed
    - O_CLOEXEC undeclared error when compiling with glibc older than 2.12
index 224d71560a4ba63d66fa50bff23caa3616a80935..ec7777844cd801bd130ccbfc8bd4e1198debeabb 100644 (file)
@@ -1,8 +1,8 @@
 # vnStat 2.2 config file
 ##
 
-# default interface
-Interface "eth0"
+# default interface (leave empty for automatic selection)
+Interface ""
 
 # location of the database directory
 DatabaseDir "/var/lib/vnstat"
index 5f05985f5b88abf18ee1ec4f68ec5824bf45a8fe..83787f178d8b61051acbfb8cf5c929ea513c741f 100644 (file)
@@ -58,8 +58,14 @@ hourly graph output. 0 = none, 1 = '|', 2 = '][', 3 = '[ ]'.
 
 .TP
 .B Interface
-Default interface used when no other interface is specified on
-the command line. (vnstat and vnstati only)
+Default interface used when no other interface is specified on the command
+line. Leave empty for automatic selection. The automatic selection will
+prioritize the interface with most traffic for outputs doing database queries.
+
+Queries not using the database will first check if the database is available
+and select the interface with most traffic out those that are currently
+visible in the system. If no database can be read then the first available
+interface will be used. (vnstat and vnstati only)
 
 .TP
 .B "List5Mins, ListHours, ListDays, ListMonths, ListYears, ListTop"
index ad68225b731c8d0965538e84c957d1c4050a85ac..32606da6d105aa707b16d6efa0c64b64ebabcab4 100644 (file)
@@ -9,7 +9,7 @@ void printcfgfile(void)
        printf("# vnStat %s config file\n", getversion());
        printf("##\n\n");
 
-       printf("# default interface\n");
+       printf("# default interface (leave empty for automatic selection)\n");
        printf("Interface \"%s\"\n\n", cfg.iface);
 
        printf("# location of the database directory\n");
index 1948c43b98f133a0d3ec394a6cf8d28a9847fb77..5f56cea8c0b5d8a9a2f7e0e3216f944bd0861814 100644 (file)
@@ -124,7 +124,7 @@ and most can be changed later from the config file.
 
 /* default interface */
 #ifndef DEFIFACE
-#define DEFIFACE "eth0"
+#define DEFIFACE ""
 #endif
 
 /* default locale */
index e2042b65ae1a1fe05ad5bc369cf60e352c9bfd14..949c8b770ba544676dad9180f6797d201acc0a91 100644 (file)
@@ -252,6 +252,7 @@ void debugtimestamp(void)
 
 void initdstate(DSTATE *s)
 {
+       db = NULL;
        noexit = 1;                /* disable exits in functions */
        debug = 0;                 /* debug disabled by default */
        disableprints = 0; /* let prints be visible */
index 0e3e979d319c1a390f7f01dc60ceee2860555804..ae5280e57b21ef5b84af62623d48dacf0fd7f692 100644 (file)
@@ -30,6 +30,10 @@ int db_open(const int createifnotfound, const int readonly)
 #else
        struct stat filestat;
 
+       if (db != NULL) {
+               return 1;
+       }
+
        snprintf(dbfilename, 530, "%s/%s", cfg.dbdir, DATABASEFILE);
 
        /* create database if file doesn't exist */
@@ -245,6 +249,9 @@ int db_close(void)
        int rc;
        rc = sqlite3_close(db);
        if (rc == SQLITE_OK) {
+               db = NULL;
+               if (debug)
+                       printf("Database closed\n");
                return 1;
        } else {
                db_errcode = rc;
@@ -684,12 +691,22 @@ char *db_getinfo(const char *name)
 }
 
 int db_getiflist(iflist **ifl)
+{
+       return db_getiflist_sorted(ifl, 0);
+}
+
+int db_getiflist_sorted(iflist **ifl, const int orderbytraffic)
 {
        int rc;
        char *sql;
        sqlite3_stmt *sqlstmt;
 
-       sql = "select name from interface order by name desc";
+       /* last entry added to list is the first entry when the list is read */
+       if (!orderbytraffic) {
+               sql = "select name from interface order by name desc";
+       } else {
+               sql = "select name from interface order by rxtotal+txtotal asc";
+       }
 
        rc = sqlite3_prepare_v2(db, sql, -1, &sqlstmt, NULL);
        if (rc != SQLITE_OK) {
index 0c6df85223643d60cea2a581ca3e40ad7f71a37a..8b2da670b5bbeff506c4a7a44686f36e3401eff3 100644 (file)
@@ -51,6 +51,7 @@ int db_setalias(const char *iface, const char *alias);
 int db_setinfo(const char *name, const char *value, const int createifnotfound);
 char *db_getinfo(const char *name);
 int db_getiflist(iflist **ifl);
+int db_getiflist_sorted(iflist **ifl, const int orderbytraffic);
 char *db_get_date_generator(const int range, const short direct, const char *nowdate);
 int db_addtraffic(const char *iface, const uint64_t rx, const uint64_t tx);
 int db_addtraffic_dated(const char *iface, const uint64_t rx, const uint64_t tx, const uint64_t timestamp);
index 7bd6c7c1f74d3a4749285a782993492949d445cb..0b8307816ea74bd9a060e7028dd9da4684cfd204 100644 (file)
@@ -25,6 +25,19 @@ int iflistadd(iflist **ifl, const char *iface, const uint32_t bandwidth)
        return 1;
 }
 
+int iflistsearch(iflist **ifl, const char *iface)
+{
+       iflist *ifl_iterator = *ifl;
+
+       while (ifl_iterator != NULL) {
+               if (strcmp(iface, ifl_iterator->interface) == 0) {
+                       return 1;
+               }
+               ifl_iterator = ifl_iterator->next;
+       }
+       return 0;
+}
+
 void iflistfree(iflist **ifl)
 {
        iflist *ifl_prev;
index 1437bf465a6aaa78f3628e4ec5c12bca2af3f7c0..4de7b68a416bae1188df7605c53ca35a17a04cd8 100644 (file)
@@ -9,6 +9,7 @@ typedef struct iflist {
 } iflist;
 
 int iflistadd(iflist **ifl, const char *iface, const uint32_t bandwidth);
+int iflistsearch(iflist **ifl, const char *iface);
 void iflistfree(iflist **ifl);
 
 #endif
index 9e57de65cd3fd4d8376c20cd75855d88ee240d6c..6ea1cb8edcd460553c6677b847a859caa41fc7b6 100644 (file)
@@ -36,7 +36,6 @@ int main(int argc, char *argv[])
        int currentarg;
        DIR *dir = NULL;
        PARAMS p;
-       iflist *dbifl = NULL;
 
        initparams(&p);
 
@@ -94,9 +93,13 @@ int main(int argc, char *argv[])
                                        return 1;
                                }
                                strncpy_nt(p.interface, argv[currentarg + 1], 32);
-                               p.defaultiface = 0;
+                               if (strlen(p.interface)) {
+                                       p.defaultiface = 0;
+                               } else {
+                                       strncpy_nt(p.definterface, p.interface, 32);
+                               }
                                if (debug)
-                                       printf("Used interface: %s\n", p.interface);
+                                       printf("Used interface: \"%s\"\n", p.interface);
                                currentarg++;
                                continue;
                        } else {
@@ -111,7 +114,7 @@ int main(int argc, char *argv[])
                        if (currentarg + 1 < argc) {
                                strncpy_nt(p.alias, argv[currentarg + 1], 32);
                                if (debug)
-                                       printf("Used alias: %s\n", p.alias);
+                                       printf("Used alias: \"%s\"\n", p.alias);
                                p.setalias = 1;
                                currentarg++;
                                continue;
@@ -414,11 +417,11 @@ int main(int argc, char *argv[])
                                }
                                return 1;
                        }
-                       p.ifcount = db_getinterfacecount();
+                       p.dbifcount = db_getinterfacecount();
                        if (debug)
-                               printf("%" PRIu64 " interface(s) found\n", p.ifcount);
+                               printf("%" PRIu64 " interface(s) found\n", p.dbifcount);
 
-                       if (p.ifcount > 1) {
+                       if (p.dbifcount > 1) {
                                strncpy_nt(p.definterface, cfg.iface, 32);
                        }
                } else {
@@ -434,22 +437,8 @@ int main(int argc, char *argv[])
                }
        }
 
-       /* set used interface if none specified and make sure it's null terminated */
-       if (p.defaultiface) {
-               strncpy_nt(p.interface, p.definterface, 32);
-
-               /* ensure that some usable interface is default */
-               if (p.ifcount > 0 && !db_getinterfacecountbyname(p.interface)) {
-                       if (db_getiflist(&dbifl) > 0) {
-                               strncpy_nt(p.interface, dbifl->interface, 32);
-                               if (debug) {
-                                       printf("Using \"%s\" as interface, default \"%s\" not found in database.\n", p.interface, p.definterface);
-                               }
-                               iflistfree(&dbifl);
-                       }
-               }
-       }
-       p.interface[31] = '\0';
+       /* set used interface if none specified */
+       handleifselection(&p);
 
        /* parameter handlers */
        handleremoveinterface(&p);
@@ -462,7 +451,7 @@ int main(int argc, char *argv[])
        if (!p.query && !p.traffic && !p.livetraffic) {
 
                /* give more help if there's no database */
-               if (p.ifcount == 0) {
+               if (p.dbifcount == 0) {
                        getifliststring(&p.ifacelist, 1);
                        printf("No interfaces found in the database, nothing to do. Use --help for help.\n\n");
                        printf("Interfaces can be added to the database with the following command:\n");
@@ -488,6 +477,7 @@ int main(int argc, char *argv[])
 
 void initparams(PARAMS *p)
 {
+       db = NULL;
        noexit = 0;                /* allow functions to exit in case of error */
        debug = 0;                 /* debug disabled by default */
        disableprints = 0; /* let prints be visible */
@@ -495,7 +485,7 @@ void initparams(PARAMS *p)
        p->addiface = 0;
        p->query = 1;
        p->setalias = 0;
-       p->ifcount = 0;
+       p->dbifcount = 0;
        p->force = 0;
        p->traffic = 0;
        p->livetraffic = 0;
@@ -531,7 +521,11 @@ void showhelp(PARAMS *p)
 
        printf("      -tr, --traffic [time]        calculate traffic\n");
        printf("      -l,  --live [mode]           show transfer rate in real time\n");
-       printf("      -i,  --iface <interface>     select interface (default: %s)\n\n", p->definterface);
+       printf("      -i,  --iface <interface>     select interface");
+       if (strlen(p->definterface)) {
+               printf(" (default: %s)", p->definterface);
+       }
+       printf("\n\n");
 
        printf("Use \"--longhelp\" or \"man vnstat\" for complete list of options.\n");
 }
@@ -565,7 +559,11 @@ void showlonghelp(PARAMS *p)
 
        printf("Misc:\n");
 
-       printf("      -i,  --iface <interface>     select interface (default: %s)\n", p->definterface);
+       printf("      -i,  --iface <interface>     select interface");
+       if (strlen(p->definterface)) {
+               printf(" (default: %s)", p->definterface);
+       }
+       printf("\n");
        printf("      -?,  --help                  show short help\n");
        printf("      -D,  --debug                 show some additional debug information\n");
        printf("      -v,  --version               show version\n");
@@ -710,10 +708,9 @@ void handleshowdatabases(PARAMS *p)
        }
 
        /* show all interfaces if -i isn't specified */
-       if (p->ifcount == 0) {
-               /* printf("No database found.\n"); */
+       if (p->dbifcount == 0) {
                p->query = 0;
-       } else if ((cfg.qmode == 0 || cfg.qmode == 8 || cfg.qmode == 10) && (p->ifcount > 1)) {
+       } else if ((cfg.qmode == 0 || cfg.qmode == 8 || cfg.qmode == 10) && (p->dbifcount > 1)) {
 
                if (cfg.qmode == 0) {
                        if (cfg.ostyle != 0) {
@@ -828,3 +825,79 @@ void handletrafficmeters(PARAMS *p)
                livetrafficmeter(p->interface, p->livemode);
        }
 }
+
+void handleifselection(PARAMS *p)
+{
+       int ifcount = 0, dbifcount = 0, iffound = 0, dbopened = 0;
+       iflist *ifl = NULL;
+       iflist *dbifl = NULL, *dbifl_iterator = NULL;
+
+       if (!p->defaultiface) {
+               return;
+       }
+
+       if (strlen(p->definterface)) {
+               strncpy_nt(p->interface, p->definterface, 32);
+               return;
+       }
+
+       if (p->traffic || p->livetraffic) {
+               ifcount = getiflist(&ifl, 0);
+
+               /* try to open database for extra information */
+               if (db == NULL) {
+                       if (!db_open_ro()) {
+                               db = NULL;
+                       } else {
+                               dbopened = 1;
+                       }
+               }
+
+               if (db != NULL) {
+                       dbifcount = db_getiflist_sorted(&dbifl, 1);
+                       if (dbopened) {
+                               db_close();
+                       }
+               }
+
+               if (dbifcount > 0 && ifcount > 0) {
+                       dbifl_iterator = dbifl;
+                       while (dbifl_iterator != NULL) {
+                               if (iflistsearch(&ifl, dbifl_iterator->interface)) {
+                                       strncpy_nt(p->interface, dbifl_iterator->interface, 32);
+                                       iffound = 1;
+                                       if (debug)
+                                               printf("Automatically selected interface with db: \"%s\"\n", p->interface);
+                                       break;
+                               }
+                               dbifl_iterator = dbifl_iterator->next;
+                       }
+               }
+
+               if (!iffound) {
+                       if (ifcount > 0) {
+                               strncpy_nt(p->interface, ifl->interface, 32);
+                               if (debug)
+                                       printf("Automatically selected interface without db: \"%s\"\n", p->interface);
+                       } else {
+                               printf("Error: Unable to find any suitable interface.\n");
+                               iflistfree(&ifl);
+                               iflistfree(&dbifl);
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               iflistfree(&ifl);
+       } else {
+               if (db_getiflist_sorted(&dbifl, 1) <= 0) {
+                       printf("Error: Unable to discover suitable interface from database.\n");
+                       iflistfree(&dbifl);
+                       exit(EXIT_FAILURE);
+               }
+               strncpy_nt(p->interface, dbifl->interface, 32);
+               if (debug)
+                       printf("Automatically selected interface from db: \"%s\"\n", p->interface);
+       }
+
+       iflistfree(&dbifl);
+}
index 16b35b639ded9b8a73b79aa907a2bd89f0d9e230..d72bc391b464ec89d8fbfd808ff8c3956d606e5f 100644 (file)
@@ -5,7 +5,7 @@ typedef struct {
        int query, setalias;
        int addiface, force, traffic;
        int livetraffic, defaultiface, removeiface, livemode;
-       uint64_t ifcount;
+       uint64_t dbifcount;
        char interface[32], dirname[512], alias[32], filename[512];
        char definterface[32], cfgfile[512], *ifacelist, jsonmode, xmlmode;
        char databegin[18], dataend[18];
@@ -20,5 +20,6 @@ void handlesetalias(PARAMS *p);
 void handleshowdatabases(PARAMS *p);
 void showoneinterface(PARAMS *p, const char *interface);
 void handletrafficmeters(PARAMS *p);
+void handleifselection(PARAMS *p);
 
 #endif
index fe8335f6f3c31710ec0beb63fb75a357caa81b0b..68e5fce4f64c3541e578c7b8ecf70cb24b010301 100644 (file)
@@ -16,6 +16,7 @@ vnStat image output - Copyright (c) 2007-2019 Teemu Toivola <tst@iki.fi>
 */
 
 #include "common.h"
+#include "iflist.h"
 #include "dbsql.h"
 #include "image.h"
 #include "cfg.h"
@@ -329,9 +330,11 @@ int main(int argc, char *argv[])
 
 void initiparams(IPARAMS *p)
 {
+       db = NULL;
        noexit = 0;                /* allow functions to exit in case of error */
        debug = 0;                 /* debug disabled by default */
        disableprints = 0; /* let prints be visible */
+
        p->interface[0] = '\0';
        p->filename[0] = '\0';
        p->cfgfile[0] = '\0';
@@ -359,7 +362,11 @@ void showihelp(IPARAMS *p)
        printf("      -ru, --rateunit [mode]       swap configured rate unit\n");
        printf("      -o,  --output <file>         select output filename\n");
        printf("      -c,  --cache <minutes>       update output only when too old\n");
-       printf("      -i,  --iface <interface>     select interface (default: %s)\n", p->interface);
+       printf("      -i,  --iface <interface>     select interface");
+       if (strlen(p->interface)) {
+               printf(" (default: %s)", p->interface);
+       }
+       printf("\n");
        printf("      -b,  --begin <date>          set list begin date\n");
        printf("      -e,  --end <date>            set list end date\n");
        printf("      -?,  --help                  this help\n");
@@ -410,17 +417,30 @@ void handlecaching(IPARAMS *p, IMAGECONTENT *ic)
 
 void handledatabase(IPARAMS *p, IMAGECONTENT *ic)
 {
+       iflist *dbifl = NULL;
+
        if (!db_open_ro()) {
                printf("Error: Failed to open database \"%s/%s\" in read-only mode.\n", cfg.dbdir, DATABASEFILE);
                exit(EXIT_FAILURE);
        }
-       if (!db_getinterfacecountbyname(p->interface)) {
-               if (strchr(p->interface, '+') == NULL) {
-                       printf("Error: Interface \"%s\" not found in database.\n", p->interface);
-               } else {
-                       printf("Error: Not all requested interfaces found in database or given interfaces aren't unique.\n");
+       if (strlen(p->interface)) {
+               if (!db_getinterfacecountbyname(p->interface)) {
+                       if (strchr(p->interface, '+') == NULL) {
+                               printf("Error: Interface \"%s\" not found in database.\n", p->interface);
+                       } else {
+                               printf("Error: Not all requested interfaces found in database or given interfaces aren't unique.\n");
+                       }
+                       exit(EXIT_FAILURE);
                }
-               exit(EXIT_FAILURE);
+       } else {
+               if (db_getiflist_sorted(&dbifl, 1) <= 0) {
+                       printf("Error: Unable to discover suitable interface from database.\n");
+                       exit(EXIT_FAILURE);
+               }
+               strncpy_nt(p->interface, dbifl->interface, 32);
+               iflistfree(&dbifl);
+               if (debug)
+                       printf("Automatically selected interface: \"%s\"\n", p->interface);
        }
        if (!db_getinterfaceinfo(p->interface, &ic->interface)) {
                printf("Error: Failed to fetch interface \"%s\" info from database.\n", p->interface);