/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011-2016 Martin Raiber * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **************************************************************************/ #include "vld.h" #ifdef _WIN32 #define _CRT_RAND_S #include #endif #include #include #include #include #include #ifndef _WIN32 #include #endif #include "libfastcgi/fastcgi.hpp" #include "Interface/PluginMgr.h" #include "Interface/Thread.h" #include "Interface/DatabaseFactory.h" #include "Interface/WebSocket.h" #include "Server.h" #include "Template.h" #include "stringtools.h" #include "defaults.h" #include "SessionMgr.h" #include "md5.h" #include "ServiceAcceptor.h" #include "FileSettingsReader.h" #include "DBSettingsReader.h" #include "StreamPipe.h" #include "ThreadPool.h" #include "file.h" #include "file_memory.h" #include "utf8/utf8.h" #include "MemoryPipe.h" #include "MemorySettingsReader.h" #include "Database.h" #include "SQLiteFactory.h" #include "PipeThrottler.h" #include "mt19937ar.h" #include "Query.h" #ifdef _WIN32 #include "SChannelPipe.h" #endif #ifdef WITH_OPENSSL #include "OpenSSLPipe.h" #endif #ifdef _WIN32 #include #include "Mutex_std.h" #include "Condition_std.h" #include "SharedMutex_std.h" #else #include #include "Mutex_lin.h" #include "Condition_lin.h" #include "SharedMutex_lin.h" #endif #ifdef _WIN32 # include # include "Helper_win32.h" # include #else # include # include # include # include # include # include "config.h" #endif #include "StaticPluginRegistration.h" #ifdef __MACH__ #include #include #endif #ifdef HAVE_SYS_RANDOM_H #if defined(__FreeBSD__) extern "C" { #include } #else #include #endif //__FreeBSD__ #endif const size_t SEND_BLOCKSIZE=8192; const size_t MAX_THREAD_ID=std::string::npos; namespace { //GetTickCount64 for Windows Server 2003 #ifdef _WIN32 typedef ULONGLONG(WINAPI GetTickCount64_t)(VOID); GetTickCount64_t* GetTickCount64_fun = NULL; void initialize_GetTickCount64() { HMODULE hKernel32 = GetModuleHandleW(L"Kernel32.dll"); if(hKernel32) { GetTickCount64_fun = reinterpret_cast (GetProcAddress(hKernel32, "GetTickCount64")); } } #endif #ifdef _WIN32 //from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx const DWORD MS_VC_EXCEPTION = 0x406D1388; #pragma pack(push,8) typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; #pragma pack(pop) void SetThreadName(DWORD dwThreadID, const char* threadName) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = threadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; #pragma warning(push) #pragma warning(disable: 6320 6322) __try { RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info); } __except (EXCEPTION_EXECUTE_HANDLER) { } #pragma warning(pop) } #endif } extern bool run; CServer::CServer() { curr_thread_id=0; curr_pluginid=0; curr_postfilekey=0; loglevel=LL_INFO; logfile_a=false; circular_log_buffer_id=0; circular_log_buffer_idx=0; has_circular_log_buffer=false; failbits=0; startup_complete=false; log_mutex=createMutex(); action_mutex=createMutex(); web_socket_mutex = createMutex(); requests_mutex=createMutex(); outputs_mutex=createMutex(); db_mutex=createMutex(); thread_mutex=createMutex(); plugin_mutex=createMutex(); rps_mutex=createMutex(); postfiles_mutex=createMutex(); param_mutex=createMutex(); startup_complete_mutex=createMutex(); startup_complete_cond=createCondition(); rnd_mutex=createMutex(); initRandom(static_cast(time(0))); initRandom(getSecureRandomNumber()); log_rotation_size = std::string::npos; log_rotation_files = 10; if(FileExists("large_log_rotation")) { log_rotation_files = 100; } log_console_time = true; #ifdef _WIN32 initialize_GetTickCount64(); log_rotation_size = 20*1024*1024; //20MB send_window_size = -1; recv_window_size = -1; #endif } void CServer::setup(void) { sessmgr=new CSessionMgr(); threadpool=new CThreadPool(std::string::npos, 2, "idle pool thread"); #ifndef NO_SQLITE CDatabase::initMutex(); registerDatabaseFactory("sqlite", new SQLiteFactory ); #endif CQuery::init_mutex(); #ifdef MODE_WIN File::init_mutex(); #endif #ifdef _WIN32 SChannelPipe::init(); #endif #ifdef WITH_OPENSSL OpenSSLPipe::init(); #endif #ifdef ENABLE_C_ARES LookupInit(); #endif } void CServer::destroyAllDatabases(void) { IScopedLock lock(db_mutex); for(std::map::iterator i=databases.begin(); i!=databases.end();++i) { for( std::map::iterator j=i->second->tmap.begin();j!=i->second->tmap.end();++j) { delete j->second; } i->second->tmap.clear(); } } void CServer::destroyDatabases(THREAD_ID tid) { IScopedLock lock(db_mutex); for(std::map::iterator i=databases.begin(); i!=databases.end();++i) { std::map::iterator iter=i->second->tmap.find(tid); if(iter!=i->second->tmap.end()) { delete iter->second; i->second->tmap.erase(iter); } } } CServer::~CServer() { if(getServerParameter("leak_check")!="true") //minimal cleanup { return; } Log("deleting stream services..."); //Delete extra Services for(size_t i=0;i >::iterator iter1=perthread_pluginparams.begin(); iter1!=perthread_pluginparams.end();++iter1) { std::map >::iterator iter2=perthread_plugins.find( iter1->first ); if( iter2!=perthread_plugins.end() ) { std::map *pmap=&iter2->second; for( std::map::iterator iter3=pmap->begin();iter3!=pmap->end();++iter3 ) { iter1->second.first->destroyPluginInstance( iter3->second ); } } } Log("Deleting threadsafe plugins..."); for(std::map::iterator iter1=threadsafe_plugins.begin();iter1!=threadsafe_plugins.end();++iter1) { iter1->second->Remove(); } Log("Deleting pluginmanagers..."); for(std::map::iterator iter=perthread_pluginmgrs.begin();iter!=perthread_pluginmgrs.end();++iter) { iter->second->Remove(); } for(std::map::iterator iter=threadsafe_pluginmgrs.begin();iter!=threadsafe_pluginmgrs.end();++iter) { iter->second->Remove(); } Log("Deleting actions..."); for(std::map< std::string, std::map >::iterator iter1=actions.begin();iter1!=actions.end();++iter1) { for(std::map::iterator iter2=iter1->second.begin();iter2!=iter1->second.end();++iter2) { iter2->second->Remove(); } } actions.clear(); Log("Deleting sessmgr..."); delete sessmgr; Log("Shutting down ThreadPool..."); threadpool->Shutdown(); delete threadpool; Log("destroying databases..."); //Destroy Databases destroyAllDatabases(); Log("deleting database factories..."); for(std::map::iterator it=database_factories.begin();it!=database_factories.end();++it) { it->second->Remove(); } Log("unloading dlls..."); UnloadDLLs(); Log("Destroying mutexes"); destroy(log_mutex); destroy(action_mutex); destroy(web_socket_mutex); destroy(requests_mutex); destroy(outputs_mutex); destroy(db_mutex); destroy(thread_mutex); destroy(plugin_mutex); destroy(rps_mutex); destroy(postfiles_mutex); destroy(param_mutex); destroy(startup_complete_mutex); destroy(startup_complete_cond); destroy(rnd_mutex); #ifndef NO_SQLITE CDatabase::destroyMutex(); #endif #ifdef MODE_WIN File::destroy_mutex(); #endif std::cout << "Server cleanup done..." << std::endl; } void CServer::setServerParameters(const str_map &pServerParams) { IScopedLock lock(param_mutex); server_params=pServerParams; } std::string CServer::getServerParameter(const std::string &key) { return getServerParameter(key, ""); } std::string CServer::getServerParameter(const std::string &key, const std::string &def) { IScopedLock lock(param_mutex); str_map::iterator iter=server_params.find(key); if( iter!=server_params.end() ) { return iter->second; } else return def; } void CServer::setServerParameter(const std::string &key, const std::string &value) { IScopedLock lock(param_mutex); server_params[key]=value; } void CServer::Log( const std::string &pStr, int LogLevel) { if( loglevel <=LogLevel ) { IScopedLock lock(log_mutex); time_t rawtime; char buffer [100]; time ( &rawtime ); #ifdef _WIN32 struct tm timeinfo; localtime_s(&timeinfo, &rawtime); strftime (buffer,100,"%Y-%m-%d %X: ",&timeinfo); #else struct tm *timeinfo; timeinfo = localtime ( &rawtime ); strftime (buffer,100,"%Y-%m-%d %X: ",timeinfo); #endif if(log_console_time) { std::cout << buffer; } if( LogLevel==LL_ERROR ) { std::cout << "ERROR: " << pStr << std::endl; if(logfile_a) logfile << buffer << "ERROR: " << pStr << std::endl; } else if( LogLevel==LL_WARNING ) { std::cout << "WARNING: " << pStr << std::endl; if(logfile_a) logfile<< buffer << "WARNING: " << pStr << std::endl; } else { std::cout << pStr << std::endl; if(logfile_a) logfile << buffer << pStr << std::endl; } if(logfile_a) { logfile.flush(); rotateLogfile(); } if(has_circular_log_buffer) { logToCircularBuffer(pStr, LogLevel); } } else if(has_circular_log_buffer) { IScopedLock lock(log_mutex); logToCircularBuffer(pStr, LogLevel); } } void CServer::setLogRotationFiles(size_t n) { log_rotation_files = n; } void CServer::rotateLogfile() { if(static_cast(logfile.tellp())>log_rotation_size) { logfile.close(); logfile_a=false; for(size_t i=log_rotation_files-1;i>0;--i) { rename((logfile_fn+"."+convert(i)).c_str(), (logfile_fn+"."+convert(i+1)).c_str()); } deleteFile(logfile_fn+"."+convert(log_rotation_files)); rename(logfile_fn.c_str(), (logfile_fn+".1").c_str()); setLogFile(logfile_fn, logfile_chown_user); } } void CServer::setLogFile(const std::string &plf, std::string chown_user) { IScopedLock lock(log_mutex); if(logfile_a) { logfile.close(); logfile_a=false; } logfile_fn=plf; logfile_chown_user=chown_user; logfile.open( logfile_fn.c_str(), std::ios::app | std::ios::out | std::ios::binary ); if(logfile.is_open() ) { #ifndef _WIN32 if(!chown_user.empty()) { char buf[1000]; passwd pwbuf; passwd *pw; int rc=getpwnam_r(chown_user.c_str(), &pwbuf, buf, 1000, &pw); if(pw!=NULL) { chown(plf.c_str(), pw->pw_uid, pw->pw_gid); } else { Server->Log("Unable to change logfile ownership", LL_ERROR); } } #endif logfile_a=true; } } void CServer::setLogLevel(int LogLevel) { IScopedLock lock(log_mutex); loglevel=LogLevel; } THREAD_ID CServer::Execute(const std::string &action, const std::string &context, str_map &GET, str_map &POST, str_map &PARAMS, IOutputStream *req) { IAction *action_ptr=NULL; { IScopedLock lock(action_mutex); std::map >::iterator iter1=actions.find( context ); if( iter1!=actions.end() ) { std::map::iterator iter2=iter1->second.find(action); if( iter2!=iter1->second.end() ) action_ptr=iter2->second; } } if( action_ptr!=NULL ) { THREAD_ID tid=getThreadID(); IOutputStream *current_req=NULL; { IScopedLock lock(requests_mutex); std::map::iterator iter=current_requests.find(tid); if( iter!=current_requests.end() ) { current_req=iter->second; iter->second=req; } else { current_requests.insert(std::pair(tid, req) ); } } { IScopedLock lock(outputs_mutex); current_outputs[tid]=std::pair(false, ""); } action_ptr->Execute( GET, POST, tid, PARAMS); clearDatabases(tid); bool b = WriteRaw(tid, NULL, 0, false); if( current_req!=NULL ) { IScopedLock lock(requests_mutex); current_requests[tid]=current_req; } if(!b) { throw std::runtime_error("OutputStream error"); } return tid; } return 0; } std::string CServer::Execute(const std::string &action, const std::string &context, str_map &GET, str_map &POST, str_map &PARAMS) { CStringOutputStream cos; Execute(action, context, GET, POST, PARAMS, &cos); return cos.getData(); } void CServer::AddAction(IAction *action) { IScopedLock lock(action_mutex); std::map *ptr=&actions[action_context]; ptr->insert( std::pair(action->getName(), action ) ); } bool CServer::RemoveAction(IAction *action) { IScopedLock lock(action_mutex); std::map >::iterator iter1=actions.find(action_context); if( iter1!=actions.end() ) { std::map::iterator iter2=iter1->second.find( action->getName() ); if( iter2!=iter1->second.end() ) { iter1->second.erase( iter2 ); return true; } } return false; } void CServer::setActionContext(std::string context) { action_context=context; } void CServer::resetActionContext(void) { action_context.clear(); } int64 CServer::getTimeSeconds(void) { #ifdef _WIN32 SYSTEMTIME st; GetSystemTime(&st); return unix_timestamp(&st); #else timeval t; gettimeofday(&t,NULL); return t.tv_sec; #endif } int64 CServer::getTimeMS(void) { #ifdef _WIN32 if(GetTickCount64_fun) { return GetTickCount64_fun(); } else { return GetTickCount(); } #else //return (unsigned int)(((double)clock()/(double)CLOCKS_PER_SEC)*1000.0); /* boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC); static boost::int_fast64_t start_t=xt.sec; xt.sec-=start_t; unsigned int t=xt.sec*1000+(unsigned int)((double)xt.nsec/1000000.0); return t;*/ /*timeval tp; gettimeofday(&tp, NULL); static long start_t=tp.tv_sec; tp.tv_sec-=start_t; return tp.tv_sec*1000+tp.tv_usec/1000; */ #ifdef __APPLE__ clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); return static_cast(mts.tv_sec) * 1000 + mts.tv_nsec / 1000000; #else timespec tp; if(clock_gettime(CLOCK_MONOTONIC, &tp)!=0) { timeval tv; gettimeofday(&tv, NULL); static long start_t=tv.tv_sec; tv.tv_sec-=start_t; return tv.tv_sec*1000+tv.tv_usec/1000; } return static_cast(tp.tv_sec)*1000+tp.tv_nsec/1000000; #endif //__APPLE__ #endif } bool CServer::WriteRaw(THREAD_ID tid, const char *buf, size_t bsize, bool cached) { bool ret=true; if( cached==false ) { IOutputStream* req=NULL; { IScopedLock lock(requests_mutex); std::map::iterator iter=current_requests.find( tid ); if( iter!=current_requests.end() ) req=iter->second; } if( req!=NULL ) { { IScopedLock lock(outputs_mutex); std::pair *co=¤t_outputs[tid]; std::string *curr_output=&co->second; bool sent=co->first; if( sent==false && next(*curr_output,0,"Content-type: ")==false ) { curr_output->insert(0, "Content-type: "+DEFAULT_CONTENTTYPE+"\r\n\r\n"); co->first=true; } try { if(curr_output->size()write(*curr_output); } else { for(size_t i=0,size=curr_output->size();iwrite(&(*curr_output)[i], (std::min)(SEND_BLOCKSIZE, size-i) ); } } } catch(std::exception&) { Server->Log("Sending data failed", LL_INFO); ret=false; } curr_output->clear(); } try { if( bsize>0 && bsizewrite(buf, bsize); } else if(bsize>0 ) { for(size_t i=0,size=bsize;iwrite(&buf[i], (std::min)(SEND_BLOCKSIZE, size-i) ); } } } catch(std::exception&) { Server->Log("Sending data failed", LL_INFO); ret=false; } } else Log("Couldn't find THREAD_ID - cached=true", LL_ERROR); } else { if( bsize>0 ) { IScopedLock lock(outputs_mutex); std::map >::iterator iter=current_outputs.find( tid ); if( iter!=current_outputs.end() ) { iter->second.second.append(buf, bsize); } else { Log("Couldn't find THREAD_ID - cached=false", LL_ERROR); ret=false; } } } return ret; } bool CServer::Write(THREAD_ID tid, const std::string &str, bool cached) { return WriteRaw(tid, str.c_str(), str.size(), cached); } bool CServer::UnloadDLLs(void) { UnloadDLLs2(); return true; } void CServer::ShutdownPlugins(void) { for(std::map::iterator iter=unload_functs.begin(); iter!=unload_functs.end();++iter) { if(iter->second!=NULL) iter->second(); } unload_functs.clear(); } bool CServer::UnloadDLL(const std::string &name) { std::map::iterator iter=unload_functs.find(name); if(iter!=unload_functs.end() ) { if( iter->second!=NULL ) { iter->second(); unload_functs.erase(iter); } return true; } return false; } void CServer::destroy(IObject *obj) { if(obj!=NULL) { obj->Remove(); } } ITemplate* CServer::createTemplate(std::string pFile) { return new CTemplate(pFile); } THREAD_ID CServer::getThreadID(void) { #ifdef _WIN32 IScopedLock lock(thread_mutex); std::thread::id ct = std::this_thread::get_id(); std::map::iterator iter=threads.find(ct); if(iter!=threads.end() ) { return iter->second; } ++curr_thread_id; if( curr_thread_id>=MAX_THREAD_ID ) curr_thread_id=0; threads.insert( std::pair( ct, curr_thread_id) ); return curr_thread_id; #else //_WIN32 IScopedLock lock(thread_mutex); pthread_t ct=pthread_self(); std::map::iterator iter=threads.find(ct); if(iter!=threads.end() ) { return iter->second; } ++curr_thread_id; if( curr_thread_id>=MAX_THREAD_ID ) curr_thread_id=0; threads.insert( std::pair( ct, curr_thread_id) ); return curr_thread_id; #endif //_WIN32 } bool CServer::openDatabase(std::string pFile, DATABASE_ID pIdentifier, const str_map& params, std::string pEngine) { IScopedLock lock(db_mutex); std::map::iterator iter=databases.find(pIdentifier); if( iter!=databases.end() ) { Log("Database already openend", LL_ERROR); return false; } std::map::iterator iter2=database_factories.find(pEngine); if(iter2==database_factories.end()) { Log("Database engine not found", LL_ERROR); return false; } SDatabase* ndb= new SDatabase(iter2->second, pFile); ndb->single_user_mutex.reset(createSharedMutex()); ndb->lock_mutex.reset(createMutex()); ndb->lock_count.reset(new int); *ndb->lock_count = 0; ndb->unlock_cond.reset(createCondition()); ndb->params = params; databases.insert( std::pair(pIdentifier, ndb) ); return true; } IDatabase* CServer::getDatabase(THREAD_ID tid, DATABASE_ID pIdentifier) { IScopedLock lock(db_mutex); std::map::iterator database_iter=databases.find(pIdentifier); if( database_iter==databases.end() ) { Log("Database with identifier \""+convert((int)pIdentifier)+"\" couldn't be opened", LL_ERROR); return NULL; } std::map::iterator thread_iter=database_iter->second->tmap.find( tid ); if( thread_iter==database_iter->second->tmap.end() ) { IDatabaseInt *db=database_iter->second->factory->createDatabase(); std::pair::iterator, bool> ins = database_iter->second->tmap.insert(std::pair< THREAD_ID, IDatabaseInt* >(tid, db)); SDatabase* params = database_iter->second; lock.relock(NULL); if(!db->Open(params->file, params->attach, params->allocation_chunk_size, params->single_user_mutex.get(), params->lock_mutex.get(), params->lock_count.get(), params->unlock_cond.get(), params->params) ) { destroy(db); Log("Database \""+database_iter->second->file+"\" couldn't be opened", LL_ERROR); lock.relock(db_mutex); database_iter->second->tmap.erase(ins.first); return NULL; } return db; } else { return thread_iter->second; } } void CServer::clearDatabases(THREAD_ID tid) { IScopedLock lock(db_mutex); for(std::map::iterator i=databases.begin(); i!=databases.end();++i) { std::map::iterator iter=i->second->tmap.find(tid); if( iter!=i->second->tmap.end() ) { iter->second->destroyAllQueries(); } } } void CServer::setContentType(THREAD_ID tid, const std::string &str) { { IScopedLock lock(outputs_mutex); std::pair *co=¤t_outputs[tid]; std::string *curr_output=&co->second; if( curr_output->find("Content-type: ")==0 ) { *curr_output=strdelete("Content-type: "+getbetween("Content-type: ","\r\n\r\n", *curr_output)+"\r\n\r\n", *curr_output); } if(curr_output->find("\r\n\r\n")!=std::string::npos ) { curr_output->insert(0, "Content-type: "+str+"\r\n"); } else { curr_output->insert(0, "Content-type: "+str+"\r\n\r\n"); } co->first=true; } } void CServer::addHeader(THREAD_ID tid, const std::string &str) { { IScopedLock lock(outputs_mutex); std::pair *co=¤t_outputs[tid]; std::string *curr_output=&co->second; std::string tadd=str; if( curr_output->find("\r\n\r\n")!=std::string::npos ) { tadd+="\r\n"; } else { tadd+="\r\n\r\n"; } curr_output->insert(0, tadd); co->first=true; } } ISessionMgr *CServer::getSessionMgr(void) { return sessmgr; } std::string CServer::GenerateHexMD5(const std::string &input) { MD5 md((unsigned char*)input.c_str() ); return md.hex_digest(); } std::string CServer::GenerateBinaryMD5(const std::string &input) { MD5 md((unsigned char*)input.c_str(), static_cast(input.size())); unsigned char *p=md.raw_digest_int(); std::string ret(reinterpret_cast(p), reinterpret_cast(p)+16); return ret; } void CServer::StartCustomStreamService(IService *pService, std::string pServiceName, unsigned short pPort, int pMaxClientsPerThread, IServer::BindTarget bindTarget) { CServiceAcceptor *acc=new CServiceAcceptor(pService, pServiceName, pPort, pMaxClientsPerThread, bindTarget); Server->createThread(acc, pServiceName+": accept"); stream_services.push_back( acc ); } IPipe* CServer::ConnectStream(std::string pServer, unsigned short pPort, unsigned int pTimeoutms) { std::vector lookup_result; #ifdef ENABLE_C_ARES lookup_result = LookupWithTimeout(pServer, pTimeoutms, pTimeoutms/2); #else lookup_result = LookupBlocking(pServer); #endif if (lookup_result.empty()) { #ifdef _WIN32 Server->Log("No result when looking up hostname \"" + pServer + "\". Err: " + convert(WSAGetLastError()), LL_DEBUG); #else Server->Log("No result when looking up hostname \"" + pServer + "\". Err: " + convert(errno), LL_DEBUG); #endif return NULL; } for (size_t i = 0; i < lookup_result.size(); ++i) { IPipe* ret = ConnectStream(lookup_result[i], pPort, pTimeoutms); if (ret != NULL) return ret; } return NULL; } IPipe* CServer::ConnectStream(const SLookupBlockingResult& lookup_result, unsigned short pPort, unsigned int pTimeoutms) { union { sockaddr_in addr_v4; sockaddr_in6 addr_v6; } addr = {}; if (lookup_result.is_ipv6) { memcpy(&addr.addr_v6.sin6_addr, lookup_result.addr_v6, sizeof(addr.addr_v6.sin6_addr)); addr.addr_v6.sin6_port = htons(pPort); addr.addr_v6.sin6_family = AF_INET6; addr.addr_v6.sin6_scope_id = lookup_result.zone; } else { addr.addr_v4.sin_addr.s_addr = lookup_result.addr_v4; addr.addr_v4.sin_port = htons(pPort); addr.addr_v4.sin_family = AF_INET; } int type = SOCK_STREAM; #if !defined(_WIN32) && defined(SOCK_CLOEXEC) type |= SOCK_CLOEXEC; #endif SOCKET s = socket(lookup_result.is_ipv6 ? AF_INET6 : AF_INET, type, 0); if (s == SOCKET_ERROR) { #if !defined(_WIN32) && defined(SOCK_CLOEXEC) if (errno == EINVAL) { type &= ~SOCK_CLOEXEC; s = socket(lookup_result.is_ipv6 ? AF_INET6 : AF_INET, type, 0); } #endif if (s == SOCKET_ERROR) { return NULL; } } #if !defined(_WIN32) && !defined(SOCK_CLOEXEC) fcntl(s, F_SETFD, fcntl(s, F_GETFD, 0) | FD_CLOEXEC); #endif #ifdef _WIN32 u_long nonBlocking = 1; if (ioctlsocket(s, FIONBIO, &nonBlocking) == SOCKET_ERROR) { Server->Log("Error setting socket to non-blocking. Err: " + convert(WSAGetLastError()), LL_ERROR); closesocket(s); return NULL; } #else int flags = fcntl(s, F_GETFL, 0); if (flags == -1) { Server->Log("Error getting socket flags. Errno: " + convert(errno), LL_ERROR); closesocket(s); return NULL; } if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) { Server->Log("Error setting socket to non-blocking. Err: " + convert(errno), LL_ERROR); closesocket(s); return NULL; } #endif #ifdef __APPLE__ int val = 1; setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)); #endif sockaddr* addrptr = lookup_result.is_ipv6 ? reinterpret_cast(&addr.addr_v6) : reinterpret_cast(&addr.addr_v4); int rc=connect(s, addrptr, lookup_result.is_ipv6 ? sizeof(addr.addr_v6) : sizeof(addr.addr_v4) ); #ifndef _WIN32 if(rc==SOCKET_ERROR) { if(errno!=EINPROGRESS) { closesocket(s); Server->Log("errno !=EINPROGRESS. Connect failed...", LL_DEBUG); return NULL; } } else { return new CStreamPipe(s); } #else if(rc!=SOCKET_ERROR) { return new CStreamPipe(s); } #endif #ifdef _WIN32 fd_set conn; FD_ZERO(&conn); FD_SET(s, &conn); timeval lon; lon.tv_sec=(long)(pTimeoutms/1000); lon.tv_usec=(long)(pTimeoutms%1000)*1000; rc=select((int)s+1,NULL,&conn,NULL,&lon); if( rc>0 && FD_ISSET(s, &conn) ) { #else pollfd conn[1]; conn[0].fd=s; conn[0].events=POLLOUT; conn[0].revents=0; rc = poll(conn, 1, pTimeoutms); if( rc>0 ) { #endif int err; socklen_t len=sizeof(int); rc=getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, &len); if(rc<0) { closesocket(s); Server->Log("Error getting socket status.", LL_ERROR); return NULL; } if(err) { closesocket(s); Server->Log("Socket has error: "+convert(err), LL_INFO); return NULL; } else { #ifdef _WIN32 if(send_window_size>0) setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &send_window_size, sizeof(send_window_size)); if(recv_window_size>0) setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &recv_window_size, sizeof(recv_window_size)); #endif return new CStreamPipe(s); } } else { closesocket(s); return NULL; } } IPipe * CServer::ConnectSslStream(const std::string & pServer, unsigned short pPort, unsigned int pTimeoutms) { #if defined(_WIN32) || defined(WITH_OPENSSL) int64 starttime = Server->getTimeMS(); CStreamPipe* bpipe = static_cast(ConnectStream(pServer, pPort, pTimeoutms)); if (bpipe == NULL) return NULL; int64 remaining_time = pTimeoutms - (Server->getTimeMS() - starttime); if (remaining_time < 0) remaining_time = 0; #ifdef WITH_OPENSSL OpenSSLPipe* ssl_pipe = new OpenSSLPipe(bpipe); #else SChannelPipe* ssl_pipe = new SChannelPipe(bpipe); #endif if (!ssl_pipe->ssl_connect(pServer, static_cast(remaining_time))) { delete ssl_pipe; return NULL; } return ssl_pipe; #else return NULL; #endif } IPipe *CServer::PipeFromSocket(SOCKET pSocket) { return new CStreamPipe(pSocket); } void CServer::DisconnectStream(IPipe *pipe) { CStreamPipe *sp=(CStreamPipe*)pipe; SOCKET s=sp->getSocket(); closesocket(s); } std::string CServer::LookupHostname(const std::string & pIp) { std::string hostname; if (!::LookupHostname(pIp, hostname)) { return std::string(); } return hostname; } bool CServer::RegisterPluginPerThreadModel(IPluginMgr *pPluginMgr, std::string pName) { IScopedLock lock(plugin_mutex); std::map::iterator iter=perthread_pluginmgrs.find( pName ); if( iter!= perthread_pluginmgrs.end() ) return false; perthread_pluginmgrs.insert( std::pair( pName, pPluginMgr ) ); return true; } bool CServer::RegisterPluginThreadsafeModel(IPluginMgr *pPluginMgr, std::string pName) { IScopedLock lock(plugin_mutex); std::map::iterator iter=threadsafe_pluginmgrs.find( pName ); if( iter!= threadsafe_pluginmgrs.end() ) return false; threadsafe_pluginmgrs.insert( std::pair( pName, pPluginMgr ) ); return true; } PLUGIN_ID CServer::StartPlugin(std::string pName, str_map ¶ms) { IScopedLock lock(plugin_mutex); { std::map::iterator iter=perthread_pluginmgrs.find( pName ); if( iter!=perthread_pluginmgrs.end() ) { ++curr_pluginid; std::pair tmp(iter->second, params); perthread_pluginparams.insert( std::pair >(curr_pluginid, tmp) ); return curr_pluginid; } } { std::map::iterator iter1=threadsafe_pluginmgrs.find( pName ); if( iter1!=threadsafe_pluginmgrs.end() ) { ++curr_pluginid; IPlugin *plugin=iter1->second->createPluginInstance( params ); threadsafe_plugins.insert( std::pair( curr_pluginid, plugin) ); return curr_pluginid; } } return ILLEGAL_PLUGIN_ID; } bool CServer::RestartPlugin(PLUGIN_ID pIdentifier) { IScopedLock lock(plugin_mutex); { std::map >::iterator iter1=perthread_plugins.find( pIdentifier ); if( iter1!=perthread_plugins.end() ) { bool ret=true; for( std::map::iterator iter2=iter1->second.begin();iter2!=iter1->second.end();++iter2) { bool b=iter2->second->Reload(); if( b==false ) ret=false; } return ret; } } { std::map::iterator iter1=threadsafe_plugins.find( pIdentifier ); if( iter1!=threadsafe_plugins.end() ) { return iter1->second->Reload(); } } return false; } IPlugin* CServer::getPlugin(THREAD_ID tid, PLUGIN_ID pIdentifier) { IScopedLock lock(plugin_mutex); { std::map::iterator iter1=threadsafe_plugins.find( pIdentifier ); if( iter1!=threadsafe_plugins.end() ) { return iter1->second; } } { std::map >::iterator iter1=perthread_pluginparams.find( pIdentifier ); if( iter1!= perthread_pluginparams.end() ) { std::map *pmap=&perthread_plugins[pIdentifier]; std::map::iterator iter2=pmap->find(tid); if( iter2==pmap->end() ) { IPlugin* newplugin=iter1->second.first->createPluginInstance( iter1->second.second); pmap->insert( std::pair( tid, newplugin) ); newplugin->Reset(); return newplugin; } else { iter2->second->Reset(); return iter2->second; } } } return NULL; } IMutex* CServer::createMutex(void) { return new CMutex; } IPipe *CServer::createMemoryPipe(void) { return new CMemoryPipe; } #ifdef _WIN32 struct SThreadInfo { std::string name; IThread* t; }; void thread_helper_f2(IThread* t) { (*t)(); Server->destroyDatabases(Server->getThreadID()); } DWORD WINAPI thread_helper_f(LPVOID param) { IThread* t; #if defined(_WIN32) && defined(_DEBUG) SThreadInfo* thread_info = reinterpret_cast(param); SetThreadName(-1, thread_info->name.c_str()); t = thread_info->t; delete thread_info; #else t = reinterpret_cast(param); #endif #ifndef _DEBUG __try { #endif thread_helper_f2(t); #ifndef _DEBUG } __except(CServer::WriteDump(GetExceptionInformation())) { throw; } #endif return 0; } #else //_WIN32 void os_reset_priority(); void* thread_helper_f(void * t) { #ifdef __linux__ os_reset_priority(); #endif IThread *tmp=(IThread*)t; #ifndef _DEBUG try { #endif (*tmp)(); #ifndef _DEBUG } catch (std::exception& e) { Server->Log(std::string("Thread exit with unhandled std::exception ") + e.what(), LL_ERROR); throw; } catch (...) { Server->Log(std::string("Thread exit with unhandled C++ exception "), LL_ERROR); throw; } #endif Server->destroyDatabases(Server->getThreadID()); return NULL; } #endif //_WIN32 bool CServer::createThread(IThread *thread, const std::string& name, CreateThreadFlags flags) { const size_t thread_large_stack_size = 64 * 1024 * 1024; #ifdef _WIN32 SIZE_T stack_size = 0; if (flags & IServer::CreateThreadFlags_LargeStackSize) { stack_size = thread_large_stack_size; } void* param; #if defined(_WIN32) && defined(_DEBUG) SThreadInfo* thread_info = new SThreadInfo; thread_info->name = name; thread_info->t = thread; param = thread_info; #else param = thread; #endif HANDLE hThread = CreateThread(NULL, stack_size, &thread_helper_f, param, 0, NULL); if (hThread == NULL && flags & IServer::CreateThreadFlags_LargeStackSize) { Server->Log("Error creating thread with large stack size. Trying to create thread without LargeStackSize", LL_WARNING); hThread = CreateThread(NULL, 0, &thread_helper_f, param, 0, NULL); } if (hThread == NULL) { Server->Log("Error creating thread. Errno: " + convert((int)GetLastError()), LL_ERROR); return false; } else { CloseHandle(hThread); return true; } #else pthread_attr_t attr; if (pthread_attr_init(&attr) != 0) { Server->Log("Error initializing pthread attrs", LL_ERROR); return false; } #if !defined(URB_THREAD_STACKSIZE64) || !defined(URB_THREAD_STACKSIZE32) #ifndef _LP64 //Only on 32bit architectures pthread_attr_setstacksize(&attr, 1*1024*1024); #endif #else #ifdef _LP64 pthread_attr_setstacksize(&attr, (URB_THREAD_STACKSIZE64)); #else pthread_attr_setstacksize(&attr, (URB_THREAD_STACKSIZE32)); #endif #endif if (flags & IServer::CreateThreadFlags_LargeStackSize) { pthread_attr_setstacksize(&attr, thread_large_stack_size); } if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) { Server->Log("Error setting thread to detached", LL_ERROR); pthread_attr_destroy(&attr); return false; } pthread_t t; if (pthread_create(&t, &attr, &thread_helper_f, (void*)thread) != 0) { if (errno == ENOMEM && flags & IServer::CreateThreadFlags_LargeStackSize) { pthread_attr_destroy(&attr); Server->Log("Error creating pthread (ENOMEM). Trying to create thread without LargeStackSize", LL_WARNING); return createThread(thread, name, static_cast(flags & ~(IServer::CreateThreadFlags_LargeStackSize))); } Server->Log("Error creating pthread. Errno: " + convert(errno), LL_ERROR); pthread_attr_destroy(&attr); return false; } #ifdef HAVE_PTHREAD_SETNAME_NP if (!name.empty()) { std::string thread_name; if (name.size() > 15) { thread_name = name.substr(0, 15); } else { thread_name = name; } pthread_setname_np(t, thread_name.c_str()); } #endif //HAVE_PTHREAD_SETNAME_NP pthread_attr_destroy(&attr); return true; #endif } void CServer::setCurrentThreadName(const std::string& name) { #if defined(_WIN32) #if defined(_DEBUG) SetThreadName(-1, name.c_str()); #endif #elif defined(HAVE_PTHREAD_SETNAME_NP) if (!name.empty()) { pthread_t ct = pthread_self(); std::string thread_name; if (name.size() > 15) { thread_name = name.substr(0, 15); } else { thread_name = name; } pthread_setname_np(ct, thread_name.c_str()); } #endif } IThreadPool *CServer::getThreadPool(void) { return threadpool; } ISettingsReader* CServer::createFileSettingsReader(const std::string& pFile) { return new CFileSettingsReader(pFile); } ISettingsReader* CServer::createDBSettingsReader(THREAD_ID tid, DATABASE_ID pIdentifier, const std::string &pTable, const std::string &pSQL) { return new CDBSettingsReader(tid, pIdentifier, pTable, pSQL); } ISettingsReader* CServer::createDBSettingsReader(IDatabase *db, const std::string &pTable, const std::string &pSQL) { return new CDBSettingsReader(db, pTable, pSQL); } ISettingsReader * CServer::createDBMemSettingsReader(THREAD_ID tid, DATABASE_ID pIdentifier, const std::string & pTable, const std::string & pSQL) { return new CDBMemSettingsReader(tid, pIdentifier, pTable, pSQL); } ISettingsReader * CServer::createDBMemSettingsReader(IDatabase * db, const std::string & pTable, const std::string & pSQL) { return new CDBMemSettingsReader(db, pTable, pSQL); } ISettingsReader* CServer::createMemorySettingsReader(const std::string &pData) { return new CMemorySettingsReader(pData); } void CServer::wait(unsigned int ms) { #ifdef _WIN32 Sleep(ms); #else if (ms > 1000) { sleep(ms / 1000); if (ms % 1000 != 0) { usleep((ms % 1000) * 1000); } } else { usleep(ms * 1000); } #endif } unsigned int CServer::getNumRequests(void) { IScopedLock lock(rps_mutex); unsigned int ret=num_requests; num_requests=0; return ret; } void CServer::addRequest(void) { IScopedLock lock(rps_mutex); ++num_requests; } IFsFile* CServer::openFile(std::string pFilename, int pMode) { File *file=new File; if(!file->Open(pFilename, pMode) ) { delete file; return NULL; } return file; } IFsFile* CServer::openFileFromHandle(void *handle, const std::string& pFilename) { File *file=new File; if(!file->Open(handle, pFilename) ) { delete file; return NULL; } return file; } IFsFile* CServer::openTemporaryFile(void) { File *file=new File; if(!file->OpenTemporaryFile(tmpdir) ) { Server->Log("Error creating temporary file at \""+file->getFilename()+"\"", LL_ERROR); delete file; return NULL; } return file; } IMemFile* CServer::openMemoryFile(const std::string& name, bool mlock_mem) { return new CMemoryFile(name, mlock_mem); } bool CServer::deleteFile(std::string pFilename) { return DeleteFileInt(pFilename); } bool CServer::fileExists(std::string pFilename) { #ifndef WIN32 return ::FileExists(pFilename); #else std::fstream in(ConvertToWchar(pFilename).c_str(), std::ios::in); if( in.is_open()==false ) return false; in.close(); return true; #endif } std::string CServer::ConvertToUTF16(const std::string &input) { std::string ret; try { std::vector tmp; utf8::utf8to16(input.begin(), input.end(), back_inserter(tmp) ); ret.resize(tmp.size()*2); memcpy(&ret[0], &tmp[0], tmp.size()*2); } catch(...){} return ret; } std::string CServer::ConvertToUTF32(const std::string &input) { std::string ret; try { std::vector tmp; utf8::utf8to32(input.begin(), input.end(), back_inserter(tmp) ); ret.resize(tmp.size()*4); memcpy(&ret[0], &tmp[0], tmp.size()*4); } catch(...){} return ret; } std::string CServer::ConvertFromUTF16(const std::string &input) { if(input.empty()) { return std::string(); } std::string ret; try { utf8::utf16to8((utf8::uint16_t*)&input[0], (utf8::uint16_t*)(&input[input.size()-1]+1), back_inserter(ret)); } catch(...){} return ret; } std::string CServer::ConvertFromUTF32(const std::string &input) { if(input.empty()) { return std::string(); } std::string ret; try { utf8::utf32to8((utf8::uint32_t*)&input[0], (utf8::uint32_t*)(&input[input.size()-1]+1), back_inserter(ret)); } catch(...){} return ret; } std::wstring CServer::ConvertToWchar(const std::string &input) { if(input.empty()) { return std::wstring(); } std::wstring ret; try { if(sizeof(wchar_t)==2) { utf8::utf8to16(&input[0], &input[input.size()-1]+1, back_inserter(ret)); } else if(sizeof(wchar_t)==4) { utf8::utf8to32(&input[0], &input[input.size()-1]+1, back_inserter(ret)); } } catch(...){} return ret; } std::string CServer::ConvertFromWchar(const std::wstring &input) { if(input.empty()) { return std::string(); } std::string ret; try { if(sizeof(wchar_t)==2) { utf8::utf16to8(&input[0], &input[input.size()-1]+1, back_inserter(ret)); } else if(sizeof(wchar_t)==4) { utf8::utf32to8(&input[0], &input[input.size()-1]+1, back_inserter(ret)); } } catch(...){} return ret; } ICondition* CServer::createCondition(void) { return new CCondition(); } void CServer::addPostFile(POSTFILE_KEY pfkey, const std::string &name, const SPostfile &pf) { IScopedLock lock(postfiles_mutex); postfiles[pfkey][name]=pf; } SPostfile CServer::getPostFile(POSTFILE_KEY pfkey, const std::string &name) { IScopedLock lock(postfiles_mutex); std::map >::iterator iter1=postfiles.find(pfkey); if(iter1!=postfiles.end()) { std::map::iterator iter2=iter1->second.find(name); if(iter2!=iter1->second.end() ) { return iter2->second; } } return SPostfile(); } void CServer::clearPostFiles(POSTFILE_KEY pfkey) { IScopedLock lock(postfiles_mutex); std::map >::iterator iter1=postfiles.find(pfkey); if(iter1!=postfiles.end()) { for(std::map::iterator iter2=iter1->second.begin();iter2!=iter1->second.end();++iter2) { destroy(iter2->second.file); } postfiles.erase(iter1); } } POSTFILE_KEY CServer::getPostFileKey() { IScopedLock lock(postfiles_mutex); return curr_postfilekey++; } std::string CServer::getServerWorkingDir(void) { return workingdir; } void CServer::setServerWorkingDir(const std::string &wdir) { workingdir=wdir; } void CServer::setTemporaryDirectory(const std::string &dir) { tmpdir=dir; } void CServer::registerDatabaseFactory(const std::string &pEngineName, IDatabaseFactory *factory) { IScopedLock lock(db_mutex); database_factories[pEngineName]=factory; } bool CServer::hasDatabaseFactory(const std::string &pEngineName) { IScopedLock lock(db_mutex); std::map::iterator it=database_factories.find(pEngineName); return it!=database_factories.end(); } bool CServer::attachToDatabase(const std::string &pFile, const std::string &pName, DATABASE_ID pIdentifier) { IScopedLock lock(db_mutex); std::map::iterator iter=databases.find(pIdentifier); if( iter==databases.end() ) { return false; } if(std::find(iter->second->attach.begin(), iter->second->attach.end(), std::pair(pFile, pName))==iter->second->attach.end()) { iter->second->attach.push_back(std::pair(pFile, pName)); } return true; } bool CServer::setDatabaseAllocationChunkSize(DATABASE_ID pIdentifier, size_t allocation_chunk_size) { IScopedLock lock(db_mutex); std::map::iterator iter=databases.find(pIdentifier); if( iter==databases.end() ) { return false; } iter->second->allocation_chunk_size = allocation_chunk_size; return true; } void CServer::waitForStartupComplete(void) { IScopedLock lock(startup_complete_mutex); if(!startup_complete) { startup_complete_cond->wait(&lock); } } void CServer::startupComplete(void) { IScopedLock lock(startup_complete_mutex); startup_complete=true; startup_complete_cond->notify_all(); } IPipeThrottler* CServer::createPipeThrottler(size_t bps, bool percent_max) { return new PipeThrottler(bps, percent_max, NULL); } IPipeThrottler* CServer::createPipeThrottler( IPipeThrottlerUpdater* updater) { bool percent_max = false; size_t bps = updater->getThrottleLimit(percent_max); return new PipeThrottler(bps, percent_max, updater); } IThreadPool * CServer::createThreadPool(size_t max_threads, size_t max_waiting_threads, const std::string & idle_name) { return new CThreadPool(max_threads, max_waiting_threads, idle_name); } void CServer::shutdown(void) { run=false; } void CServer::initRandom(unsigned int seed) { init_genrand(seed); } unsigned int CServer::getRandomNumber(void) { IScopedLock lock(rnd_mutex); return genrand_int32(); } std::vector CServer::getRandomNumbers(size_t n) { IScopedLock lock(rnd_mutex); std::vector ret; ret.resize(n); for(size_t i=0;i=sizeof(unsigned int)) { *((unsigned int*)buf)=genrand_int32(); buf+=sizeof(unsigned int); } else { unsigned int rnd=genrand_int32(); memcpy(buf, &rnd, dptr-buf); buf+=dptr-buf; } } } unsigned int CServer::getSecureRandomNumber(void) { #ifdef _WIN32 unsigned int rnd; errno_t err = rand_s(&rnd); if (err != 0) { Log("Error generating secure random number", LL_ERROR); abort(); return getRandomNumber(); } return rnd; #else unsigned int rnd; secureRandomFill(reinterpret_cast(&rnd), sizeof(rnd)); return rnd; #endif } std::vector CServer::getSecureRandomNumbers(size_t n) { if (n == 0) { return std::vector(); } std::vector ret; ret.resize(n); #ifdef _WIN32 for (size_t i = 0; i < n; ++i) { ret[i] = getSecureRandomNumber(); } #else char* buf = reinterpret_cast(&ret[0]); size_t bsize = sizeof(unsigned int) * n; secureRandomFill(buf, bsize); #endif return ret; } void CServer::secureRandomFill(char *buf, size_t blen) { #ifdef _WIN32 char *dptr=buf+blen; while(buf(dptr - buf)); memcpy(buf, &rnd, to_write); buf += to_write; } #elif defined(HAVE_GETRANDOM) && defined(HAVE_SYS_RANDOM_H) while (blen > 0) { ssize_t rc = getrandom(buf, blen, 0); if (rc == -1 && errno == EINTR) { rc = 0; } else if (rc == -1) { Server->Log("Error filling secure random buffer. Errno " + convert((int64)errno), LL_ERROR); abort(); } blen -= rc; buf += rc; } #else std::fstream rnd_in("/dev/urandom", std::ios::in | std::ios::binary); if (!rnd_in.is_open()) { Log("Error opening /dev/urandom for secure random number fill. Errno " + convert((int64)errno), LL_ERROR); randomFill(buf, blen); return; } rnd_in.read(buf, blen); assert(rnd_in.gcount() == blen); if (rnd_in.gcount() != blen || rnd_in.fail() || rnd_in.eof()) { Log("Error reading secure random numbers fill. Errno " + convert((int64)errno), LL_ERROR); abort(); randomFill(buf, blen); return; } #endif } std::string CServer::secureRandomString(size_t len) { std::string rchars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; std::string key; std::vector rnd_n=Server->getSecureRandomNumbers(len); for(size_t j=0;j0?true:false; } std::vector CServer::getCicularLogBuffer( size_t minid ) { IScopedLock lock(log_mutex); if(minid==std::string::npos) { return circular_log_buffer; } for(size_t i=0;iminid && circular_log_buffer[i].id!=std::string::npos) { return circular_log_buffer; } } return std::vector(); } void CServer::logToCircularBuffer(const std::string& msg, int loglevel) { if(circular_log_buffer.empty()) return; SCircularLogEntry &entry=circular_log_buffer[circular_log_buffer_idx]; entry.utf8_msg=msg; entry.loglevel=loglevel; entry.id=circular_log_buffer_id++; entry.time=Server->getTimeSeconds(); circular_log_buffer_idx=(circular_log_buffer_idx+1)%circular_log_buffer.size(); } void CServer::setFailBit(size_t failbit) { failbits=failbits|failbit; } void CServer::clearFailBit(size_t failbit) { failbits=failbit & (~failbit); } size_t CServer::getFailBits(void) { return failbits; } ISharedMutex* CServer::createSharedMutex() { return new SharedMutex; } void CServer::setLogRotationFilesize( size_t filesize ) { log_rotation_size=filesize; } void CServer::setLogRotationNumFiles( size_t numfiles ) { log_rotation_files=numfiles; } void CServer::LoadStaticPlugins() { std::vector& staticplugins = get_static_plugin_registrations(); std::sort(staticplugins.begin(), staticplugins.end()); for(size_t i=0;igetName()] = websocket; } THREAD_ID CServer::ExecuteWebSocket(const std::string& name, str_map& GET, str_map& PARAMS, IPipe* pipe, const std::string& endpoint_name) { IWebSocket* ws; { IScopedLock lock(web_socket_mutex); std::map::iterator it = web_sockets.find(name); if (it == web_sockets.end()) return ILLEGAL_THREAD_ID; ws = it->second; } THREAD_ID tid = getThreadID(); ws->Execute(GET, tid, PARAMS, pipe, endpoint_name); return tid; }