From: Teemu Toivola Date: Sun, 16 Jun 2019 13:58:26 +0000 (+0300) Subject: automatic interface selection when the Interface configuration setting is left empty... X-Git-Tag: v2.3~17 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e69b982d8b4ddb636d78ffeef586ce4038ced466;p=vnstat automatic interface selection when the Interface configuration setting is left empty, eth0 is no longer the hardcoded default --- diff --git a/CHANGES b/CHANGES index 712291e..571dfc8 100644 --- 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 diff --git a/cfg/vnstat.conf b/cfg/vnstat.conf index 224d715..ec77778 100644 --- a/cfg/vnstat.conf +++ b/cfg/vnstat.conf @@ -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" diff --git a/man/vnstat.conf.5 b/man/vnstat.conf.5 index 5f05985..83787f1 100644 --- a/man/vnstat.conf.5 +++ b/man/vnstat.conf.5 @@ -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" diff --git a/src/cfgoutput.c b/src/cfgoutput.c index ad68225..32606da 100644 --- a/src/cfgoutput.c +++ b/src/cfgoutput.c @@ -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"); diff --git a/src/common.h b/src/common.h index 1948c43..5f56cea 100644 --- a/src/common.h +++ b/src/common.h @@ -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 */ diff --git a/src/daemon.c b/src/daemon.c index e2042b6..949c8b7 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -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 */ diff --git a/src/dbsql.c b/src/dbsql.c index 0e3e979..ae5280e 100644 --- a/src/dbsql.c +++ b/src/dbsql.c @@ -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) { diff --git a/src/dbsql.h b/src/dbsql.h index 0c6df85..8b2da67 100644 --- a/src/dbsql.h +++ b/src/dbsql.h @@ -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); diff --git a/src/iflist.c b/src/iflist.c index 7bd6c7c..0b83078 100644 --- a/src/iflist.c +++ b/src/iflist.c @@ -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; diff --git a/src/iflist.h b/src/iflist.h index 1437bf4..4de7b68 100644 --- a/src/iflist.h +++ b/src/iflist.h @@ -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 diff --git a/src/vnstat.c b/src/vnstat.c index 9e57de6..6ea1cb8 100644 --- a/src/vnstat.c +++ b/src/vnstat.c @@ -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 select interface (default: %s)\n\n", p->definterface); + printf(" -i, --iface 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 select interface (default: %s)\n", p->definterface); + printf(" -i, --iface 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); +} diff --git a/src/vnstat.h b/src/vnstat.h index 16b35b6..d72bc39 100644 --- a/src/vnstat.h +++ b/src/vnstat.h @@ -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 diff --git a/src/vnstati.c b/src/vnstati.c index fe8335f..68e5fce 100644 --- a/src/vnstati.c +++ b/src/vnstati.c @@ -16,6 +16,7 @@ vnStat image output - Copyright (c) 2007-2019 Teemu Toivola */ #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 select output filename\n"); printf(" -c, --cache update output only when too old\n"); - printf(" -i, --iface select interface (default: %s)\n", p->interface); + printf(" -i, --iface select interface"); + if (strlen(p->interface)) { + printf(" (default: %s)", p->interface); + } + printf("\n"); printf(" -b, --begin set list begin date\n"); printf(" -e, --end 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);