diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 52b47665f..f35bcc152 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -71,6 +71,9 @@ // than this (ms). #define ZT_NETCONF_MIN_REQUEST_PERIOD 1000 +// Delay between backups in milliseconds +#define ZT_NETCONF_BACKUP_PERIOD 60000 + namespace ZeroTier { namespace { @@ -122,6 +125,7 @@ struct NetworkRecord { SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath) : _node(node), + _backupThreadRun(true), _dbPath(dbPath), _circuitTestPath(circuitTestPath), _db((sqlite3 *)0) @@ -247,10 +251,15 @@ SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,c throw std::runtime_error("SqliteNetworkController unable to read instanceId (it's NULL)"); _instanceId = iid; } + + _backupThread = Thread::start(this); } SqliteNetworkController::~SqliteNetworkController() { + _backupThreadRun = false; + Thread::join(_backupThread); + Mutex::Lock _l(_lock); if (_db) { sqlite3_finalize(_sGetNetworkById); @@ -991,6 +1000,52 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( return 404; } +void SqliteNetworkController::threadMain() + throw() +{ + uint64_t lastBackupTime = 0; + while (_backupThreadRun) { + if ((OSUtils::now() - lastBackupTime) >= ZT_NETCONF_BACKUP_PERIOD) { + lastBackupTime = OSUtils::now(); + + char backupPath[4096],backupPath2[4096]; + Utils::snprintf(backupPath,sizeof(backupPath),"%s.backupInProgress",_dbPath.c_str()); + Utils::snprintf(backupPath2,sizeof(backupPath),"%s.backup",_dbPath.c_str()); + OSUtils::rm(backupPath); // delete any unfinished backups + + sqlite3 *bakdb = (sqlite3 *)0; + sqlite3_backup *bak = (sqlite3_backup *)0; + if (sqlite3_open_v2(backupPath,&bakdb,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) { + fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_open_v2()"ZT_EOL_S); + continue; + } + bak = sqlite3_backup_init(bakdb,"main",_db,"main"); + if (!bak) { + sqlite3_close(bakdb); + OSUtils::rm(backupPath); // delete any unfinished backups + fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_backup_init()"ZT_EOL_S); + continue; + } + + int rc = SQLITE_OK; + for(;;) { + rc = sqlite3_backup_step(bak,1); + if ((rc == SQLITE_OK)||(rc == SQLITE_LOCKED)||(rc == SQLITE_BUSY)) + Thread::sleep(100); + else break; + } + + sqlite3_backup_finish(bak); + sqlite3_close(bakdb); + + OSUtils::rm(backupPath2); + ::rename(backupPath,backupPath2); + } + + Thread::sleep(500); + } +} + unsigned int SqliteNetworkController::_doCPGet( const std::vector &path, const std::map &urlArgs, diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index a3d5dfc7b..0e2bb63e7 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -39,6 +39,7 @@ #include "../node/Constants.hpp" #include "../node/NetworkController.hpp" #include "../node/Mutex.hpp" +#include "../osdep/Thread.hpp" // Number of in-memory last log entries to maintain per user #define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32 @@ -86,6 +87,10 @@ public: std::string &responseBody, std::string &responseContentType); + // threadMain() for backup thread -- do not call directly + void threadMain() + throw(); + private: enum IpAssignmentType { // IP assignment is a static IP address @@ -112,6 +117,8 @@ private: static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report); Node *_node; + Thread _backupThread; + volatile bool _backupThreadRun; std::string _dbPath; std::string _circuitTestPath; std::string _instanceId; diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index 5de35eba3..43fd28130 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -95,7 +95,6 @@ public: static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); } static inline bool mkdir(const char *path) - throw() { #ifdef __WINDOWS__ if (::PathIsDirectoryA(path))