/*************************************************************************
* UrBackup - Client/Server backup system
* Copyright (C) 2011 Martin Raiber
*
* 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 3 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, see .
**************************************************************************/
#ifndef CLIENT_ONLY
#include "server.h"
#include "../Interface/Server.h"
#include "../Interface/Database.h"
#include "../Interface/ThreadPool.h"
#include "server_get.h"
#include "database.h"
#include "../Interface/SettingsReader.h"
#include "server_status.h"
#include "../stringtools.h"
#include "../urbackupcommon/os_functions.h"
#include "InternetServiceConnector.h"
#include "../Interface/PipeThrottler.h"
#include
const unsigned int waittime=50*1000; //1 min
const int max_offline=5;
IPipeThrottler *BackupServer::global_internet_throttler=NULL;
IPipeThrottler *BackupServer::global_local_throttler=NULL;
IMutex *BackupServer::throttle_mutex=NULL;
BackupServer::BackupServer(IPipe *pExitpipe)
{
throttle_mutex=Server->createMutex();
exitpipe=pExitpipe;
if(Server->getServerParameter("internet_test_mode")=="true")
internet_test_mode=true;
else
internet_test_mode=false;
}
BackupServer::~BackupServer()
{
Server->destroy(throttle_mutex);
}
void BackupServer::operator()(void)
{
IDatabase *db=Server->getDatabase(Server->getThreadID(),URBACKUPDB_SERVER);
ISettingsReader *settings=Server->createDBSettingsReader(Server->getDatabase(Server->getThreadID(),URBACKUPDB_SERVER), "settings_db.settings");
#ifdef _WIN32
std::wstring tmpdir;
if(settings->getValue(L"tmpdir", &tmpdir) && !tmpdir.empty())
{
os_remove_nonempty_dir(tmpdir+os_file_sep()+L"urbackup_tmp");
if(!os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp"))
{
Server->wait(5000);
os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp");
}
Server->setTemporaryDirectory(tmpdir+os_file_sep()+L"urbackup_tmp");
}
else
{
wchar_t tmpp[MAX_PATH];
DWORD l;
if((l=GetTempPathW(MAX_PATH, tmpp))==0 || l>MAX_PATH )
{
wcscpy_s(tmpp,L"C:\\");
}
std::wstring w_tmp=tmpp;
if(!w_tmp.empty() && w_tmp[w_tmp.size()-1]=='\\')
{
w_tmp.erase(w_tmp.size()-1, 1); }
os_remove_nonempty_dir(w_tmp+os_file_sep()+L"urbackup_tmp");
if(!os_create_dir(w_tmp+os_file_sep()+L"urbackup_tmp"))
{
Server->wait(5000);
os_create_dir(tmpdir+os_file_sep()+L"urbackup_tmp");
}
Server->setTemporaryDirectory(w_tmp+os_file_sep()+L"urbackup_tmp");
}
#endif
q_get_extra_hostnames=db->Prepare("SELECT id,hostname FROM settings_db.extra_clients");
q_update_extra_ip=db->Prepare("UPDATE settings_db.extra_clients SET lastip=? WHERE id=?");
FileClient fc;
Server->wait(1000);
while(true)
{
findClients(fc);
startClients(fc);
if(!ServerStatus::isActive() && settings->getValue("autoshutdown", "false")=="true")
{
writestring("true", "urbackup/shutdown_now");
#ifdef _WIN32
ExitWindowsEx(EWX_POWEROFF|EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_APPLICATION|SHTDN_REASON_MINOR_OTHER );
#endif
}
std::string r;
exitpipe->Read(&r, waittime);
if(r=="exit")
{
removeAllClients();
exitpipe->Write("ok");
Server->destroy(settings);
db->destroyAllQueries();
delete this;
return;
}
}
}
void BackupServer::findClients(FileClient &fc)
{
db_results res=q_get_extra_hostnames->Read();
q_get_extra_hostnames->Reset();
std::vector addr_hints;
for(size_t i=0;iConvertToUTF8(res[i][L"hostname"]), &dest);
if(b)
{
q_update_extra_ip->Bind((_i64)dest);
q_update_extra_ip->Bind(res[i][L"id"]);
q_update_extra_ip->Write();
q_update_extra_ip->Reset();
in_addr tmp;
tmp.s_addr=dest;
addr_hints.push_back(tmp);
}
}
_u32 rc=fc.GetServers(true, addr_hints);
while(rc==ERR_CONTINUE)
{
Server->wait(50);
rc=fc.GetServers(false, addr_hints);
}
if(rc==ERR_ERROR)
{
Server->Log("Error in BackupServer::findClients rc==ERR_ERROR",LL_ERROR);
}
}
void BackupServer::startClients(FileClient &fc)
{
std::vector names;
std::vector servers;
if(!internet_test_mode)
{
names=fc.getServerNames();
servers=fc.getServers();
}
std::vector inetclient;
inetclient.resize(names.size());
std::fill(inetclient.begin(), inetclient.end(), false);
std::vector anames=InternetServiceConnector::getOnlineClients();
for(size_t i=0;iConvertToUnicode(anames[i]));
inetclient.push_back(true);
sockaddr_in n;
memset(&n, 0, sizeof(sockaddr_in));
servers.push_back(n);
}
for(size_t i=0;iConvertToUnicode(conv_filename(Server->ConvertToUTF8(names[i])));
std::map::iterator it=clients.find(names[i]);
if( it==clients.end() )
{
if(inetclient[i]==true)
{
bool skip=false;
for(size_t j=0;jLog(L"New Backupclient: "+names[i]);
ServerStatus::setOnline(names[i], true);
IPipe *np=Server->createMemoryPipe();
BackupServerGet *client=new BackupServerGet(np, servers[i], names[i], inetclient[i]);
Server->getThreadPool()->execute(client);
SClient c;
c.pipe=np;
c.offlinecount=0;
c.addr=servers[i];
c.internet_connection=inetclient[i];
ServerStatus::setIP(names[i], c.addr.sin_addr.s_addr);
clients.insert(std::pair(names[i], c) );
}
else if(it->second.offlinecountsecond.internet_connection==true)
{
found_lan=true;
}
if(it->second.addr.sin_addr.s_addr==servers[i].sin_addr.s_addr && !found_lan)
{
it->second.offlinecount=0;
}
else
{
bool none_fits=true;
for(size_t j=0;jsecond.addr.sin_addr.s_addr==servers[j].sin_addr.s_addr)
{
none_fits=false;
break;
}
}
if(none_fits || found_lan)
{
it->second.addr=servers[i];
std::string msg;
msg.resize(7+sizeof(sockaddr_in)+1);
msg[0]='a'; msg[1]='d'; msg[2]='d'; msg[3]='r'; msg[4]='e'; msg[5]='s'; msg[6]='s';
memcpy(&msg[7], &it->second.addr, sizeof(sockaddr_in));
msg[7+sizeof(sockaddr_in)]=(inetclient[i]==true?1:0);
it->second.pipe->Write(msg);
char *ip=(char*)&it->second.addr.sin_addr.s_addr;
Server->Log("New client address: "+nconvert((unsigned char)ip[0])+"."+nconvert((unsigned char)ip[1])+"."+nconvert((unsigned char)ip[2])+"."+nconvert((unsigned char)ip[3]), LL_INFO);
ServerStatus::setIP(names[i], it->second.addr.sin_addr.s_addr);
it->second.offlinecount=0;
}
}
}
}
bool c=true;
size_t maxi=0;
while(c && !clients.empty())
{
c=false;
size_t i_c=0;
for(std::map::iterator it=clients.begin();it!=clients.end();++it)
{
bool found=false;
for(size_t i=0;ifirst==names[i] )
{
found=true;
break;
}
}
if( found==false || it->second.offlinecount>max_offline)
{
if(it->second.offlinecount==max_offline)
{
Server->Log(L"Client exitet: "+it->first);
it->second.pipe->Write("exit");
++it->second.offlinecount;
ServerStatus::setOnline(it->first, false);
}
else if(it->second.offlinecount>max_offline)
{
std::string msg;
std::vector msgs;
while(it->second.pipe->Read(&msg,0)>0)
{
if(msg!="ok")
{
msgs.push_back(msg);
}
else
{
Server->Log(L"Client finished: "+it->first);
ServerStatus::setDone(it->first, true);
Server->destroy(it->second.pipe);
clients.erase(it);
maxi=i_c;
c=true;
break;
}
}
if( c==false )
{
for(size_t i=0;isecond.pipe->Write(msgs[i]);
}
}
else
{
break;
}
}
else if(i_c>=maxi)
{
SStatusAction s_action=ServerStatus::getStatus(it->first).statusaction;
if(s_action==sa_none)
{
++it->second.offlinecount;
}
}
}
++i_c;
}
}
}
void BackupServer::removeAllClients(void)
{
for(std::map::iterator it=clients.begin();it!=clients.end();++it)
{
it->second.pipe->Write("exitnow");
std::string msg;
while(msg!="ok")
{
it->second.pipe->Read(&msg);
it->second.pipe->Write(msg.c_str());
Server->wait(500);
}
Server->destroy(it->second.pipe);
}
}
IPipeThrottler *BackupServer::getGlobalInternetThrottler(size_t speed_bps)
{
IScopedLock lock(throttle_mutex);
if(global_internet_throttler==NULL && speed_bps==0 )
return NULL;
if(global_internet_throttler==NULL)
{
global_internet_throttler=Server->createPipeThrottler(speed_bps);
}
else
{
global_internet_throttler->changeThrottleLimit(speed_bps);
}
return global_internet_throttler;
}
IPipeThrottler *BackupServer::getGlobalLocalThrottler(size_t speed_bps)
{
IScopedLock lock(throttle_mutex);
if(global_local_throttler==NULL && speed_bps==0 )
return NULL;
if(global_local_throttler==NULL)
{
global_local_throttler=Server->createPipeThrottler(speed_bps);
}
else
{
global_local_throttler->changeThrottleLimit(speed_bps);
}
return global_local_throttler;
}
void BackupServer::cleanupThrottlers(void)
{
if(global_internet_throttler!=NULL)
{
Server->destroy(global_internet_throttler);
}
if(global_local_throttler!=NULL)
{
Server->destroy(global_local_throttler);
}
}
#endif //CLIENT_ONLY