/* Pi-hole: A black hole for Internet advertisements * (c) 2017 Pi-hole, LLC (https://pi-hole.net) * Network-wide ad blocking via your own hardware. * * FTL Engine * Database routines * * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ #include "FTL.h" sqlite3 *db; bool database = false; bool DBdeleteoldqueries = false; long int lastdbindex = 0; pthread_mutex_t dblock; enum { DB_VERSION, DB_LASTTIMESTAMP }; void dbclose(void) { sqlite3_close(db); pthread_mutex_unlock(&dblock); } float get_db_filesize(void) { struct stat st; if(stat(FTLfiles.db, &st) != 0) { // stat() failed (maybe the DB file does not exist?) return 0; } return 1e-6*st.st_size; } static int callback(void *NotUsed, int argc, char **argv, char **azColName){ int i; for(i=0; i 0) sqlite3_bind_text(stmt, 5, clients[queries[i].clientID].name, -1, SQLITE_TRANSIENT); else sqlite3_bind_text(stmt, 5, clients[queries[i].clientID].ip, -1, SQLITE_TRANSIENT); // FORWARD if(queries[i].forwardID > -1) { validate_access("forwarded", queries[i].forwardID, true, __LINE__, __FUNCTION__, __FILE__); sqlite3_bind_text(stmt, 6, forwarded[queries[i].forwardID].ip, -1, SQLITE_TRANSIENT); } else { sqlite3_bind_null(stmt, 6); } // Step and check if successful int rc = sqlite3_step(stmt); sqlite3_clear_bindings(stmt); sqlite3_reset(stmt); if( rc != SQLITE_DONE ){ logg("save_to_DB() - SQL error: %s", sqlite3_errmsg(db)); saved_error++; continue; } saved++; // Mark this query as saved in the database only if successful queries[i].db = true; // Update lasttimestamp variable with timestamp of the latest stored query if(queries[i].timestamp > lasttimestamp) newlasttimestamp = queries[i].timestamp; } // Finish prepared statement dbquery("END TRANSACTION"); sqlite3_finalize(stmt); // Store index for next loop interation round and update last time stamp // in the database only if all queries have been saved successfully if(saved_error == 0) { lastdbindex = i; db_set_FTL_property(DB_LASTTIMESTAMP, newlasttimestamp); } // Close database dbclose(); if(debug) { if(saved > 0) logg("Notice: Queries stored in DB: %u", saved); if(saved_error > 0) logg(" Queries NOT stored in DB: %u (due to an error)", saved_error); } } void delete_old_queries_in_DB(void) { // Open database if(!dbopen()) { logg("Failed to open DB in delete_old_queries_in_DB()"); return; } int timestamp = time(NULL) - config.maxDBdays * 86400; 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; } // Get how many rows have been affected (deleted) int affected = sqlite3_changes(db); // Print final message only if there is a difference if(debug || affected) logg("Notice: Database size is %.2f MB, deleted %i rows", get_db_filesize(), affected); // Close database dbclose(); // Re-enable database actions database = true; } void *DB_thread(void *val) { // Set thread name prctl(PR_SET_NAME,"DB",0,0,0); if(!DBdeleteoldqueries) { // Lock FTL's data structure, since it is likely that it will be changed here enable_thread_lock("DB_thread"); // Save data to database save_to_DB(); // Release thread lock disable_thread_lock("DB_thread"); } else { // No thread locks needed delete_old_queries_in_DB(); DBdeleteoldqueries = false; } return NULL; }