diff --git a/src/api/auth.c b/src/api/auth.c index 22bbd9de2f..f09f3c59f9 100644 --- a/src/api/auth.c +++ b/src/api/auth.c @@ -25,6 +25,8 @@ #include "config/password.h" // database session functions #include "database/session-table.h" +// FTLDBerror() +#include "database/common.h" static uint16_t max_sessions = 0; static struct session *auth_data = NULL; @@ -51,7 +53,9 @@ void init_api(void) log_crit("Could not allocate memory for API sessions, check config value of webserver.api.max_sessions"); exit(EXIT_FAILURE); } - restore_db_sessions(auth_data, max_sessions); + + if(!FTLDBerror()) + restore_db_sessions(auth_data, max_sessions); } void free_api(void) diff --git a/src/database/common.c b/src/database/common.c index 7917c5b94f..bf54a4c4b5 100644 --- a/src/database/common.c +++ b/src/database/common.c @@ -311,6 +311,7 @@ void db_init(void) { log_warn("Database not available, please ensure the database is unlocked when starting pihole-FTL !"); dbclose(&db); + DBerror = true; return; } else @@ -328,6 +329,7 @@ void db_init(void) { log_err("Counter table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -343,6 +345,7 @@ void db_init(void) { log_err("Network table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -358,6 +361,7 @@ void db_init(void) { log_err("Unable to unify clients in network table, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -373,6 +377,7 @@ void db_init(void) { log_err("Network-addresses table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -388,6 +393,7 @@ void db_init(void) { log_err("Message table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -403,6 +409,7 @@ void db_init(void) { log_err("Column additional_info not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -418,6 +425,7 @@ void db_init(void) { log_err("Network addresses table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -433,6 +441,7 @@ void db_init(void) { log_err("Aliasclients table not initialized, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -448,6 +457,7 @@ void db_init(void) { log_info("Queries table not optimized, database not available"); dbclose(&db); + DBerror = true; return; } @@ -469,6 +479,7 @@ void db_init(void) { log_info("Link table for additional_info not generated, database not available"); dbclose(&db); + DBerror = true; return; } @@ -490,6 +501,7 @@ void db_init(void) { log_info("Additional records not generated, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -505,6 +517,7 @@ void db_init(void) { log_info("Additional records not generated, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -520,6 +533,7 @@ void db_init(void) { log_info("FTL table description cannot be added, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -535,6 +549,7 @@ void db_init(void) { log_info("Session table cannot be created, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -550,6 +565,7 @@ void db_init(void) { log_info("Session table cannot be updated, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -565,6 +581,7 @@ void db_init(void) { log_info("regex_id cannot be renamed to list_id, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -580,6 +597,7 @@ void db_init(void) { log_info("Session table cannot be updated, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -595,6 +613,7 @@ void db_init(void) { log_info("Session table cannot be updated, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -610,6 +629,7 @@ void db_init(void) { log_info("Network addresses network_id index cannot be added, database not available"); dbclose(&db); + DBerror = true; return; } // Get updated version @@ -627,7 +647,12 @@ void db_init(void) // Last check after all migrations, if this happens, it will cause the // CI to fail the tests if(dbversion != MEMDB_VERSION) - log_err("Expected query database version %d but found %d", MEMDB_VERSION, dbversion); + { + log_err("Expected query database version %d but found %d, database not available", MEMDB_VERSION, dbversion); + dbclose(&db); + DBerror = true; + return; + } lock_shm(); import_aliasclients(db); diff --git a/src/database/query-table.c b/src/database/query-table.c index 88fa972304..e27a126d4a 100644 --- a/src/database/query-table.c +++ b/src/database/query-table.c @@ -124,15 +124,14 @@ bool init_memory_database(void) } } - // Attach disk database - if(!attach_database(_memdb, NULL, config.files.database.v.s, "disk")) - return false; + // Attach disk database. This may fail if the database is unavailable + const bool attached = attach_database(_memdb, NULL, config.files.database.v.s, "disk"); // Enable WAL mode for the on-disk database (pihole-FTL.db) if // configured (default is yes). User may not want to enable WAL // mode if the database is on a network share as all processes // accessing the database must be on the same host in WAL mode. - if(config.database.useWAL.v.b) + if(config.database.useWAL.v.b && attached) { // Change journal mode to WAL // - WAL is significantly faster in most scenarios. @@ -149,7 +148,7 @@ bool init_memory_database(void) return false; } } - else + else if(attached) { // Unlike the other journaling modes, PRAGMA journal_mode=WAL is // persistent. If a process sets WAL mode, then closes and @@ -223,7 +222,11 @@ bool init_memory_database(void) return false; } - load_queries_from_disk(); + // Attach disk database + if(attached) + load_queries_from_disk(); + else + log_err("init_memory_database(): Failed to attach disk database"); // Everything went well return true; @@ -352,6 +355,10 @@ bool attach_database(sqlite3* db, const char **message, const char *path, const bool okay = false; sqlite3_stmt *stmt = NULL; + // Only try to attach database if it is not known to be broken + if(FTLDBerror()) + return false; + log_debug(DEBUG_DATABASE, "ATTACH %s AS %s", path, alias); // ATTACH database file on-disk @@ -583,6 +590,11 @@ bool export_queries_to_disk(bool final) { bool okay = false; const double time = double_time() - (final ? 0.0 : REPLY_TIMEOUT); + + // Only try to export to database if it is known to not be broken + if(FTLDBerror()) + return false; + const char *querystr = "INSERT INTO disk.query_storage SELECT * FROM query_storage WHERE id > ? AND timestamp < ?"; log_debug(DEBUG_DATABASE, "Storing queries on disk WHERE id > %lu (max is %lu) and timestamp < %f", @@ -1032,6 +1044,10 @@ void DB_read_queries(void) "dnssec "\ "FROM queries WHERE timestamp >= ?"; + // Only try to import from database if it is known to not be broken + if(FTLDBerror()) + return; + log_info("Parsing queries in database"); // Prepare SQLite3 statement @@ -1350,12 +1366,22 @@ void DB_read_queries(void) log_info("Imported %u queries from the long-term database", counters->queries); } -void update_disk_db_idx(void) +void init_disk_db_idx(void) { // Query the database to get the maximum database ID is important to avoid // starting counting from zero (would result in a UNIQUE constraint violation) const char *querystr = "SELECT MAX(id) FROM disk.query_storage"; + // If the disk database is broken, we cannot import queries from it, + // however, as we will also never export any queries, we can safely + // assume any index + if(FTLDBerror()) + { + last_disk_db_idx = 0; + last_mem_db_idx = 0; + return; + } + // Prepare SQLite3 statement sqlite3_stmt *stmt = NULL; sqlite3 *memdb = get_memdb(); @@ -1365,7 +1391,7 @@ void update_disk_db_idx(void) if(rc == SQLITE_OK && (rc = sqlite3_step(stmt)) == SQLITE_ROW) last_disk_db_idx = sqlite3_column_int64(stmt, 0); else - log_err("update_disk_db_idx(): Failed to get MAX(id) from disk.query_storage: %s", + log_err("init_disk_db_idx(): Failed to get MAX(id) from disk.query_storage: %s", sqlite3_errstr(rc)); // Finalize statement @@ -1384,6 +1410,10 @@ bool queries_to_database(void) unsigned int added = 0, updated = 0; sqlite3_int64 idx = 0; + // Only try to export to database if it is known to not be broken + if(FTLDBerror()) + return false; + // Skip, we never store nor count queries recorded while have been in // maximum privacy mode in the database if(config.misc.privacylevel.v.privacy_level >= PRIVACY_MAXIMUM) diff --git a/src/database/query-table.h b/src/database/query-table.h index 20b02e6c63..c051499dce 100644 --- a/src/database/query-table.h +++ b/src/database/query-table.h @@ -117,7 +117,7 @@ bool export_queries_to_disk(bool final); bool delete_old_queries_from_db(const bool use_memdb, const double mintime); bool add_additional_info_column(sqlite3 *db); void DB_read_queries(void); -void update_disk_db_idx(void); +void init_disk_db_idx(void); bool queries_to_database(void); bool optimize_queries_table(sqlite3 *db); diff --git a/src/dnsmasq_interface.c b/src/dnsmasq_interface.c index 47bafb146f..de82f34b5a 100644 --- a/src/dnsmasq_interface.c +++ b/src/dnsmasq_interface.c @@ -3100,7 +3100,8 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw, bool dnsmasq_start) log_crit("Cannot initialize in-memory database."); // Flush messages stored in the long-term database - flush_message_table(); + if(!FTLDBerror()) + flush_message_table(); // Verify checksum of this binary early on to ensure that the binary is // not corrupted and that the binary is not tampered with. We can only @@ -3109,7 +3110,7 @@ void FTL_fork_and_bind_sockets(struct passwd *ent_pw, bool dnsmasq_start) verify_FTL(false); // Initialize in-memory database starting index - update_disk_db_idx(); + init_disk_db_idx(); // Handle real-time signals in this process (and its children) // Helper processes are already split from the main instance