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
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
# 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"
.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"
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");
/* default interface */
#ifndef DEFIFACE
-#define DEFIFACE "eth0"
+#define DEFIFACE ""
#endif
/* default locale */
void initdstate(DSTATE *s)
{
+ db = NULL;
noexit = 1; /* disable exits in functions */
debug = 0; /* debug disabled by default */
disableprints = 0; /* let prints be visible */
#else
struct stat filestat;
+ if (db != NULL) {
+ return 1;
+ }
+
snprintf(dbfilename, 530, "%s/%s", cfg.dbdir, DATABASEFILE);
/* create database if file doesn't exist */
int rc;
rc = sqlite3_close(db);
if (rc == SQLITE_OK) {
+ db = NULL;
+ if (debug)
+ printf("Database closed\n");
return 1;
} else {
db_errcode = rc;
}
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) {
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);
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;
} iflist;
int iflistadd(iflist **ifl, const char *iface, const uint32_t bandwidth);
+int iflistsearch(iflist **ifl, const char *iface);
void iflistfree(iflist **ifl);
#endif
int currentarg;
DIR *dir = NULL;
PARAMS p;
- iflist *dbifl = NULL;
initparams(&p);
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 {
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;
}
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 {
}
}
- /* 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);
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");
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 */
p->addiface = 0;
p->query = 1;
p->setalias = 0;
- p->ifcount = 0;
+ p->dbifcount = 0;
p->force = 0;
p->traffic = 0;
p->livetraffic = 0;
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");
}
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");
}
/* 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) {
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);
+}
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];
void handleshowdatabases(PARAMS *p);
void showoneinterface(PARAMS *p, const char *interface);
void handletrafficmeters(PARAMS *p);
+void handleifselection(PARAMS *p);
#endif
*/
#include "common.h"
+#include "iflist.h"
#include "dbsql.h"
#include "image.h"
#include "cfg.h"
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';
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");
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);