// distribution boxbackup-0.11.1 (svn version: 2821_2827)
// Box Backup, http://www.boxbackup.org/
// 
// Copyright (c) 2003-2010, Ben Summers and contributors.
// All rights reserved.
// 
// Note that this project uses mixed licensing. Any file with this license
// attached, or where the code LICENSE-GPL appears on the first line, falls
// under the "Box Backup GPL" license. See the file COPYING.txt for more
// information about this license.
// 
// ---------------------------------------------------------------------
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC4]
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any software that can be downloaded from
// the OpenSSL website [http://www.openssl.org] under either the
// "OpenSSL License" or the "Original SSLeay License", and to distribute
// the linked executables under the terms of the "Box Backup GPL" license.
// 
// As a special exception to the GPLv2, the Box Backup Project gives
// permission to link any code falling under this license (the Box Backup
// GPL) with any version of Microsoft's Volume Shadow Copy Service 7.2 SDK
// or Microsoft Windows Software Development Kit (SDK), including
// vssapi.lib, that can be downloaded from the Microsoft website
// [*.microsoft.com], and to distribute the linked executables under the
// terms of the "Box Backup GPL" license.
// --------------------------------------------------------------------------
//
// File
//		Name:    BBStoreDHousekeeping.cpp
//		Purpose: Implementation of housekeeping functions for bbstored
//		Created: 11/12/03
//
// --------------------------------------------------------------------------

#include "Box.h"

#include <stdio.h>

#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)
	{
		return;
	}

	// Store the time
	mLastHousekeepingRun = timeNow;
	BOX_INFO("Starting housekeeping");

	// Get the list of accounts
	std::vector<int32_t> accounts;
	if(mpAccountDatabase)
	{
		mpAccountDatabase->GetAllAccountIDs(accounts);
	}
			
	SetProcessTitle("housekeeping, active");
			
	// Check them all
	for(std::vector<int32_t>::const_iterator i = accounts.begin(); i != accounts.end(); ++i)
	{
		try
		{
			if(mpAccounts)
			{
				// Get the account root
				std::string rootDir;
				int discSet = 0;
				mpAccounts->GetAccountRoot(*i, rootDir, discSet);
				
				// 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;
}


