/* 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; long int lastdbindex = 0; enum { DB_VERSION, DB_LASTTIMESTAMP }; 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("DB thread - 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); } // Release thread lock disable_thread_lock("DB_thread"); return NULL; } void *DB_GC_thread(void *val) { // Set thread name prctl(PR_SET_NAME,"DB-GC",0,0,0); // Need no lock on FTL's data structure, so this can work // in parallel w/o affecting FTL's core responsibilities // Disable any other DB accesses while doing this database = false; if(!dbopen()) { logg("Failed to open DB in GC thread"); database = true; return NULL; } while(get_db_filesize() > config.maxDBfilesize) { logg("Notice: DB filesize is %.2f MB, limit is %i.00 MB", get_db_filesize(), config.maxDBfilesize); if(!dbquery("DELETE FROM queries ORDER BY timestamp ASC LIMIT 1000")) { dbclose(); logg("ERROR: Deleting queries due to exceeded filesize of database failed!"); database = true; return NULL; } // When a large amount of data is deleted from the database file // it leaves behind empty space, or "free" database pages. This // means the database file might be larger than strictly necessary. // Running VACUUM to rebuild the database reclaims this space and // reduces the size of the database file. // Furthermore, running VACUUM ensures that each table and index is // largely stored contiguously within the database file. In some // cases, VACUUM may also reduce the number of partially filled pages // in the database, reducing the size of the database file further. dbquery("VACUUM"); } // Print final message logg("Notice: DB filesize is %.2f MB, limit is %i.00 MB", get_db_filesize(), config.maxDBfilesize); // Close database dbclose(); // Sleep one second so that we don't immediately re-launch the DB GC thread sleepms(1000); // Re-enable database actions database = true; return NULL; }