diff --git a/Makefile b/Makefile index 0774e8a92..e69895867 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,8 @@ DEBUG_FLAGS=-rdynamic -fno-omit-frame-pointer # -DSQLITE_DEFAULT_MEMSTATUS=0: This setting causes the sqlite3_status() interfaces that track memory usage to be disabled. This helps the sqlite3_malloc() routines run much faster, and since SQLite uses sqlite3_malloc() internally, this helps to make the entire library faster. # -DSQLITE_OMIT_DEPRECATED: Omitting deprecated interfaces and features will not help SQLite to run any faster. It will reduce the library footprint, however. And it is the right thing to do. # -DSQLITE_OMIT_PROGRESS_CALLBACK: The progress handler callback counter must be checked in the inner loop of the bytecode engine. By omitting this interface, a single conditional is removed from the inner loop of the bytecode engine, helping SQL statements to run slightly faster. -SQLITEFLAGS=-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_MEMORYDB +# -DSQLITE_DEFAULT_FOREIGN_KEYS=1: This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections. +SQLITEFLAGS=-DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_MEMORYDB -DSQLITE_DEFAULT_FOREIGN_KEYS=1 # -Wall: This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. This also enables some language-specific warnings described in C++ Dialect Options and Objective-C and Objective-C++ Dialect Options. # -Wextra: This enables some extra warning flags that are not enabled by -Wall. diff --git a/database.c b/database.c index b59b2ace2..7f2b91c09 100644 --- a/database.c +++ b/database.c @@ -25,7 +25,7 @@ static int db_get_FTL_property(const unsigned int ID); // defined in networktable.c extern bool unify_hwaddr(sqlite3 *db); -static bool check_database(int rc) +bool check_database(int rc) { // We will retry if the database is busy at the moment // However, we won't retry if any other error happened @@ -37,6 +37,7 @@ static bool check_database(int rc) rc != SQLITE_BUSY) { logg("check_database(%i): Disabling database connection due to error", rc); + dbclose(); database = false; } @@ -47,8 +48,10 @@ void dbclose(void) { int rc = sqlite3_close(db); // Report any error - if( rc ) + if( rc != SQLITE_OK ) + { logg("dbclose() - SQL error (%i): %s", rc, sqlite3_errmsg(db)); + } // Unlock mutex on the database pthread_mutex_unlock(&dblock); @@ -75,7 +78,7 @@ bool dbopen(void) { pthread_mutex_lock(&dblock); int rc = sqlite3_open_v2(FTLfiles.db, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("dbopen() - SQL error (%i): %s", rc, sqlite3_errmsg(db)); dbclose(); check_database(rc); @@ -100,7 +103,8 @@ bool dbquery(const char *format, ...) return false; } - if(config.debug & DEBUG_DATABASE) logg("dbquery: %s", query); + if(config.debug & DEBUG_DATABASE) + logg("dbquery: \"%s\"", query); int rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg); @@ -119,57 +123,48 @@ bool dbquery(const char *format, ...) static bool create_counter_table(void) { - bool ret; // Create FTL table in the database (holds properties like database version, etc.) - ret = dbquery("CREATE TABLE counters ( id INTEGER PRIMARY KEY NOT NULL, value INTEGER NOT NULL );"); - if(!ret){ dbclose(); return false; } + SQL_bool("CREATE TABLE counters ( id INTEGER PRIMARY KEY NOT NULL, value INTEGER NOT NULL );"); // ID 0 = total queries - ret = db_set_counter(DB_TOTALQUERIES, 0); - if(!ret){ dbclose(); return false; } + if(!db_set_counter(DB_TOTALQUERIES, 0)){ dbclose(); return false; } // ID 1 = total blocked queries - ret = db_set_counter(DB_BLOCKEDQUERIES, 0); - if(!ret){ dbclose(); return false; } + if(!db_set_counter(DB_BLOCKEDQUERIES, 0)){ dbclose(); return false; } // Time stamp of creation of the counters database - ret = db_set_FTL_property(DB_FIRSTCOUNTERTIMESTAMP, time(NULL)); - if(!ret){ dbclose(); return false; } + if(!db_set_FTL_property(DB_FIRSTCOUNTERTIMESTAMP, time(NULL))){ dbclose(); return false; } // Update database version to 2 - ret = db_set_FTL_property(DB_VERSION, 2); - if(!ret){ dbclose(); return false; } + if(!db_set_FTL_property(DB_VERSION, 2)){ dbclose(); return false; } return true; } static bool db_create(void) { - bool ret; int rc = sqlite3_open_v2(FTLfiles.db, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("db_create() - SQL error (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return false; } // Create Queries table in the database - ret = dbquery("CREATE TABLE queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain TEXT NOT NULL, client TEXT NOT NULL, forward TEXT );"); - if(!ret){ dbclose(); return false; } + SQL_bool("CREATE TABLE queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain TEXT NOT NULL, client TEXT NOT NULL, forward TEXT );"); + // Add an index on the timestamps (not a unique index!) - ret = dbquery("CREATE INDEX idx_queries_timestamps ON queries (timestamp);"); - if(!ret){ dbclose(); return false; } + SQL_bool("CREATE INDEX idx_queries_timestamps ON queries (timestamp);"); + // Create FTL table in the database (holds properties like database version, etc.) - ret = dbquery("CREATE TABLE ftl ( id INTEGER PRIMARY KEY NOT NULL, value BLOB NOT NULL );"); - if(!ret){ dbclose(); return false; } + SQL_bool("CREATE TABLE ftl ( id INTEGER PRIMARY KEY NOT NULL, value BLOB NOT NULL );"); // Set DB version 1 - ret = dbquery("INSERT INTO ftl (ID,VALUE) VALUES(%i,1);", DB_VERSION); - if(!ret){ dbclose(); return false; } + if(!dbquery("INSERT INTO ftl (ID,VALUE) VALUES(%i,1);", DB_VERSION)) + return false; // Most recent timestamp initialized to 00:00 1 Jan 1970 - ret = dbquery("INSERT INTO ftl (ID,VALUE) VALUES(%i,0);", DB_LASTTIMESTAMP); - if(!ret){ dbclose(); return false; } + if(!dbquery("INSERT INTO ftl (ID,VALUE) VALUES(%i,0);", DB_LASTTIMESTAMP)) + return false; // Create counter table // Will update DB version to 2 @@ -288,6 +283,21 @@ void db_init(void) dbversion = db_get_FTL_property(DB_VERSION); } + // Update to version 5 if lower + if(dbversion < 5) + { + // Update to version 5: Create network-addresses table + logg("Updating long-term database to version 5"); + if(!create_network_addresses_table()) + { + logg("Network-addresses table not initialized, database not available"); + database = false; + return; + } + // Get updated version + dbversion = db_get_FTL_property(DB_VERSION); + } + // Close database to prevent having it opened all time // we already closed the database when we returned earlier sqlite3_close(db); @@ -344,9 +354,8 @@ int db_query_int(const char* querystr) { sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("db_query_int(%s) - SQL error prepare (%i): %s", querystr, rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -366,7 +375,6 @@ int db_query_int(const char* querystr) else { logg("db_query_int(%s) - SQL error step (%i): %s", querystr, rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -382,9 +390,8 @@ static int number_of_queries_in_DB(void) // Count number of rows using the index timestamp is faster than select(*) int rc = sqlite3_prepare_v2(db, "SELECT COUNT(timestamp) FROM queries", -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("number_of_queries_in_DB() - SQL error prepare (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -392,7 +399,6 @@ static int number_of_queries_in_DB(void) rc = sqlite3_step(stmt); if( rc != SQLITE_ROW ){ logg("number_of_queries_in_DB() - SQL error step (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -409,9 +415,8 @@ static sqlite3_int64 last_ID_in_DB(void) sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db, "SELECT MAX(ID) FROM queries", -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("last_ID_in_DB() - SQL error prepare (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -419,7 +424,6 @@ static sqlite3_int64 last_ID_in_DB(void) rc = sqlite3_step(stmt); if( rc != SQLITE_ROW ){ logg("last_ID_in_DB() - SQL error step (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return DB_FAILED; } @@ -471,19 +475,12 @@ void save_to_DB(void) // Get last ID stored in the database sqlite3_int64 lastID = last_ID_in_DB(); - bool ret = dbquery("BEGIN TRANSACTION"); - if(!ret) - { - logg("save_to_DB() - unable to begin transaction (%i): %s", ret, sqlite3_errmsg(db)); - dbclose(); - return; - } + SQL_void("BEGIN TRANSACTION"); int rc = sqlite3_prepare_v2(db, "INSERT INTO queries VALUES (NULL,?,?,?,?,?,?)", -1, &stmt, NULL); - if( rc ) + if( rc != SQLITE_OK ) { - logg("save_to_DB() - error in preparing SQL statement (%i): %s", ret, sqlite3_errmsg(db)); - dbclose(); + logg("save_to_DB() - error in preparing SQL statement (%i): %s", rc, sqlite3_errmsg(db)); check_database(rc); return; } @@ -585,9 +582,9 @@ void save_to_DB(void) } // Finish prepared statement - ret = dbquery("END TRANSACTION"); + SQL_void("END TRANSACTION"); int ret2 = sqlite3_finalize(stmt); - if(!ret || ret2 != SQLITE_OK){ dbclose(); return; } + if(ret2 != SQLITE_OK){ dbclose(); return; } // Store index for next loop interation round and update last time stamp // in the database only if all queries have been saved successfully @@ -628,9 +625,7 @@ static void delete_old_queries_in_DB(void) if(!dbquery("DELETE FROM queries WHERE timestamp <= %i", timestamp)) { - dbclose(); logg("delete_old_queries_in_DB(): Deleting queries due to age of entries failed!"); - database = true; return; } @@ -683,9 +678,9 @@ void *DB_thread(void *val) DBdeleteoldqueries = false; } - // Parse ARP cache (fill network table) if enabled + // Parse neighbor cache (fill network table) if enabled if (config.parse_arp_cache) - parse_arp_cache(); + parse_neighbor_cache(); } sleepms(100); } @@ -724,9 +719,8 @@ void read_data_from_DB(void) // Prepare SQLite3 statement sqlite3_stmt* stmt = NULL; rc = sqlite3_prepare_v2(db, rstr, -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("read_data_from_DB() - SQL error prepare (%i): %s", rc, sqlite3_errmsg(db)); - dbclose(); check_database(rc); return; } @@ -910,3 +904,12 @@ void read_data_from_DB(void) dbclose(); free(rstr); } + +// Returns ID of the most recent successful INSERT. +long db_lastID(void) +{ + long id = sqlite3_last_insert_rowid(db); + if(config.debug & DEBUG_DATABASE) + logg("db_lastID(): %ld", id); + return id; +} diff --git a/networktable.c b/networktable.c index 2a35e1ca1..31c1711f1 100644 --- a/networktable.c +++ b/networktable.c @@ -11,7 +11,6 @@ #include "FTL.h" #include "shmem.h" #include "sqlite3.h" -#define ARPCACHE "/proc/net/arp" // Private prototypes static char* getMACVendor(const char* hwaddr); @@ -19,43 +18,104 @@ bool unify_hwaddr(sqlite3 *db); bool create_network_table(void) { - bool ret; // Create network table in the database - ret = dbquery("CREATE TABLE network ( id INTEGER PRIMARY KEY NOT NULL, " \ - "ip TEXT NOT NULL, " \ - "hwaddr TEXT NOT NULL, " \ - "interface TEXT NOT NULL, " \ - "name TEXT, " \ - "firstSeen INTEGER NOT NULL, " \ - "lastQuery INTEGER NOT NULL, " \ - "numQueries INTEGER NOT NULL," \ - "macVendor TEXT);"); - if(!ret){ dbclose(); return false; } + SQL_bool("CREATE TABLE network ( id INTEGER PRIMARY KEY NOT NULL, " \ + "ip TEXT NOT NULL, " \ + "hwaddr TEXT NOT NULL, " \ + "interface TEXT NOT NULL, " \ + "name TEXT, " \ + "firstSeen INTEGER NOT NULL, " \ + "lastQuery INTEGER NOT NULL, " \ + "numQueries INTEGER NOT NULL," \ + "macVendor TEXT);"); // Update database version to 3 - ret = db_set_FTL_property(DB_VERSION, 3); - if(!ret){ dbclose(); return false; } + if(!db_set_FTL_property(DB_VERSION, 3)) + { + logg("create_network_table(): Failed to update database version!"); + return false; + } return true; } -// Read kernel's ARP cache using procfs -void parse_arp_cache(void) +bool create_network_addresses_table(void) { - FILE* arpfp = NULL; - // Try to access the kernel's ARP cache - if((arpfp = fopen(ARPCACHE, "r")) == NULL) + // Disable foreign key enforcement for this transaction + // Otherwise, dropping the network table would not be allowed + SQL_bool("PRAGMA foreign_keys=OFF"); + + // Begin new transaction + SQL_bool("BEGIN TRANSACTION"); + + // Create network_addresses table in the database + SQL_bool("CREATE TABLE network_addresses ( network_id INTEGER NOT NULL, "\ + "ip TEXT NOT NULL, "\ + "lastSeen INTEGER NOT NULL DEFAULT (cast(strftime('%%s', 'now') as int)), "\ + "UNIQUE(network_id,ip), "\ + "FOREIGN KEY(network_id) REFERENCES network(id));"); + + // Create a network_addresses row for each entry in the network table + SQL_bool("INSERT INTO network_addresses (network_id,ip) SELECT id,ip FROM network;"); + + // Remove IP column from network table. + // As ALTER TABLE is severely limit, we have to do the column deletion manually. + // Step 1: We create a new table without the ip column + SQL_bool("CREATE TABLE network_bck ( id INTEGER PRIMARY KEY NOT NULL, " \ + "hwaddr TEXT UNIQUE NOT NULL, " \ + "interface TEXT NOT NULL, " \ + "name TEXT, " \ + "firstSeen INTEGER NOT NULL, " \ + "lastQuery INTEGER NOT NULL, " \ + "numQueries INTEGER NOT NULL, " \ + "macVendor TEXT);"); + + // Step 2: Copy data (except ip column) from network into network_back + SQL_bool("INSERT INTO network_bck "\ + "SELECT id, hwaddr, interface, name, firstSeen, "\ + "lastQuery, numQueries, macVendor "\ + "FROM network;"); + + // Step 3: Drop the network table, the unique index will be automatically dropped + SQL_bool("DROP TABLE network;"); + + // Step 4: Rename network_bck table to network table as last step + SQL_bool("ALTER TABLE network_bck RENAME TO network;"); + + // Update database version to 5 + if(!db_set_FTL_property(DB_VERSION, 5)) { - logg("WARN: Opening of %s failed!", ARPCACHE); - logg(" Message: %s", strerror(errno)); - return; + logg("create_network_addresses_table(): Failed to update database version!"); + return false; } + // Finish transaction + SQL_bool("COMMIT"); + + // Re-enable foreign key enforcement + SQL_bool("PRAGMA foreign_keys=ON"); + + return true; +} + +// Parse kernel's neighbor cache +void parse_neighbor_cache(void) +{ // Open database file if(!dbopen()) { - logg("read_arp_cache() - Failed to open DB"); - fclose(arpfp); + logg("parse_arp_cache() - Failed to open DB"); + return; + } + + // Try to access the kernel's neighbor cache + // We are only interested in entries which are in either STALE or REACHABLE state + FILE* arpfp = NULL; + if((arpfp = popen("ip neigh show nud stale nud reachable", "r")) == NULL) + { + logg("WARN: Command \"ip neigh show nud stale nud reachable\" failed!"); + logg(" Message: %s", strerror(errno)); + dbclose(); return; } @@ -65,26 +125,22 @@ void parse_arp_cache(void) // Prepare buffers char * linebuffer = NULL; size_t linebuffersize = 0; - char ip[100], mask[100], hwaddr[100], iface[100]; - unsigned int type, flags, entries = 0; + char ip[100], hwaddr[100], iface[100]; + unsigned int entries = 0; time_t now = time(NULL); // Start collecting database commands lock_shm(); - dbquery("BEGIN TRANSACTION"); + SQL_void("BEGIN TRANSACTION"); // Read ARP cache line by line while(getline(&linebuffer, &linebuffersize, arpfp) != -1) { - int num = sscanf(linebuffer, "%99s 0x%x 0x%x %99s %99s %99s\n", - ip, &type, &flags, hwaddr, mask, iface); + int num = sscanf(linebuffer, "%99s dev %99s lladdr %99s", + ip, iface, hwaddr); - // Skip header and empty lines - if (num < 4) - continue; - - // Skip incomplete entires, i.e., entries without C (complete) flag - if(!(flags & 0x02)) + // Check if we want to process the line we just read + if(num != 3) continue; // Get ID of this device in our network database. If it cannot be @@ -102,12 +158,12 @@ void parse_arp_cache(void) int ret = asprintf(&querystr, "SELECT id FROM network WHERE hwaddr = \'%s\';", hwaddr); if(querystr == NULL || ret < 0) { - logg("Memory allocation failed in parse_arp_cache (%i)", ret); + logg("Memory allocation failed in parse_arp_cache(): %i", ret); break; } // Perform SQL query - const int dbID = db_query_int(querystr); + int dbID = db_query_int(querystr); free(querystr); if(dbID == DB_FAILED) @@ -140,14 +196,17 @@ void parse_arp_cache(void) { char* macVendor = getMACVendor(hwaddr); dbquery("INSERT INTO network "\ - "(ip,hwaddr,interface,firstSeen,lastQuery,numQueries,name,macVendor) "\ - "VALUES (\'%s\',\'%s\',\'%s\',%lu, %ld, %u, \'%s\', \'%s\');",\ - ip, hwaddr, iface, now, + "(hwaddr,interface,firstSeen,lastQuery,numQueries,name,macVendor) "\ + "VALUES (\'%s\',\'%s\',%lu, %ld, %u, \'%s\', \'%s\');",\ + hwaddr, iface, now, client != NULL ? client->lastQuery : 0L, client != NULL ? client->numQueriesARP : 0u, hostname, macVendor); free(macVendor); + + // Obtain ID which was given to this new entry + dbID = db_lastID(); } // Device in database AND client known to Pi-hole else if(client != NULL) @@ -169,13 +228,6 @@ void parse_arp_cache(void) client->numQueriesARP, dbID); client->numQueriesARP = 0; - // Update IP address in case it changed. This might happen with - // sequential DHCP servers as found in many commercial routers - dbquery("UPDATE network "\ - "SET ip = \'%s\' "\ - "WHERE id = %i;",\ - ip, dbID); - // Store hostname if available if(strlen(hostname) > 0) { @@ -189,12 +241,20 @@ void parse_arp_cache(void) // else: // Device in database but not known to Pi-hole: No action required + // Add unique pair of ID (corresponds to one particular hardware + // address) and IP address if it does not exist (INSERT). In case + // this pair already exists, the UNIQUE(network_id,ip) trigger + // becomes active and the line is instead REPLACEd, causing the + // lastQuery timestamp to be updated + dbquery("INSERT OR REPLACE INTO network_addresses "\ + "(network_id,ip) VALUES(%i,\'%s\');", dbID, ip); + // Count number of processed ARP cache entries entries++; } // Actually update the database - dbquery("COMMIT"); + SQL_void("COMMIT"); unlock_shm(); // Debug logging @@ -202,7 +262,7 @@ void parse_arp_cache(void) logg("ARP table processing (%i entries) took %.1f ms", entries, timer_elapsed_msec(ARP_TIMER)); // Close file handle - fclose(arpfp); + pclose(arpfp); // Close database connection dbclose(); @@ -231,9 +291,9 @@ bool unify_hwaddr(sqlite3 *db) // Perform SQL query sqlite3_stmt* stmt; ret = sqlite3_prepare_v2(db, querystr, -1, &stmt, NULL); - if( ret ){ + if( ret != SQLITE_OK){ logg("unify_hwaddr(%s) - SQL error prepare (%i): %s", querystr, ret, sqlite3_errmsg(db)); - dbclose(); + check_database(ret); return false; } @@ -283,14 +343,11 @@ bool unify_hwaddr(sqlite3 *db) // See https://www.sqlite.org/lang_createtable.html#constraints: // >>> In most cases, UNIQUE and PRIMARY KEY constraints are // >>> implemented by creating a unique index in the database. - dbquery("CREATE UNIQUE INDEX network_hwaddr_idx ON network(hwaddr)"); + SQL_bool("CREATE UNIQUE INDEX network_hwaddr_idx ON network(hwaddr)"); // Update database version to 4 if(!db_set_FTL_property(DB_VERSION, 4)) - { - dbclose(); return false; - } return true; } @@ -315,7 +372,7 @@ static char* getMACVendor(const char* hwaddr) sqlite3 *macdb; int rc = sqlite3_open_v2(FTLfiles.macvendordb, &macdb, SQLITE_OPEN_READONLY, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("getMACVendor(%s) - SQL error (%i): %s", hwaddr, rc, sqlite3_errmsg(macdb)); sqlite3_close(macdb); return strdup(""); @@ -336,7 +393,7 @@ static char* getMACVendor(const char* hwaddr) sqlite3_stmt* stmt; rc = sqlite3_prepare_v2(macdb, querystr, -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("getMACVendor(%s) - SQL error prepare (%s, %i): %s", hwaddr, querystr, rc, sqlite3_errmsg(macdb)); sqlite3_close(macdb); return strdup(""); @@ -380,7 +437,7 @@ void updateMACVendorRecords() sqlite3 *db; int rc = sqlite3_open_v2(FTLfiles.db, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("updateMACVendorRecords() - SQL error (%i): %s", rc, sqlite3_errmsg(db)); sqlite3_close(db); return; @@ -389,7 +446,7 @@ void updateMACVendorRecords() sqlite3_stmt* stmt; const char* selectstr = "SELECT id,hwaddr FROM network;"; rc = sqlite3_prepare_v2(db, selectstr, -1, &stmt, NULL); - if( rc ){ + if( rc != SQLITE_OK ){ logg("updateMACVendorRecords() - SQL error prepare (%s, %i): %s", selectstr, rc, sqlite3_errmsg(db)); sqlite3_close(db); return; diff --git a/routines.h b/routines.h index cecf5e03b..9f85fe46a 100644 --- a/routines.h +++ b/routines.h @@ -86,6 +86,7 @@ void *GC_thread(void *val); // database.c void db_init(void); +bool check_database(int rc); void *DB_thread(void *val); int get_number_of_queries_in_DB(void); void save_to_DB(void); @@ -96,6 +97,7 @@ bool dbopen(void); void dbclose(void); int db_query_int(const char*); void SQLite3LogCallback(void *pArg, int iErrCode, const char *zMsg); +long db_lastID(void); // memory.c void memory_check(const int which); @@ -157,7 +159,8 @@ bool check_capabilities(void); // networktable.c bool create_network_table(void); -void parse_arp_cache(void); +bool create_network_addresses_table(void); +void parse_neighbor_cache(void); void updateMACVendorRecords(void); // gravity.c @@ -169,4 +172,19 @@ void gravityDB_finalizeTable(void); int gravityDB_count(unsigned char list); bool in_whitelist(const char *domain); +// Database macros +#define SQL_bool(sql) {\ + if(!dbquery(sql)) {\ + logg("%s(): \"%s\" failed!", __FUNCTION__, sql);\ + return false;\ + }\ +} + +#define SQL_void(sql) {\ + if(!dbquery(sql)) {\ + logg("%s(): \"%s\" failed!", __FUNCTION__, sql);\ + return;\ + }\ +} + #endif // ROUTINES_H diff --git a/test/test_suite.bats b/test/test_suite.bats index a5b62cbe8..efddc4491 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -252,9 +252,9 @@ [[ "${lines[@]}" == *"CREATE TABLE queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp INTEGER NOT NULL, type INTEGER NOT NULL, status INTEGER NOT NULL, domain TEXT NOT NULL, client TEXT NOT NULL, forward TEXT );"* ]] [[ "${lines[@]}" == *"CREATE TABLE ftl ( id INTEGER PRIMARY KEY NOT NULL, value BLOB NOT NULL );"* ]] [[ "${lines[@]}" == *"CREATE TABLE counters ( id INTEGER PRIMARY KEY NOT NULL, value INTEGER NOT NULL );"* ]] - [[ "${lines[@]}" == *"CREATE TABLE network ( id INTEGER PRIMARY KEY NOT NULL, ip TEXT NOT NULL, hwaddr TEXT NOT NULL, interface TEXT NOT NULL, name TEXT, firstSeen INTEGER NOT NULL, lastQuery INTEGER NOT NULL, numQueries INTEGER NOT NULL,macVendor TEXT);"* ]] + [[ "${lines[@]}" == *"CREATE TABLE IF NOT EXISTS \"network\" ( id INTEGER PRIMARY KEY NOT NULL, hwaddr TEXT UNIQUE NOT NULL, interface TEXT NOT NULL, name TEXT, firstSeen INTEGER NOT NULL, lastQuery INTEGER NOT NULL, numQueries INTEGER NOT NULL, macVendor TEXT);"* ]] + [[ "${lines[@]}" == *"CREATE TABLE network_addresses ( network_id INTEGER NOT NULL, ip TEXT NOT NULL, lastSeen INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)), UNIQUE(network_id,ip), FOREIGN KEY(network_id) REFERENCES network(id));"* ]] [[ "${lines[@]}" == *"CREATE INDEX idx_queries_timestamps ON queries (timestamp);"* ]] - [[ "${lines[@]}" == *"CREATE UNIQUE INDEX network_hwaddr_idx ON network(hwaddr);"* ]] } @test "Fail on invalid argument" {