// -------------------------------------------------------------------------- // // File // Name: BBStoreDHousekeeping.cpp // Purpose: Implementation of housekeeping functions for bbstored // Created: 11/12/03 // // -------------------------------------------------------------------------- #include "Box.h" #include #include "BackupStoreDaemon.h" #include "BackupStoreAccountDatabase.h" #include "BackupStoreAccounts.h" #include "HousekeepStoreAccount.h" #include "BoxTime.h" #include "Configuration.h" #include "MemLeakFindOn.h" // -------------------------------------------------------------------------- // // Function // Name: BackupStoreDaemon::HousekeepingProcess() // Purpose: Do housekeeping // Created: 11/12/03 // // -------------------------------------------------------------------------- void BackupStoreDaemon::HousekeepingInit() { mLastHousekeepingRun = 0; } void BackupStoreDaemon::HousekeepingProcess() { HousekeepingInit(); // Get the time between housekeeping runs const Configuration &rconfig(GetConfiguration()); int64_t housekeepingInterval = SecondsToBoxTime(rconfig.GetKeyValueInt("TimeBetweenHousekeeping")); while(!StopRun()) { RunHousekeepingIfNeeded(); // Stop early? if(StopRun()) { break; } // Calculate how long should wait before doing the next // housekeeping run int64_t timeNow = GetCurrentBoxTime(); time_t secondsToGo = BoxTimeToSeconds( (mLastHousekeepingRun + housekeepingInterval) - timeNow); if(secondsToGo < 1) secondsToGo = 1; if(secondsToGo > 60) secondsToGo = 60; int32_t millisecondsToGo = ((int)secondsToGo) * 1000; // Check to see if there's any message pending CheckForInterProcessMsg(0 /* no account */, millisecondsToGo); } } void BackupStoreDaemon::RunHousekeepingIfNeeded() { // Get the time between housekeeping runs const Configuration &rconfig(GetConfiguration()); int64_t housekeepingInterval = SecondsToBoxTime(rconfig.GetKeyValueInt("TimeBetweenHousekeeping")); // Time now int64_t timeNow = GetCurrentBoxTime(); // Do housekeeping if the time interval has elapsed since the last check if((timeNow - mLastHousekeepingRun) < housekeepingInterval) { BOX_TRACE("No need for housekeeping, " << BoxTimeToSeconds(timeNow - mLastHousekeepingRun) << " seconds since last run is less than " << BoxTimeToSeconds(housekeepingInterval)); return; } else { BOX_TRACE("Running housekeeping now, because " << BoxTimeToSeconds(timeNow - mLastHousekeepingRun) << " seconds since last run is more than " << BoxTimeToSeconds(housekeepingInterval)); } // Store the time mLastHousekeepingRun = timeNow; BOX_INFO("Starting housekeeping"); // Get the list of accounts std::vector accounts; if(mpAccountDatabase) { mpAccountDatabase->GetAllAccountIDs(accounts); } SetProcessTitle("housekeeping, active"); // Check them all for(std::vector::const_iterator i = accounts.begin(); i != accounts.end(); ++i) { try { std::string rootDir; int discSet = 0; { // Tag log output to identify account std::ostringstream tag; tag << "hk/" << BOX_FORMAT_ACCOUNT(*i); Logging::Tagger tagWithClientID(tag.str()); // Get the account root mpAccounts->GetAccountRoot(*i, rootDir, discSet); // Reset tagging as HousekeepStoreAccount will // do that itself, to avoid duplicate tagging. // Happens automatically when tagWithClientID // goes out of scope. } // Do housekeeping on this account HousekeepStoreAccount housekeeping(*i, rootDir, discSet, this); housekeeping.DoHousekeeping(); } catch(BoxException &e) { BOX_ERROR("Housekeeping on account " << BOX_FORMAT_ACCOUNT(*i) << " threw exception, " "aborting run for this account: " << e.what() << " (" << e.GetType() << "/" << e.GetSubType() << ")"); } catch(std::exception &e) { BOX_ERROR("Housekeeping on account " << BOX_FORMAT_ACCOUNT(*i) << " threw exception, " "aborting run for this account: " << e.what()); } catch(...) { BOX_ERROR("Housekeeping on account " << BOX_FORMAT_ACCOUNT(*i) << " threw exception, " "aborting run for this account: " "unknown exception"); } int64_t timeNow = GetCurrentBoxTime(); time_t secondsToGo = BoxTimeToSeconds( (mLastHousekeepingRun + housekeepingInterval) - timeNow); if(secondsToGo < 1) secondsToGo = 1; if(secondsToGo > 60) secondsToGo = 60; int32_t millisecondsToGo = ((int)secondsToGo) * 1000; // Check to see if there's any message pending CheckForInterProcessMsg(0 /* no account */, millisecondsToGo); // Stop early? if(StopRun()) { break; } } BOX_INFO("Finished housekeeping"); // Placed here for accuracy, if StopRun() is true, for example. SetProcessTitle("housekeeping, idle"); } void BackupStoreDaemon::OnIdle() { if (!IsSingleProcess()) { return; } if (!mHousekeepingInited) { HousekeepingInit(); mHousekeepingInited = true; } RunHousekeepingIfNeeded(); } // -------------------------------------------------------------------------- // // Function // Name: BackupStoreDaemon::CheckForInterProcessMsg(int, int) // Purpose: Process a message, returning true if the housekeeping process // should abort for the specified account. // Created: 11/12/03 // // -------------------------------------------------------------------------- bool BackupStoreDaemon::CheckForInterProcessMsg(int AccountNum, int MaximumWaitTime) { if(!mInterProcessCommsSocket.IsOpened()) { return false; } // First, check to see if it's EOF -- this means something has gone wrong, and the housekeeping should terminate. if(mInterProcessComms.IsEOF()) { SetTerminateWanted(); return true; } // Get a line, and process the message std::string line; if(mInterProcessComms.GetLine(line, false /* no pre-processing */, MaximumWaitTime)) { BOX_TRACE("Housekeeping received command '" << line << "' over interprocess comms"); int account = 0; if(line == "h") { // HUP signal received by main process SetReloadConfigWanted(); return true; } else if(line == "t") { // Terminate signal received by main process SetTerminateWanted(); return true; } else if(sscanf(line.c_str(), "r%x", &account) == 1) { // Main process is trying to lock an account -- are we processing it? if(account == AccountNum) { // Yes! -- need to stop now so when it retries to get the lock, it will succeed BOX_INFO("Housekeeping on account " << BOX_FORMAT_ACCOUNT(AccountNum) << "giving way to client connection"); return true; } } } return false; }