From 256d8c83a390d7aca83980a11fe4c2f47d3671f0 Mon Sep 17 00:00:00 2001 From: Teemu Toivola Date: Sun, 8 Jan 2017 21:17:30 +0200 Subject: [PATCH] add full legacy database import for all found databases during first daemon startup --- README.md | 11 +- src/common.c | 8 +- src/common.h | 42 +----- src/daemon.c | 45 ++++-- src/daemon.h | 1 + src/dbaccess.c | 331 ++++++++++++++++++++++++++++--------------- src/dbaccess.h | 48 ++++++- src/dbsql.c | 3 +- tests/common_tests.c | 28 ++-- 9 files changed, 318 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index bb71982..380973a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ configurable durations. Yearly and 5 minute resolution statistics are also plann * only daemon is being compiled * alpha version with minimal working daemon implementation * many sanity checks are missing or disabled - * don't even try with a user that has write access to any vnStat 1.x database files + * some daemon related configuration options aren't active + * don't try with a user that has write access to any vnStat 1.x database files + * just to be on the safe side ##### Done @@ -20,14 +22,17 @@ configurable durations. Yearly and 5 minute resolution statistics are also plann * support for multiple interfaces * dynamic data buffering in daemon, SaveInterval is honored * 5 minute, hourly, daily, monthly, yearly and total traffic recorded to database - * legacy database files aren't being accessed + * legacy database files are read only during first startup for data import + * write support is no longer included in code + * full data import from vnStat 1.x database format including reconstructed yearly data + * legacy database is not kept in memory for each interface during daemon runtime ##### TODO + * `grep TODO src/* tests/*` * continue daemon refactoring * add missing sanity checks to daemon * rewrite disabled tests - * data import from vnStat 1.x database format * all outputs (text and image) * use of 5 minute resolution statistics * old data cleanup, everything gets currently stored forever diff --git a/src/common.c b/src/common.c index 4f49175..307b432 100644 --- a/src/common.c +++ b/src/common.c @@ -166,7 +166,7 @@ int dmonth(int month) } } -uint32_t mosecs(void) +uint32_t mosecs(time_t month, time_t updated) { struct tm d; #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) || defined(__linux__) @@ -175,7 +175,7 @@ uint32_t mosecs(void) int timezone = 0; #endif - if (localtime_r(&data.month[0].month, &d) == NULL) { + if (localtime_r(&month, &d) == NULL) { return 1; } @@ -186,8 +186,8 @@ uint32_t mosecs(void) d.tm_mday = cfg.monthrotate; d.tm_hour = d.tm_min = d.tm_sec = 0; - if ((data.lastupdated-data.month[0].month)>0) { - return data.lastupdated-mktime(&d)+timezone; + if ((updated-month)>0) { + return updated-mktime(&d)+timezone; } else { return 1; } diff --git a/src/common.h b/src/common.h index 081eb52..3016729 100644 --- a/src/common.h +++ b/src/common.h @@ -155,10 +155,6 @@ and most can be changed later from the config file. /* each try takes about a second */ #define LOCKTRYLIMIT 5 -/* database version */ -/* 1 = 1.0, 2 = 1.1-1.2, 3 = 1.3- */ -#define DBVERSION 3 - /* 1 = 2.0 */ #define SQLDBVERSION "1" @@ -248,41 +244,6 @@ typedef struct { time_t timestamp; } IFINFO; -typedef struct { - time_t date; - uint64_t rx, tx; -} HOUR; - -typedef struct { - time_t date; - uint64_t rx, tx; - int rxk, txk; - int used; -} DAY; - -typedef struct { - time_t month; - uint64_t rx, tx; - int rxk, txk; - int used; -} MONTH; - -/* db structure */ -typedef struct { - int version; - char interface[32]; - char nick[32]; - int active; - uint64_t totalrx, totaltx, currx, curtx; - int totalrxk, totaltxk; - time_t lastupdated, created; - DAY day[30]; - MONTH month[12]; - DAY top10[10]; - HOUR hour[24]; - uint64_t btime; -} DATA; - typedef struct ibwnode { char interface[32]; uint32_t limit; @@ -305,7 +266,7 @@ int printe(PrintType type); int logprint(PrintType type); int verifylogaccess(void); int dmonth(int month); -uint32_t mosecs(void); +uint32_t mosecs(time_t month, time_t updated); uint64_t countercalc(const uint64_t *a, const uint64_t *b); void addtraffic(uint64_t *destmb, int *destkb, const uint64_t srcmb, const int srckb); uint64_t mbkbtokb(uint64_t mb, uint64_t kb); @@ -315,7 +276,6 @@ void panicexit(const char *sourcefile, const int sourceline); char *getversion(void); /* global variables */ -DATA data; CFG cfg; IFINFO ifinfo; char errorstring[512]; diff --git a/src/daemon.c b/src/daemon.c index 6b1aeb7..3d153a6 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -236,9 +236,6 @@ void initdstate(DSTATE *s) void preparedatabases(DSTATE *s) { - /*DIR *dir; - struct dirent *di;*/ - s->dbcount = db_getinterfacecount(); if (s->dbcount > 0 && !s->alwaysadd) { @@ -250,15 +247,36 @@ void preparedatabases(DSTATE *s) printf("Zero database found, exiting.\n"); exit(EXIT_FAILURE); } + if (!spacecheck(s->dirname)) { printf("Error: Not enough free diskspace available, exiting.\n"); exit(EXIT_FAILURE); } + + if (importlegacydbs(s) && !s->alwaysadd) { + s->dbcount = 0; + return; + } + if (s->dbcount == 0) { printf("Zero database found, adding available interfaces...\n"); } - /* TODO: import legacy databases first if found when the sqldb doesn't contain any interfaces + if (!addinterfaces(s) && s->dbcount == 0) { + printf("Nothing to do, exiting.\n"); + exit(EXIT_FAILURE); + } + + /* set counter back to zero so that dbs will be cached later */ + s->dbcount = 0; +} + +int importlegacydbs(DSTATE *s) +{ + DIR *dir; + struct dirent *di; + int importcount = 0; + if ((dir=opendir(s->dirname))==NULL) { printf("Error: Unable to open database directory \"%s\": %s\n", s->dirname, strerror(errno)); printf("Make sure it exists and is at least read enabled for current user.\n"); @@ -269,18 +287,19 @@ void preparedatabases(DSTATE *s) s->dbcount = 0; while ((di=readdir(dir))) { if ((di->d_name[0]!='.') && (strcmp(di->d_name, DATABASEFILE)!=0)) { - s->dbcount++; + /* ignore already known interfaces */ + if (db_getinterfacecountbyname(di->d_name)) { + continue; + } + if (importlegacydb(di->d_name, s->dirname)) { + importcount++; + } } } - closedir(dir);*/ + closedir(dir); - if (!addinterfaces(s) && s->dbcount == 0) { - printf("Nothing to do, exiting.\n"); - exit(EXIT_FAILURE); - } - - /* set counter back to zero so that dbs will be cached later */ - s->dbcount = 0; + s->dbcount += importcount; + return importcount; } void setsignaltraps(void) diff --git a/src/daemon.h b/src/daemon.h index e3bd848..7d9e51b 100644 --- a/src/daemon.h +++ b/src/daemon.h @@ -18,6 +18,7 @@ void debugtimestamp(void); int addinterfaces(DSTATE *s); void initdstate(DSTATE *s); void preparedatabases(DSTATE *s); +int importlegacydbs(DSTATE *s); void setsignaltraps(void); void filldatabaselist(DSTATE *s); void adjustsaveinterval(DSTATE *s); diff --git a/src/dbaccess.c b/src/dbaccess.c index da72711..3c4a80d 100644 --- a/src/dbaccess.c +++ b/src/dbaccess.c @@ -1,8 +1,111 @@ #include "common.h" +#include "dbsql.h" #include "fs.h" #include "dbaccess.h" -int readdb(const char *iface, const char *dirname, const int force) +int importlegacydb(const char *iface, const char *dirname) +{ + DATA data; + + /* TODO: check consistency of this print with other output when debug isn't enabled */ + printf("Importing legacy database \"%s\"...\n", iface); + + if (db_getinterfacecountbyname(iface)) { + return 0; + } + + if (readdb(&data, iface, dirname, 0) != 0) { + return 0; + } + + if (!db_addinterface(iface)) { + return 0; + } + + if (!insertlegacydata(&data, iface)) { + return 0; + } + + return 1; +} + +int insertlegacydata(DATA *data, const char *iface) +{ + int i, year; + time_t yeartime; + struct tm *stm; + uint64_t rx, tx; + + /* TODO: return value checks */ + + db_begintransaction(); + + db_setactive(iface, data->active); + if (strcmp(iface, data->nick) != 0) { + db_setalias(iface, data->nick); + } + db_setcounters(iface, data->currx, data->curtx); + db_setcreation(iface, (uint64_t)data->created); + + for (i = 23; i >= 0; i--) { + if (data->hour[i].date > 0 && ( data->hour[i].rx > 0 || data->hour[i].tx > 0 )) { + db_insertdata("hour", iface, data->hour[i].rx*1024, data->hour[i].tx*1024, (uint64_t)data->hour[i].date); + } + } + for (i = 29; i >= 0; i--) { + if (data->day[i].used) { + db_insertdata("day", iface, data->day[i].rx*1024*1024+data->day[i].rxk*1024, data->day[i].tx*1024*1024+data->day[i].txk*1024, (uint64_t)data->day[i].date); + } + } + for (i = 11; i >= 0; i--) { + if (data->month[i].used) { + db_insertdata("month", iface, data->month[i].rx*1024*1024+data->month[i].rxk*1024, data->month[i].tx*1024*1024+data->month[i].txk*1024, (uint64_t)data->month[i].month); + } + } + for (i = 9; i >= 0; i--) { + if (data->top10[i].used) { + db_insertdata("top", iface, data->top10[i].rx*1024*1024+data->top10[i].rxk*1024, data->top10[i].tx*1024*1024+data->top10[i].txk*1024, (uint64_t)data->top10[i].date); + } + } + + /* construct yearly data from legacy monthly data */ + rx = 0; + tx = 0; + year = 0; + yeartime = 0; + for (i = 11; i >= 0; i--) { + if (!data->month[i].used) { + continue; + } + stm = localtime(&data->month[i].month); + /* sanity check for possible invalid data */ + if (stm->tm_year+1900 <= 1980 || stm->tm_year+1900 >= 2050) { + continue; + } + if (stm->tm_year+1900 != year) { + if (year != 0 && ( rx > 0 || tx > 0 )) { + db_insertdata("year", iface, rx, tx, (uint64_t)yeartime); + } + year = stm->tm_year + 1900; + yeartime = data->month[i].month; + rx = 0; + tx = 0; + } + rx += data->month[i].rx*1024*1024+data->month[i].rxk*1024; + tx += data->month[i].tx*1024*1024+data->month[i].txk*1024; + } + if (year != 0 && ( rx > 0 || tx > 0 )) { + db_insertdata("year", iface, rx, tx, (uint64_t)yeartime); + } + + db_settotal(iface, data->totalrx*1024*1024+data->totalrxk*1024, data->totaltx*1024*1024+data->totaltxk*1024); + + db_committransaction(); + + return 1; +} + +int readdb(DATA *data, const char *iface, const char *dirname, const int force) { FILE *db; char file[512], backup[512]; @@ -15,9 +118,9 @@ int readdb(const char *iface, const char *dirname, const int force) printe(PT_Error); /* create new database template */ - initdb(); - strncpy_nt(data.interface, iface, 32); - strncpy_nt(data.nick, data.interface, 32); + initdb(data); + strncpy_nt(data->interface, iface, 32); + strncpy_nt(data->nick, data->interface, 32); return 1; } @@ -27,29 +130,29 @@ int readdb(const char *iface, const char *dirname, const int force) return -1; } - if (fread(&data,sizeof(DATA),1,db)==0) { - data.version=-1; + if (fread(data,sizeof(DATA),1,db)==0) { + data->version=-1; if (debug) { printf("db: Database read failed for file \"%s\".\n", file); } } else { if (debug) { - printf("db: Database loaded for interface \"%s\"...\n", data.interface); + printf("db: Database loaded for interface \"%s\"...\n", data->interface); } } - if (data.version == DBVERSION) { - if (!validatedb() && !force) { - data.version=-1; + if (data->version == LEGACYDBVERSION) { + if (!validatedb(data) && !force) { + data->version=-1; if (debug) { - printf("db: Database for interface \"%s\" fails to validate, trying with backup\n", data.interface); + printf("db: Database for interface \"%s\" fails to validate, trying with backup\n", data->interface); } } } /* convert old database to new format if necessary */ - if (data.versionversionversion==-1) { /* close current db and try using backup if database conversion failed */ fclose(db); @@ -69,7 +172,7 @@ int readdb(const char *iface, const char *dirname, const int force) return -1; } - if (fread(&data,sizeof(DATA),1,db)==0) { + if (fread(data,sizeof(DATA),1,db)==0) { snprintf(errorstring, 512, "Database load failed even when using backup (%s). Aborting.", strerror(errno)); printe(PT_Error); fclose(db); @@ -81,21 +184,21 @@ int readdb(const char *iface, const char *dirname, const int force) } } else { if (debug) { - printf("db: Backup database loaded for interface \"%s\"...\n", data.interface); + printf("db: Backup database loaded for interface \"%s\"...\n", data->interface); } } - if (data.version == DBVERSION) { - if (!validatedb()) { - data.version=-1; + if (data->version == LEGACYDBVERSION) { + if (!validatedb(data)) { + data->version=-1; if (debug) { - printf("db: Backup database for interface \"%s\" fails to validate\n", data.interface); + printf("db: Backup database for interface \"%s\" fails to validate\n", data->interface); } } } - if (data.version!=DBVERSION) { - if (data.version==-1) { + if (data->version!=LEGACYDBVERSION) { + if (data->version==-1) { snprintf(errorstring, 512, "Unable to use database \"%s\" or backup database \"%s\".", file, backup); printe(PT_Error); fclose(db); @@ -111,8 +214,8 @@ int readdb(const char *iface, const char *dirname, const int force) printe(PT_Info); } - } else if (data.version>DBVERSION) { - snprintf(errorstring, 512, "Downgrading database \"%s\" (v%d) is not supported.", file, data.version); + } else if (data->version>LEGACYDBVERSION) { + snprintf(errorstring, 512, "Downgrading database \"%s\" (v%d) is not supported.", file, data->version); printe(PT_Error); fclose(db); @@ -125,25 +228,25 @@ int readdb(const char *iface, const char *dirname, const int force) fclose(db); - if (strcmp(data.interface,iface)) { - snprintf(errorstring, 512, "Warning:\nThe previous interface for this file was \"%s\".",data.interface); + if (strcmp(data->interface,iface)) { + snprintf(errorstring, 512, "Warning:\nThe previous interface for this file was \"%s\".",data->interface); printe(PT_Multiline); snprintf(errorstring, 512, "It has now been replaced with \"%s\".",iface); printe(PT_Multiline); snprintf(errorstring, 512, "You can ignore this message if you renamed the filename."); printe(PT_Multiline); - snprintf(errorstring, 512, "Interface name mismatch, renamed \"%s\" -> \"%s\"", data.interface, iface); + snprintf(errorstring, 512, "Interface name mismatch, renamed \"%s\" -> \"%s\"", data->interface, iface); printe(PT_ShortMultiline); - if (strcmp(data.interface, data.nick)==0) { - strncpy_nt(data.nick, iface, 32); + if (strcmp(data->interface, data->nick)==0) { + strncpy_nt(data->nick, iface, 32); } - strncpy_nt(data.interface, iface, 32); + strncpy_nt(data->interface, iface, 32); } return 0; } -void initdb(void) +void initdb(DATA *data) { int i; time_t current; @@ -153,56 +256,56 @@ void initdb(void) d=localtime(¤t); /* set default values for a new database */ - data.version=DBVERSION; - data.active=1; - data.totalrx=0; - data.totaltx=0; - data.currx=0; - data.curtx=0; - data.totalrxk=0; - data.totaltxk=0; - data.lastupdated=current; - data.created=current; + data->version=LEGACYDBVERSION; + data->active=1; + data->totalrx=0; + data->totaltx=0; + data->currx=0; + data->curtx=0; + data->totalrxk=0; + data->totaltxk=0; + data->lastupdated=current; + data->created=current; /* days */ for (i=0;i<=29;i++) { - data.day[i].rx=0; - data.day[i].tx=0; - data.day[i].rxk=0; - data.day[i].txk=0; - data.day[i].date=0; - data.day[i].used=0; + data->day[i].rx=0; + data->day[i].tx=0; + data->day[i].rxk=0; + data->day[i].txk=0; + data->day[i].date=0; + data->day[i].used=0; } /* months */ for (i=0;i<=11;i++) { - data.month[i].rx=0; - data.month[i].tx=0; - data.month[i].rxk=0; - data.month[i].txk=0; - data.month[i].month=0; - data.month[i].used=0; + data->month[i].rx=0; + data->month[i].tx=0; + data->month[i].rxk=0; + data->month[i].txk=0; + data->month[i].month=0; + data->month[i].used=0; } /* top10 */ for (i=0;i<=9;i++) { - data.top10[i].rx=0; - data.top10[i].tx=0; - data.top10[i].rxk=0; - data.top10[i].txk=0; - data.top10[i].date=0; - data.top10[i].used=0; + data->top10[i].rx=0; + data->top10[i].tx=0; + data->top10[i].rxk=0; + data->top10[i].txk=0; + data->top10[i].date=0; + data->top10[i].used=0; } /* hours */ for (i=0;i<=23;i++) { - data.hour[i].rx=0; - data.hour[i].tx=0; - data.hour[i].date=0; + data->hour[i].rx=0; + data->hour[i].tx=0; + data->hour[i].date=0; } - data.day[0].used=data.month[0].used=1; - data.day[0].date=current; + data->day[0].used=data->month[0].used=1; + data->day[0].date=current; /* calculate new date for current month if current day is less than the set monthrotate value so that new databases begin @@ -210,12 +313,12 @@ void initdb(void) if (d->tm_mday < cfg.monthrotate) { d->tm_mday=cfg.monthrotate; d->tm_mon--; - data.month[0].month=mktime(d); + data->month[0].month=mktime(d); } else { - data.month[0].month=current; + data->month[0].month=current; } - data.btime=MAX32; + data->btime=MAX32; } int lockdb(int fd, int dbwrite) @@ -302,7 +405,7 @@ int removedb(const char *iface, const char *dirname) return 1; } -int validatedb(void) +int validatedb(DATA *data) { int i, used; uint64_t rxsum, txsum; @@ -312,29 +415,29 @@ int validatedb(void) } /* enforce string termination */ - data.interface[sizeof(data.interface)-1] = '\0'; - data.nick[sizeof(data.nick)-1] = '\0'; + data->interface[sizeof(data->interface)-1] = '\0'; + data->nick[sizeof(data->nick)-1] = '\0'; - if (data.version>DBVERSION) { - snprintf(errorstring, 512, "%s: Invalid database version: %d", data.interface, data.version); + if (data->version>LEGACYDBVERSION) { + snprintf(errorstring, 512, "%s: Invalid database version: %d", data->interface, data->version); printe(PT_Error); return 0; } - if (data.active<0 || data.active>1) { - snprintf(errorstring, 512, "%s: Invalid database activity status: %d", data.interface, data.active); + if (data->active<0 || data->active>1) { + snprintf(errorstring, 512, "%s: Invalid database activity status: %d", data->interface, data->active); printe(PT_Error); return 0; } - if (!strlen(data.interface)) { - snprintf(errorstring, 512, "Invalid database interface string: %s", data.interface); + if (!strlen(data->interface)) { + snprintf(errorstring, 512, "Invalid database interface string: %s", data->interface); printe(PT_Error); return 0; } - if (!data.created || !data.lastupdated || !data.btime) { - snprintf(errorstring, 512, "%s: Invalid database timestamp.", data.interface); + if (!data->created || !data->lastupdated || !data->btime) { + snprintf(errorstring, 512, "%s: Invalid database timestamp.", data->interface); printe(PT_Error); return 0; } @@ -342,42 +445,42 @@ int validatedb(void) rxsum = txsum = 0; used = 1; for (i=0; i<30; i++) { - if (data.day[i].used<0 || data.day[i].used>1) { - snprintf(errorstring, 512, "%s: Invalid database daily use information: %d %d", data.interface, i, data.day[i].used); + if (data->day[i].used<0 || data->day[i].used>1) { + snprintf(errorstring, 512, "%s: Invalid database daily use information: %d %d", data->interface, i, data->day[i].used); printe(PT_Error); return 0; } - if (data.day[i].rxk<0 || data.day[i].txk<0) { - snprintf(errorstring, 512, "%s: Invalid database daily traffic: %d", data.interface, i); + if (data->day[i].rxk<0 || data->day[i].txk<0) { + snprintf(errorstring, 512, "%s: Invalid database daily traffic: %d", data->interface, i); printe(PT_Error); return 0; } - if (data.day[i].used && !used) { - snprintf(errorstring, 512, "%s: Invalid database daily use order: %d", data.interface, i); + if (data->day[i].used && !used) { + snprintf(errorstring, 512, "%s: Invalid database daily use order: %d", data->interface, i); printe(PT_Error); return 0; - } else if (!data.day[i].used) { + } else if (!data->day[i].used) { used = 0; } - if (data.day[i].used) { - rxsum += data.day[i].rx; - txsum += data.day[i].tx; + if (data->day[i].used) { + rxsum += data->day[i].rx; + txsum += data->day[i].tx; } } for (i=1; i<30; i++) { - if (!data.day[i].used) { + if (!data->day[i].used) { break; } - if (data.day[i-1].date < data.day[i].date) { - snprintf(errorstring, 512, "%s: Invalid database daily date order: %u (%d) < %u (%d)", data.interface, (unsigned int)data.day[i-1].date, i-1, (unsigned int)data.day[i].date, i); + if (data->day[i-1].date < data->day[i].date) { + snprintf(errorstring, 512, "%s: Invalid database daily date order: %u (%d) < %u (%d)", data->interface, (unsigned int)data->day[i-1].date, i-1, (unsigned int)data->day[i].date, i); printe(PT_Error); return 0; } } - if (data.totalrx < rxsum || data.totaltx < txsum) { - snprintf(errorstring, 512, "%s: Invalid database total traffic compared to daily usage.", data.interface); + if (data->totalrx < rxsum || data->totaltx < txsum) { + snprintf(errorstring, 512, "%s: Invalid database total traffic compared to daily usage.", data->interface); printe(PT_Error); return 0; } @@ -385,63 +488,63 @@ int validatedb(void) rxsum = txsum = 0; used = 1; for (i=0; i<12; i++) { - if (data.month[i].used<0 || data.month[i].used>1) { - snprintf(errorstring, 512, "%s: Invalid database monthly use information: %d %d", data.interface, i, data.month[i].used); + if (data->month[i].used<0 || data->month[i].used>1) { + snprintf(errorstring, 512, "%s: Invalid database monthly use information: %d %d", data->interface, i, data->month[i].used); printe(PT_Error); return 0; } - if (data.month[i].rxk<0 || data.month[i].txk<0) { - snprintf(errorstring, 512, "%s: Invalid database monthly traffic: %d", data.interface, i); + if (data->month[i].rxk<0 || data->month[i].txk<0) { + snprintf(errorstring, 512, "%s: Invalid database monthly traffic: %d", data->interface, i); printe(PT_Error); return 0; } - if (data.month[i].used && !used) { - snprintf(errorstring, 512, "%s: Invalid database monthly use order: %d", data.interface, i); + if (data->month[i].used && !used) { + snprintf(errorstring, 512, "%s: Invalid database monthly use order: %d", data->interface, i); printe(PT_Error); return 0; - } else if (!data.month[i].used) { + } else if (!data->month[i].used) { used = 0; } - if (data.month[i].used) { - rxsum += data.month[i].rx; - txsum += data.month[i].tx; + if (data->month[i].used) { + rxsum += data->month[i].rx; + txsum += data->month[i].tx; } } for (i=1; i<12; i++) { - if (!data.month[i].used) { + if (!data->month[i].used) { break; } - if (data.month[i-1].month < data.month[i].month) { - snprintf(errorstring, 512, "%s: Invalid database monthly date order: %u (%d) < %u (%d)", data.interface, (unsigned int)data.month[i-1].month, i-1, (unsigned int)data.month[i].month, i); + if (data->month[i-1].month < data->month[i].month) { + snprintf(errorstring, 512, "%s: Invalid database monthly date order: %u (%d) < %u (%d)", data->interface, (unsigned int)data->month[i-1].month, i-1, (unsigned int)data->month[i].month, i); printe(PT_Error); return 0; } } - if (data.totalrx < rxsum || data.totaltx < txsum) { - snprintf(errorstring, 512, "%s: Invalid database total traffic compared to monthly usage.", data.interface); + if (data->totalrx < rxsum || data->totaltx < txsum) { + snprintf(errorstring, 512, "%s: Invalid database total traffic compared to monthly usage.", data->interface); printe(PT_Error); return 0; } used = 1; for (i=0; i<10; i++) { - if (data.top10[i].used<0 || data.top10[i].used>1) { - snprintf(errorstring, 512, "%s: Invalid database top10 use information: %d %d", data.interface, i, data.top10[i].used); + if (data->top10[i].used<0 || data->top10[i].used>1) { + snprintf(errorstring, 512, "%s: Invalid database top10 use information: %d %d", data->interface, i, data->top10[i].used); printe(PT_Error); return 0; } - if (data.top10[i].rxk<0 || data.top10[i].txk<0) { - snprintf(errorstring, 512, "%s: Invalid database top10 traffic: %d", data.interface, i); + if (data->top10[i].rxk<0 || data->top10[i].txk<0) { + snprintf(errorstring, 512, "%s: Invalid database top10 traffic: %d", data->interface, i); printe(PT_Error); return 0; } - if (data.top10[i].used && !used) { - snprintf(errorstring, 512, "%s: Invalid database top10 use order: %d", data.interface, i); + if (data->top10[i].used && !used) { + snprintf(errorstring, 512, "%s: Invalid database top10 use order: %d", data->interface, i); printe(PT_Error); return 0; - } else if (!data.top10[i].used) { + } else if (!data->top10[i].used) { used = 0; } } diff --git a/src/dbaccess.h b/src/dbaccess.h index ce6c7d6..d698975 100644 --- a/src/dbaccess.h +++ b/src/dbaccess.h @@ -1,11 +1,53 @@ #ifndef DBACCESS_H #define DBACCESS_H -int readdb(const char *iface, const char *dirname, const int force); -void initdb(void); +/* legacy database version */ +/* import is supported on from version 3 */ +/* 1 = 1.0, 2 = 1.1-1.2, 3 = 1.3- */ +#define LEGACYDBVERSION 3 + +typedef struct { + time_t date; + uint64_t rx, tx; +} HOUR; + +typedef struct { + time_t date; + uint64_t rx, tx; + int rxk, txk; + int used; +} DAY; + +typedef struct { + time_t month; + uint64_t rx, tx; + int rxk, txk; + int used; +} MONTH; + +/* legacy database structure */ +typedef struct { + int version; + char interface[32]; + char nick[32]; + int active; + uint64_t totalrx, totaltx, currx, curtx; + int totalrxk, totaltxk; + time_t lastupdated, created; + DAY day[30]; + MONTH month[12]; + DAY top10[10]; + HOUR hour[24]; + uint64_t btime; +} DATA; + +int importlegacydb(const char *iface, const char *dirname); +int insertlegacydata(DATA *data, const char *iface); +int readdb(DATA *data, const char *iface, const char *dirname, const int force); +void initdb(DATA *data); int lockdb(int fd, int dbwrite); int checkdb(const char *iface, const char *dirname); int removedb(const char *iface, const char *dirname); -int validatedb(void); +int validatedb(DATA *data); #endif diff --git a/src/dbsql.c b/src/dbsql.c index 0eefd3a..a7d4041 100644 --- a/src/dbsql.c +++ b/src/dbsql.c @@ -507,7 +507,7 @@ int db_insertdata(const char *table, const char *iface, const uint64_t rx, const } snprintf(nowdate, 64, "datetime(%"PRIu64", 'unixepoch')", timestamp); - snprintf(datebuffer, 512, datadates[i], nowdate); + snprintf(datebuffer, 512, datadates[index], nowdate); sqlite3_snprintf(1024, sql, "insert or ignore into %s (interface, date, rx, tx) values (%"PRId64", %s, %"PRIu64", %"PRIu64");", table, (int64_t)ifaceid, datebuffer, rx, tx); return db_exec(sql); @@ -522,6 +522,7 @@ int db_removeoldentries(void) } /* TODO: read cleanup limits from configuration and actually use this function somewhere */ + /* running this about once every hour during cache flush would keep the fiveminute table from accumulating too much excess data */ sqlite3_snprintf(512, sql, "delete from fiveminute where date < datetime('now', '-48 hours', 'localtime');"); if (!db_exec(sql)) { diff --git a/tests/common_tests.c b/tests/common_tests.c index aeffb31..a9f7d39 100644 --- a/tests/common_tests.c +++ b/tests/common_tests.c @@ -54,25 +54,19 @@ END_TEST #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) || defined(__linux__) START_TEST(mosecs_return_values) { - initdb(); defaultcfg(); ck_assert_int_eq(cfg.monthrotate, 1); - ck_assert_int_eq(mosecs(), 1); - data.month[0].month = 172800; - data.lastupdated = 173000; - ck_assert_int_eq(mosecs(), 173000); + ck_assert_int_eq(mosecs(0, 0), 1); + ck_assert_int_eq(mosecs(172800, 173000), 173000); } END_TEST #else START_TEST(mosecs_return_values_without_timezone) { - initdb(); defaultcfg(); ck_assert_int_eq(cfg.monthrotate, 1); - ck_assert_int_eq(mosecs(), 1); - data.month[0].month = 172800; - data.lastupdated = 173000; - ck_assert_int_gt(mosecs(), 1); + ck_assert_int_eq(mosecs(0, 0), 1); + ck_assert_int_gt(mosecs(172800, 173000), 1); } END_TEST #endif @@ -89,13 +83,10 @@ START_TEST(mosecs_does_not_change_tz) tzset(); timezone_before_call = timezone; - initdb(); defaultcfg(); - data.month[0].month = 1; - data.lastupdated = 2; ck_assert_int_eq(cfg.monthrotate, 1); - ck_assert_int_ne(mosecs(), 0); - ck_assert_int_ne(mosecs(), 1); + ck_assert_int_ne(mosecs(1, 2), 0); + ck_assert_int_ne(mosecs(1, 2), 1); ck_assert_int_eq(timezone_before_call, timezone); } END_TEST @@ -108,14 +99,11 @@ START_TEST(mosecs_does_not_change_struct_tm_pointer_content) current = time(NULL); stm = localtime(¤t); - initdb(); defaultcfg(); - data.month[0].month = 1; - data.lastupdated = 2; ck_assert_int_eq(cfg.monthrotate, 1); ck_assert_int_eq(current, timelocal(stm)); - ck_assert_int_ne(mosecs(), 0); - ck_assert_int_ne(mosecs(), 1); + ck_assert_int_ne(mosecs(1, 2), 0); + ck_assert_int_ne(mosecs(1, 2), 1); ck_assert_int_eq(current, timelocal(stm)); } END_TEST -- 2.40.0