uint32_t fileWrite(const char *filename, uint8_t *buffer, uint32_t size ) { int file ; uint32_t result ; mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; file = open( filename, O_WRONLY | O_CREAT | O_TRUNC, mode ); if( file == -1 ) { LOG_BASIC("error: open file %s !\n", filename); return FILE_OPEN_ERROR ; } result = (uint32_t)write(file, buffer, size); if( result != size ) { LOG_BASIC("error: write (%d writed instead of %ld).\n",result,size); return FILE_IO_ERROR; } close( file ); return FILE_OK ; }
void ConsoleThread::terminate() { m_killSwitch = true; #ifdef WIN32 /* write the return keydown/keyup event */ DWORD dwTmp; INPUT_RECORD ir[2]; ir[0].EventType = KEY_EVENT; ir[0].Event.KeyEvent.bKeyDown = TRUE; ir[0].Event.KeyEvent.dwControlKeyState = 288; ir[0].Event.KeyEvent.uChar.AsciiChar = 13; ir[0].Event.KeyEvent.wRepeatCount = 1; ir[0].Event.KeyEvent.wVirtualKeyCode = 13; ir[0].Event.KeyEvent.wVirtualScanCode = 28; ir[1].EventType = KEY_EVENT; ir[1].Event.KeyEvent.bKeyDown = FALSE; ir[1].Event.KeyEvent.dwControlKeyState = 288; ir[1].Event.KeyEvent.uChar.AsciiChar = 13; ir[1].Event.KeyEvent.wRepeatCount = 1; ir[1].Event.KeyEvent.wVirtualKeyCode = 13; ir[1].Event.KeyEvent.wVirtualScanCode = 28; WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, & dwTmp); #endif LOG_BASIC("Waiting for console thread to terminate...."); while(m_isRunning) { Arcemu::Sleep(100); } LOG_BASIC("Console shut down."); }
void LogonConsole::Kill() { _thread->kill.SetVal(true); #ifdef WIN32 /* write the return keydown/keyup event */ DWORD dwTmp; INPUT_RECORD ir[2]; ir[0].EventType = KEY_EVENT; ir[0].Event.KeyEvent.bKeyDown = TRUE; ir[0].Event.KeyEvent.dwControlKeyState = 288; ir[0].Event.KeyEvent.uChar.AsciiChar = 13; ir[0].Event.KeyEvent.wRepeatCount = 1; ir[0].Event.KeyEvent.wVirtualKeyCode = 13; ir[0].Event.KeyEvent.wVirtualScanCode = 28; ir[1].EventType = KEY_EVENT; ir[1].Event.KeyEvent.bKeyDown = FALSE; ir[1].Event.KeyEvent.dwControlKeyState = 288; ir[1].Event.KeyEvent.uChar.AsciiChar = 13; ir[1].Event.KeyEvent.wRepeatCount = 1; ir[1].Event.KeyEvent.wVirtualKeyCode = 13; ir[1].Event.KeyEvent.wVirtualScanCode = 28; WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), ir, 2, & dwTmp); #endif LOG_BASIC("Waiting for console thread to terminate...."); while(_thread != NULL) { arcpro::Sleep(100); } LOG_BASIC("Console shut down."); }
void XAI::InitAI(IGlobalAICallback* gcb, int team) { xaiInstance = xaiInstances++; xaiHelper->Init(gcb); LOG_BASIC( xaiHelper->logger, "[XAI::InitAI]\n" << "\tteam: " << team << "\n" "\tinstance: " << xaiInstance << "\n" "\tactive instances: " << xaiInstances << "\n" ); std::string initMsg = std::string(AI_VERSION) + " initialized succesfully!"; std::string logMsg = "logging events to " + (xaiHelper->logger->GetLogName(NULL)) + "[log].txt"; xaiHelper->rcb->SendTextMsg(initMsg.c_str(), 0); xaiHelper->rcb->SendTextMsg(logMsg.c_str(), 0); XAICScopedTimer t("[XAI::InitAI]", xaiHelper->timer); XAI_BEG_EXCEPTION XAIInitEvent e; e.type = XAI_EVENT_INIT; e.frame = 0; xaiHelper->eventHandler->NotifyReceivers(&e); XAI_END_EXCEPTION(xaiHelper->logger, "[XAI::InitAI]") }
uint8_t *fileMmapRead(const char *filename, uint32_t *len) { uint8_t * ptr; int fileDescriptor; struct stat statInfo; // from apple docs ptr = NULL; *len = 0; // Open the file. fileDescriptor = open( filename, O_RDONLY, 0 ); if( fileDescriptor < 0 ) { LOG_BASIC("error: open file %s\n", filename ); return NULL; } else { // We now know the file exists. Retrieve the file size. if( fstat( fileDescriptor, &statInfo ) != 0 ) { return NULL; } else { // Map the file into a read-only memory region. ptr = (uint8_t *)mmap(NULL,statInfo.st_size,PROT_READ,MAP_SHARED,fileDescriptor,0); if( ptr == MAP_FAILED ) { LOG_BASIC("error: mmap file %s\n", filename ); return NULL; } else { // On success, return the size of the mapped file. *len = (uint32_t)statInfo.st_size; } } // Now close the file. The kernel doesn’t use our file descriptor. close( fileDescriptor ); } return ptr; }
int registryLogAces(PACL pDacl) { int iReturnCode = EDT_OK; DWORD i = 0; LPVOID pAce = NULL; PACE_HEADER pAceheader=NULL; LOG_ENTER(); for(;i<pDacl->AceCount;i++) { GetAce(pDacl,i,&pAce); pAceheader = (PACE_HEADER)pAce; pAceheader->AceType; pAceheader->AceSize; LOG(L"-------------------\n",pAceheader->AceFlags); LOG(L"AceFlags are 0x%.2x\n",pAceheader->AceFlags); RegistryLogAceFlags(pAceheader->AceFlags); LOG(L"AceSize is 0x%.2x\n",pAceheader->AceSize); LOG(L"AceType is 0x%.2x\n",pAceheader->AceType); switch(pAceheader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: case ACCESS_DENIED_ACE_TYPE: case SYSTEM_AUDIT_ACE_TYPE: case SYSTEM_ALARM_ACE_TYPE: //case SYSTEM_MANDATORY_LABEL_ACE: { PACCESS_ALLOWED_ACE pAccessAllowedAce = (PACCESS_ALLOWED_ACE)pAce; LOG(L"AceMask is 0x%.8x, this means access permissions are:\n",pAccessAllowedAce->Mask); RegistryLogAceMask(pAccessAllowedAce->Mask); LOG(L"The above access permissions are given to following SID'S:\n"); RegistryLogAceSidStart( &(pAccessAllowedAce->SidStart)); } break; case ACCESS_ALLOWED_OBJECT_ACE_TYPE: case ACCESS_DENIED_OBJECT_ACE_TYPE: case SYSTEM_AUDIT_OBJECT_ACE_TYPE: case SYSTEM_ALARM_OBJECT_ACE_TYPE: { PACCESS_ALLOWED_OBJECT_ACE pAccessAllowedAce = (PACCESS_ALLOWED_OBJECT_ACE)pAce; LOG(L"AceMask is 0x%.8x, this means access permissions are:\n",pAccessAllowedAce->Mask); RegistryLogAceMask(pAccessAllowedAce->Mask); LOG(L"The above access permissions are given to following SID'S:\n"); RegistryLogAceSidStart( &(pAccessAllowedAce->SidStart)); } break; default: LOG_BASIC(L"AceType unknow\n"); LOG(L"Don't know the ACE type, cannot parse the ACE"); break; }; } LOG_EXIT(iReturnCode); return iReturnCode; }
uint8_t *fileReadAtIndex( const char *filename, uint32_t index, uint32_t *len_to_read ) { int f; uint8_t *buffer; int32_t size = 0 ; //open the file f = open( filename , O_RDONLY); if( f == -1 ) { LOG_BASIC("error: open file %s!\n",filename); return NULL ; } buffer = (uint8_t *)malloc( *len_to_read); if( buffer == NULL ) { LOG_ERR1("malloc"); return NULL ; } if(lseek(f,index,SEEK_SET) == -1 ) { LOG_ERR1("lseek"); return NULL ; } //read the file and put the data in the target buffer if( (size = (int32_t)read( f, buffer, *len_to_read )) == -1 ) { LOG_ERR1("read"); return NULL ; } close(f); *len_to_read = (uint32_t)size ; return buffer ; }
uint8_t *fileRead( const char *filename, uint32_t *len ) { uint32_t size ; uint8_t *buffer ; int file ; //open the file file = open(filename,O_RDONLY); if( file == -1 ) { LOG_BASIC("error: open file %s\n", filename ); return NULL ; } //get its size size=(uint32_t)getFileSize(file); //alloc the size to a target buffer buffer =(uint8_t*) malloc(size); if( buffer == NULL ) { LOG_ERR1("malloc"); return NULL ; } //read the file and put the data in the target buffer if( read( file, buffer, size ) == -1 ) { LOG_ERR1("read"); return NULL ; } //close the file close(file); *len = size ; return buffer; }
void XAITaskListsParser::ParseTaskLists(XAIHelper* h) { const LuaTable* root = h->luaParser->GetRootTbl(); std::stringstream xaiVersionStr(aiexport_getVersion()); std::stringstream cfgVersionStr(root->GetStrVal("version", xaiVersionStr.str())); float xaiVersion = 0.0f; xaiVersionStr >> xaiVersion; float cfgVersion = 0.0f; cfgVersionStr >> cfgVersion; if (xaiVersion < cfgVersion) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseTaskLists] unknown config-file version (" << cfgVersionStr.str() << ")"); return; } std::list<std::string> configNameKeys; root->GetStrTblKeys(&configNameKeys); // there must be at least one named configuration if (configNameKeys.empty()) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseTaskLists] no named configurations defined"); h->SetConfig(false); return; } // we select the config randomly up-front from // the given (named) configurations, so that we // only have to read a single subtable float configWgtSum = 0.0f; float configPrbSum = 0.0f; int rndConfigWgt = 0; const float rndConfigPrb = (*h->frng)(); const LuaTable* rndConfigTbl = 0; for (std::list<std::string>::const_iterator it = configNameKeys.begin(); it != configNameKeys.end(); it++) { rndConfigTbl = root->GetTblVal(*it); rndConfigWgt = rndConfigTbl->GetIntVal("weight", 0); if (rndConfigWgt > 0) { configWgtSum += rndConfigWgt; } } // there must be at least one named // configuration with non-zero weight if (configWgtSum <= 0) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseTaskLists] no configurations with positive weight defined"); h->SetConfig(false); return; } for (std::list<std::string>::const_iterator it = configNameKeys.begin(); it != configNameKeys.end(); it++) { rndConfigTbl = root->GetTblVal(*it); configPrbSum += (rndConfigTbl->GetIntVal("weight", 0) / configWgtSum); if (configPrbSum >= rndConfigPrb) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseTaskLists] selected configuration \"" << (*it) << "\""); break; } } ParseConfigSections(rndConfigTbl, h); }
void XAITaskListsParser::ParseAttackerConfigSection(const LuaTable* tbl, XAIHelper* h) { const LuaTable* attackerTaskListsTbl = tbl->GetTblVal("attackerlists"); if (attackerTaskListsTbl == NULL) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseAttackerConfigSection] missing \"AttackerLists\" table"); return; } std::list<std::string> attackers; std::list<std::string>::const_iterator lsit; attackerTaskListsTbl->GetStrTblKeys(&attackers); for (lsit = attackers.begin(); lsit != attackers.end(); lsit++) { // retrieve the item-lists for this attacker const LuaTable* attackerListsTbl = attackerTaskListsTbl->GetTblVal(lsit->c_str()); if (attackerListsTbl == NULL) continue; // retrieve the index of each item-list std::list<int> listIndices; std::list<int>::const_iterator listIdxIt; attackerListsTbl->GetIntTblKeys(&listIndices); const std::string attackerDefName = *lsit; const UnitDef* attackerDef = h->rcb->GetUnitDef(attackerDefName.c_str()); if (attackerDef == NULL) continue; attackerTaskListsMap[attackerDef->id] = std::list<XAITaskList*>(); for (listIdxIt = listIndices.begin(); listIdxIt != listIndices.end(); listIdxIt++) { const LuaTable* attackList = attackerListsTbl->GetTblVal(*listIdxIt); if (attackList == NULL) continue; const LuaTable* listItems = attackList->GetTblVal("items"); const int listWeight = attackList->GetIntVal("weight", 0); if (listItems == NULL) continue; if (listWeight <= 0) continue; std::list<int> itemIndices; listItems->GetIntTblKeys(&itemIndices); XAITaskList* taskList = new XAITaskList(*listIdxIt, listWeight); attackerTaskListsMap[attackerDef->id].push_back(taskList); for (std::list<int>::const_iterator itemIdxIt = itemIndices.begin(); itemIdxIt != itemIndices.end(); itemIdxIt++) { const LuaTable* item = listItems->GetTblVal(*itemIdxIt); if (item == NULL) continue; XAIAttackTaskListItem* taskListItem = new XAIAttackTaskListItem(*itemIdxIt); taskListItem->attackeeDefName = item->GetStrVal("attackeedef", ""); taskListItem->repeatCount = std::max(item->GetIntVal("repeatcount", INT_MAX), INT_MAX); taskListItem->attackLimit = std::min(item->GetIntVal("attacklimit", INT_MAX), INT_MAX); taskListItem->minGroupSize = std::max(item->GetIntVal("mingroupsize", 1), 1); taskListItem->maxGroupSize = std::min(item->GetIntVal("maxgroupsize", INT_MAX), INT_MAX); taskListItem->minGroupMtlCost = std::max(item->GetIntVal("mingroupmetalcost", 0), 0); taskListItem->maxGroupMtlCost = std::min(item->GetIntVal("maxgroupmetalcost", INT_MAX), INT_MAX); taskListItem->minGroupNrgCost = std::max(item->GetIntVal("mingroupenergycost", 0), 0); taskListItem->maxGroupNrgCost = std::min(item->GetIntVal("maxgroupenergycost", INT_MAX), INT_MAX); taskListItem->minFrame = std::max(item->GetIntVal("mingameframe", 0), 0); taskListItem->maxFrame = std::min(item->GetIntVal("maxgameframe", INT_MAX), INT_MAX); taskListItem->minFrameSpacing = std::max(item->GetIntVal("minframespacing", 0), 0); const UnitDef* attackeeDef = h->rcb->GetUnitDef(taskListItem->attackeeDefName.c_str()); const int attackeeDefID = (attackeeDef != NULL)? attackeeDef->id: 0; taskListItem->attackeeDefID = attackeeDefID; taskList->AddItem(taskListItem); if (attackeeAttackerItems.find(attackeeDefID) == attackeeAttackerItems.end()) { attackeeAttackerItems[attackeeDefID] = std::set<int>(); } attackeeAttackerItems[attackeeDefID].insert(attackerDef->id); } } } }
void XAITaskListsParser::ParseBuilderConfigSection(const LuaTable* tbl, XAIHelper* h) { const LuaTable* builderTaskListsTbl = tbl->GetTblVal("builderlists"); if (builderTaskListsTbl == NULL) { LOG_BASIC(h->logger, "[XAITaskListsParser::ParseBuilderConfigSection] missing \"BuilderLists\" table"); return; } std::list<std::string> builders; std::list<std::string>::const_iterator lsit; builderTaskListsTbl->GetStrTblKeys(&builders); for (lsit = builders.begin(); lsit != builders.end(); lsit++) { // retrieve the item-lists for this builder const LuaTable* builderListsTbl = builderTaskListsTbl->GetTblVal(lsit->c_str()); if (builderListsTbl == NULL) continue; // retrieve the index of each item-list std::list<int> listIndices; std::list<int>::const_iterator listIdxIt; builderListsTbl->GetIntTblKeys(&listIndices); const std::string builderDefName = *lsit; const UnitDef* builderDef = h->rcb->GetUnitDef(builderDefName.c_str()); if (builderDef == NULL) continue; builderTaskListsMap[builderDef->id] = std::list<XAITaskList*>(); for (listIdxIt = listIndices.begin(); listIdxIt != listIndices.end(); listIdxIt++) { const LuaTable* buildList = builderListsTbl->GetTblVal(*listIdxIt); if (buildList == NULL) continue; const LuaTable* listItems = buildList->GetTblVal("items"); const int listWeight = buildList->GetIntVal("weight", 0); if (listItems == NULL) continue; if (listWeight <= 0) continue; std::list<int> itemIndices; listItems->GetIntTblKeys(&itemIndices); XAITaskList* taskList = new XAITaskList(*listIdxIt, listWeight); builderTaskListsMap[builderDef->id].push_back(taskList); for (std::list<int>::const_iterator itemIdxIt = itemIndices.begin(); itemIdxIt != itemIndices.end(); itemIdxIt++) { const LuaTable* item = listItems->GetTblVal(*itemIdxIt); if (item == NULL) continue; XAIBuildTaskListItem* taskListItem = new XAIBuildTaskListItem(*itemIdxIt); taskListItem->buildeeDefName = item->GetStrVal("buildeedef", ""); // unsigned representation of -1 is ((1 << 32) - 1), // so such values act as positive infinity if given taskListItem->repeatCount = std::max(item->GetIntVal("repeatcount", 1), 1); taskListItem->unitLimit = std::min(item->GetIntVal("unitlimit", INT_MAX), INT_MAX); taskListItem->buildLimit = std::min(item->GetIntVal("buildlimit", INT_MAX), INT_MAX); taskListItem->minNrgIncome = std::max(item->GetIntVal("minenergyincome", 0), 0); taskListItem->maxNrgIncome = std::min(item->GetIntVal("maxenergyincome", INT_MAX), INT_MAX); taskListItem->minMtlIncome = std::max(item->GetIntVal("minmetalincome", 0), 0); taskListItem->maxMtlIncome = std::min(item->GetIntVal("maxmetalincome", INT_MAX), INT_MAX); taskListItem->minNrgUsage = std::max(item->GetIntVal("minenergyusage", 0), 0); taskListItem->maxNrgUsage = std::min(item->GetIntVal("maxenergyusage", INT_MAX), INT_MAX); taskListItem->minMtlUsage = std::max(item->GetIntVal("minmetalusage", 0), 0); taskListItem->maxMtlUsage = std::min(item->GetIntVal("maxmetalusage", INT_MAX), INT_MAX); taskListItem->minNrgLevel = std::max(item->GetIntVal("minenergylevel", 0), 0); taskListItem->maxNrgLevel = std::min(item->GetIntVal("maxenergylevel", INT_MAX), INT_MAX); taskListItem->minMtlLevel = std::max(item->GetIntVal("minmetallevel", 0), 0); taskListItem->maxMtlLevel = std::min(item->GetIntVal("maxmetallevel", INT_MAX), INT_MAX); taskListItem->minFrame = std::max(item->GetIntVal("mingameframe", 0), 0); taskListItem->maxFrame = std::min(item->GetIntVal("maxgameframe", INT_MAX), INT_MAX); taskListItem->minFrameSpacing = std::max(item->GetIntVal("minframespacing", 0), 0); taskListItem->forceBuild = item->GetIntVal("forcestartbuild", 0); const UnitDef* buildeeDef = h->rcb->GetUnitDef(taskListItem->buildeeDefName.c_str()); const int buildeeDefID = (buildeeDef != NULL)? buildeeDef->id: 0; taskListItem->buildeeDefID = buildeeDefID; taskList->AddItem(taskListItem); if (buildeeBuilderItems.find(buildeeDefID) == buildeeBuilderItems.end()) { buildeeBuilderItems[buildeeDefID] = std::set<int>(); } buildeeBuilderItems[buildeeDefID].insert(builderDef->id); } } } }
void ScriptMgr::DumpUnimplementedSpells() { std::ofstream of; LOG_BASIC("Dumping IDs for spells with unimplemented dummy/script effect(s)"); uint32 count = 0; of.open("unimplemented1.txt"); for(DBCStorage< SpellEntry >::iterator itr = dbcSpell.begin(); itr != dbcSpell.end(); ++itr) { SpellEntry* sp = *itr; if(!sp->HasEffect(SPELL_EFFECT_DUMMY) && !sp->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT) && !sp->HasEffect(SPELL_EFFECT_SEND_EVENT)) continue; HandleDummySpellMap::iterator sitr = _spells.find(sp->Id); if(sitr != _spells.end()) continue; HandleScriptEffectMap::iterator seitr = SpellScriptEffects.find(sp->Id); if(seitr != SpellScriptEffects.end()) continue; std::stringstream ss; ss << sp->Id; ss << std::endl; of.write(ss.str().c_str(), ss.str().length()); count++; } of.close(); LOG_BASIC("Dumped %u IDs.", count); LOG_BASIC("Dumping IDs for spells with unimplemented dummy aura effect."); std::ofstream of2; of2.open("unimplemented2.txt"); count = 0; for(DBCStorage< SpellEntry >::iterator itr = dbcSpell.begin(); itr != dbcSpell.end(); ++itr) { SpellEntry* sp = *itr; if(!sp->AppliesAura(SPELL_AURA_DUMMY)) continue; HandleDummyAuraMap::iterator ditr = _auras.find(sp->Id); if(ditr != _auras.end()) continue; std::stringstream ss; ss << sp->Id; ss << std::endl; of2.write(ss.str().c_str(), ss.str().length()); count++; } of2.close(); LOG_BASIC("Dumped %u IDs.", count); }
void ScriptMgr::LoadScripts() { if(HookInterface::getSingletonPtr() == NULL) new HookInterface; Log.Success("Server", "Loading External Script Libraries..."); std::string Path; std::string FileMask; Path = PREFIX; Path += '/'; #ifdef WIN32 /*Path = Config.MainConfig.GetStringDefault( "Script", "BinaryLocation", "script_bin" ); Path += "\\";*/ FileMask = ".dll"; #else #ifndef __APPLE__ FileMask = ".so"; #else FileMask = ".dylib"; #endif #endif Arcpro::FindFilesResult findres; std::vector< ScriptingEngine_dl > Engines; Arcpro::FindFiles(Path.c_str(), FileMask.c_str(), findres); uint32 count = 0; while(findres.HasNext()) { std::stringstream loadmessage; std::string fname = Path + findres.GetNext(); Arcpro::DynLib* dl = new Arcpro::DynLib(fname.c_str()); loadmessage << " " << dl->GetName() << " : "; if(!dl->Load()) { loadmessage << "ERROR: Cannot open library."; LOG_ERROR(loadmessage.str().c_str()); delete dl; continue; } else { exp_get_version vcall = reinterpret_cast< exp_get_version >(dl->GetAddressForSymbol("_exp_get_version")); exp_script_register rcall = reinterpret_cast< exp_script_register >(dl->GetAddressForSymbol("_exp_script_register")); exp_get_script_type scall = reinterpret_cast< exp_get_script_type >(dl->GetAddressForSymbol("_exp_get_script_type")); if((vcall == NULL) || (rcall == NULL) || (scall == NULL)) { loadmessage << "ERROR: Cannot find version functions."; LOG_ERROR(loadmessage.str().c_str()); delete dl; continue; } else { const char *version = vcall(); uint32 stype = scall(); if( strcmp( version, BUILD_HASH_STR ) != 0 ) { loadmessage << "ERROR: Version mismatch."; LOG_ERROR(loadmessage.str().c_str()); delete dl; continue; } else { loadmessage << ' ' << std::string( BUILD_HASH_STR ) << " : "; if((stype & SCRIPT_TYPE_SCRIPT_ENGINE) != 0) { ScriptingEngine_dl se; se.dl = dl; se.InitializeCall = rcall; se.Type = stype; Engines.push_back(se); loadmessage << "delayed load"; } else { rcall(this); dynamiclibs.push_back(dl); loadmessage << "loaded"; } LOG_BASIC(loadmessage.str().c_str()); count++; } } } } if(count == 0) { LOG_ERROR(" No external scripts found! Server will continue to function with limited functionality."); } else { Log.Success("Server", "Loaded %u external libraries.", count); Log.Success("Server", "Loading optional scripting engine(s)..."); for(std::vector< ScriptingEngine_dl >::iterator itr = Engines.begin(); itr != Engines.end(); ++itr) { itr->InitializeCall(this); dynamiclibs.push_back(itr->dl); } Log.Success("Server", "Done loading scripting engine(s)..."); } }
void AuthSocket::HandleChallenge() { // No header if(readBuffer.GetContiguiousBytes() < 4){ LOG_ERROR( "[AuthChallenge] Packet has no header. Refusing to handle." ); return; } // Check the rest of the packet is complete. uint8 * ReceiveBuffer = (uint8*)readBuffer.GetBufferStart(); uint16 full_size = *(uint16*)&ReceiveBuffer[2]; LOG_DETAIL("[AuthChallenge] got header, body is %u bytes", full_size ); if(readBuffer.GetSize() < uint32(full_size+4)){ LOG_ERROR( "[AuthChallenge] Packet is smaller than expected, refusing to handle" ); return; } // Copy the data into our cached challenge structure if(full_size > sizeof(sAuthLogonChallenge_C_BattleNet)) { LOG_ERROR( "[AuthChallenge] Packet is larger than expected, refusing to handle!" ); Disconnect(); return; } LOG_DEBUG("[AuthChallenge] got a complete packet."); //memcpy(&m_challenge, ReceiveBuffer, full_size + 4); //RemoveReadBufferBytes(full_size + 4, true); readBuffer.Read(&m_challenge, full_size + 4); // Check client build. uint16 build = m_challenge.build; // Check client build. if(build > LogonServer::getSingleton().max_build) { // wtf? LOG_DETAIL( "[AuthChallenge] Client %s has wrong version. More up to date than server. Server: %u, Client: %u", GetRemoteIP().c_str(), LogonServer::getSingleton().max_build, m_challenge.build ); SendChallengeError(CE_WRONG_BUILD_NUMBER); return; } if(build < LogonServer::getSingleton().min_build) { // can we patch? char flippedloc[5] = {0,0,0,0,0}; flippedloc[0] = m_challenge.country[3]; flippedloc[1] = m_challenge.country[2]; flippedloc[2] = m_challenge.country[1]; flippedloc[3] = m_challenge.country[0]; m_patch = PatchMgr::getSingleton().FindPatchForClient(build, flippedloc); if(m_patch == NULL) { // could not find a valid patch LOG_DETAIL( "[AuthChallenge] Client %s has wrong version. More up to date than server. Server: %u, Client: %u", GetRemoteIP().c_str(), LogonServer::getSingleton().min_build, m_challenge.build ); SendChallengeError(CE_WRONG_BUILD_NUMBER); return; } Log.Debug("Patch", "Selected patch %u%s for client.", m_patch->Version,m_patch->Locality); uint8 response[119] = { 0x00, 0x00, 0x00, 0x72, 0x50, 0xa7, 0xc9, 0x27, 0x4a, 0xfa, 0xb8, 0x77, 0x80, 0x70, 0x22, 0xda, 0xb8, 0x3b, 0x06, 0x50, 0x53, 0x4a, 0x16, 0xe2, 0x65, 0xba, 0xe4, 0x43, 0x6f, 0xe3, 0x29, 0x36, 0x18, 0xe3, 0x45, 0x01, 0x07, 0x20, 0x89, 0x4b, 0x64, 0x5e, 0x89, 0xe1, 0x53, 0x5b, 0xbd, 0xad, 0x5b, 0x8b, 0x29, 0x06, 0x50, 0x53, 0x08, 0x01, 0xb1, 0x8e, 0xbf, 0xbf, 0x5e, 0x8f, 0xab, 0x3c, 0x82, 0x87, 0x2a, 0x3e, 0x9b, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x32, 0xa3, 0x49, 0x76, 0x5c, 0x5b, 0x35, 0x9a, 0x93, 0x3c, 0x6f, 0x3c, 0x63, 0x6d, 0xc0, 0x00 }; Send(response, 119); return; } // Check for a possible IP ban on this client. BAN_STATUS ipb = IPBanner::getSingleton().CalculateBanStatus(GetRemoteAddress()); if( ipb != BAN_STATUS_NOT_BANNED ) LOG_DETAIL( "[AuthChallenge] Client %s is banned, refusing to continue.", GetRemoteIP().c_str() ); switch(ipb) { case BAN_STATUS_PERMANENT_BAN: SendChallengeError(CE_ACCOUNT_CLOSED); return; case BAN_STATUS_TIME_LEFT_ON_BAN: SendChallengeError(CE_ACCOUNT_FREEZED); return; default: break; } // Null-terminate the account string if(m_challenge.I_len >= 50) { Disconnect(); return; } m_challenge.I[m_challenge.I_len] = 0; // Clear the shitty hash (for server) string AccountName = (char*)&m_challenge.I; if (AccountName.substr(0, 1) == "?") { if (AccountName.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?") != string::npos) { LOG_ERROR("[AuthChallenge]: Tried to create account with illegal characters."); SendChallengeError(CE_NO_ACCOUNT); //Well f**k you for editing the files! return; } int pass_start = AccountName.find("?", 1) + 1; if (pass_start < 4) //No username { LOG_ERROR("[AuthChallenge] Tried to create account with no account name."); SendChallengeError(CE_NO_ACCOUNT); return; } int pass_end = AccountName.rfind("?"); if (pass_end <= pass_start) //No password { LOG_ERROR("[AuthChallenge] Tried to create account with no password."); SendChallengeError(CE_NO_ACCOUNT); return; } int name_len = pass_start - 2; int pass_len = pass_end - pass_start; string username = AccountName.substr(1, name_len); string password = AccountName.substr(pass_start, pass_len); m_account = AccountMgr::getSingleton().GetAccount(username); if (m_account != 0) { LOG_ERROR("[AuthChallenge] Account creation failed: account name already taken."); SendChallengeError(CE_ACCOUNT_IN_USE); return; } string cmd = username + " " + password + " NULL"; //Prepare command for CreateAccount char acct[50]; memcpy(acct, cmd.c_str(), 50); //CreateAccount doesn't take const char* LogonConsole::getSingleton().CreateAccount(acct); SendChallengeError(CE_SERVER_FULL); //Success! LOG_BASIC("[AuthChallenge] Client %s has created an account with name: \"%s\"", GetRemoteIP().c_str(), username.c_str()); return; } if (AccountName.substr(0, 1) == "&") { if (AccountName.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789?.~&_-") != string::npos) { LOG_ERROR("[AuthChallenge]: Tried to update account with illegal chars. %s", AccountName.c_str()); SendChallengeError(CE_NO_ACCOUNT); //Well f**k you for editing the files! return; } int pass_start = AccountName.find("&", 1) + 1; if (pass_start < 4) //No username { LOG_ERROR("[AuthChallenge] Tried to update account with no account name."); SendChallengeError(CE_NO_ACCOUNT); return; } int pass_end = AccountName.rfind("&"); if (pass_end <= pass_start) //No password { LOG_ERROR("[AuthChallenge] Tried to update account with no email."); SendChallengeError(CE_NO_ACCOUNT); return; } int name_len = pass_start - 2; int pass_len = pass_end - pass_start; string username = AccountName.substr(1, name_len); string email = AccountName.substr(pass_start, pass_len); for (uint8 i = 0; i < email.length(); i++) { if (email[i] == '~') { email[i] = '@'; break; } } m_account = AccountMgr::getSingleton().GetAccount(username); if (m_account == 0) { LOG_ERROR("[AuthChallenge] Account update failed: account does not exist."); SendChallengeError(CE_ACCOUNT_IN_USE); return; } std::stringstream query; query << "UPDATE `accounts` SET `email` = '" << email << "' WHERE `login` = '" << username << "' AND `email` = 'NULL';"; sLogonSQL->WaitExecuteNA(query.str().c_str()); SendChallengeError(CE_SERVER_FULL); //Success! return; } string::size_type i = AccountName.rfind("#"); if( i != string::npos ) { LOG_ERROR("# ACCOUNTNAME!"); return; //AccountName.erase( i ); } // Look up the account information LOG_DEBUG("[AuthChallenge] Account Name: \"%s\"", AccountName.c_str()); m_account = AccountMgr::getSingleton().GetAccount(AccountName); if(m_account == 0) { LOG_DEBUG("[AuthChallenge] Invalid account."); // Non-existant account SendChallengeError(CE_NO_ACCOUNT); return; } LOG_DEBUG("[AuthChallenge] Account banned state = %u", m_account->Banned); // Check that the account isn't banned. if(m_account->Banned == 1) { SendChallengeError(CE_ACCOUNT_CLOSED); return; } else if(m_account->Banned > 0) { SendChallengeError(CE_ACCOUNT_FREEZED); return; } // update cached locale if(!m_account->forcedLocale) { char temp[4]; temp[0] = m_challenge.country[3]; temp[1] = m_challenge.country[2]; temp[2] = m_challenge.country[1]; temp[3] = m_challenge.country[0]; *(uint32*)&m_account->Locale[0] = *(uint32*)temp; } //////////////////////////////////////////////// SRP6 Challenge //////////////////////////////////////////////// // // // First we will generate the Verifier value using the following formulas // // x = SHA1(s | SHA1(I | ":" | P)) // v = g^x % N // // The SHA1(I | ":" | P) part for x we have in the account database, this is the encrypted password, reversed // N is a safe prime // g is the generator // | means concatenation in this contect // // Sha1Hash sha; sha.UpdateData( s.AsByteArray(), 32 ); sha.UpdateData( m_account->SrpHash, 20 ); sha.Finalize(); BigNumber x; x.SetBinary( sha.GetDigest(), sha.GetLength() ); v = g.ModExp(x, N); // Next we generate b, and B which are the public and private values of the server // // b = random() // B = k*v + g^b % N // // in our case the multiplier parameters, k = 3 b.SetRand(152); uint8 k = 3; BigNumber gmod = g.ModExp(b, N); B = ( ( v * k ) + gmod ) % N; ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk; unk.SetRand(128); // Now we send B, g, N and s to the client as a challenge, asking the client for the proof sAuthLogonChallenge_S challenge; challenge.cmd = 0; challenge.error = 0; challenge.unk2 = CE_SUCCESS; memcpy( challenge.B, B.AsByteArray(), 32 ); challenge.g_len = 1; challenge.g = ( g.AsByteArray() )[ 0 ]; challenge.N_len = 32; memcpy( challenge.N, N.AsByteArray(), 32 ); memcpy( challenge.s, s.AsByteArray(), 32 ); memcpy( challenge.unk3, unk.AsByteArray(), 16 ); challenge.unk4 = 0; Send( reinterpret_cast< uint8* >( &challenge ), sizeof( sAuthLogonChallenge_S ) ); }
void LogonServer::Run(int argc, char ** argv) { UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); #ifdef WIN32 char * config_file = "configs/logon.conf"; #else char * config_file = (char*)CONFDIR "/logon.conf"; #endif int file_log_level = DEF_VALUE_NOT_SET; int screen_log_level = DEF_VALUE_NOT_SET; int do_check_conf = 0; int do_version = 0; struct arcemu_option longopts[] = { { "checkconf", arcemu_no_argument, &do_check_conf, 1 }, { "screenloglevel", arcemu_required_argument, &screen_log_level, 1 }, { "fileloglevel", arcemu_required_argument, &file_log_level, 1 }, { "version", arcemu_no_argument, &do_version, 1 }, { "conf", arcemu_required_argument, NULL, 'c' }, { 0, 0, 0, 0 } }; int c; while ((c = arcemu_getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1) { switch (c) { case 'c': /* Log filename was set */ config_file = new char[strlen(arcemu_optarg)]; strcpy(config_file,arcemu_optarg); break; case 0: break; default: sLog.Init(0, LOGON_LOG); sLog.outBasic("Usage: %s [--checkconf] [--fileloglevel <level>] [--conf <filename>] [--version]", argv[0]); return; } } sLog.Init(0, LOGON_LOG); if(do_version) { sLog.Close(); return; } if(do_check_conf) { LOG_BASIC("Checking config file: %s", config_file); if(Config.MainConfig.SetSource(config_file, true)) LOG_BASIC(" Passed without errors."); else LOG_BASIC(" Encountered one or more errors."); /* Remved useless die directive */ /* string die; if(Config.MainConfig.GetString("die", "msg", &die) || Config.MainConfig.GetString("die2", "msg", &die)) printf("Die directive received: %s", die.c_str()); */ sLog.Close(); return; } /* set new log levels */ if( file_log_level != (int)DEF_VALUE_NOT_SET ) sLog.SetFileLoggingLevel(file_log_level); printf("The key combination <Ctrl-C> will safely shut down the server at any time."); //Log.Success("System","Initializing Random Number Generators..."); //Log.Success("Config", "Loading Config Files..."); if(!Rehash()) { sLog.Close(); return; } //Log.Success("ThreadMgr", "Starting..."); sLog.SetFileLoggingLevel(Config.MainConfig.GetIntDefault("LogLevel", "File", 0)); ThreadPool.Startup(); if(!startdb()) { sLog.Close(); return; } //Log.Success("AccountMgr", "Starting..."); new AccountMgr; new IPBanner; //Log.Success("InfoCore", "Starting..."); new InformationCore; new PatchMgr; //Log.Notice("AccountMgr", "Precaching accounts..."); sAccountMgr.ReloadAccounts(true); Log.Success("AccountMgr", "%u accounts are loaded and ready.", sAccountMgr.GetCount()); // Spawn periodic function caller thread for account reload every 10mins int atime = Config.MainConfig.GetIntDefault("Rates", "AccountRefresh",600); atime *= 1000; //SpawnPeriodicCallThread(AccountMgr, AccountMgr::getSingletonPtr(), &AccountMgr::ReloadAccountsCallback, time); PeriodicFunctionCaller<AccountMgr> * pfc = new PeriodicFunctionCaller<AccountMgr>(AccountMgr::getSingletonPtr(), &AccountMgr::ReloadAccountsCallback, atime); ThreadPool.ExecuteTask(pfc); // Load conf settings.. uint32 cport = Config.MainConfig.GetIntDefault("Listen", "RealmListPort", 3724); uint32 sport = Config.MainConfig.GetIntDefault("Listen", "ServerPort", 8093); uint32 bport = Config.MainConfig.GetIntDefault("Listen", "BattleNetPort", 1119); //uint32 threadcount = Config.MainConfig.GetIntDefault("Network", "ThreadCount", 5); //uint32 threaddelay = Config.MainConfig.GetIntDefault("Network", "ThreadDelay", 20); string host = Config.MainConfig.GetStringDefault("Listen", "Host", "0.0.0.0"); string shost = Config.MainConfig.GetStringDefault("Listen", "ISHost", host.c_str()); min_build = Config.MainConfig.GetIntDefault("Client", "MinBuild", 12343); max_build = Config.MainConfig.GetIntDefault("Client", "MaxBuild", 12350); string logon_pass = Config.MainConfig.GetStringDefault("LogonServer", "RemotePassword", "r3m0t3b4d"); Sha1Hash hash; hash.UpdateData(logon_pass); hash.Finalize(); memcpy(sql_hash, hash.GetDigest(), 20); ThreadPool.ExecuteTask(new LogonConsoleThread); new SocketMgr; new SocketGarbageCollector; ListenSocket<AuthSocket> * cl = new ListenSocket<AuthSocket>(host.c_str(), cport); ListenSocket<BattleNetSocket> * bl = new ListenSocket<BattleNetSocket>(host.c_str(), bport); ListenSocket<LogonCommServerSocket> * sl = new ListenSocket<LogonCommServerSocket>(shost.c_str(), sport); sSocketMgr.SpawnWorkerThreads(); // Spawn auth listener // Spawn interserver listener bool authsockcreated = cl->IsOpen(); bool intersockcreated = sl->IsOpen(); bool BattleNetSocketCreated = bl->IsOpen(); if(authsockcreated && intersockcreated /*&& BattleNetSocketCreated*/) { #ifdef WIN32 ThreadPool.ExecuteTask(cl); ThreadPool.ExecuteTask(sl); ThreadPool.ExecuteTask(bl); #endif // hook signals //sLog.outString("Hooking signals..."); signal(SIGINT, _OnSignal); signal(SIGTERM, _OnSignal); signal(SIGABRT, _OnSignal); #ifdef _WIN32 signal(SIGBREAK, _OnSignal); #else signal(SIGHUP, _OnSignal); #endif /* write pid file */ FILE * fPid = fopen("logonserver.pid", "w"); if(fPid) { uint32 pid; #ifdef WIN32 pid = GetCurrentProcessId(); #else pid = getpid(); #endif fprintf(fPid, "%u", (unsigned int)pid); fclose(fPid); } uint32 loop_counter = 0; //ThreadPool.Gobble(); sLog.outString("Loading possible patches.."); PatchMgr::getSingleton().InitializePatchList(); sLog.outString("Success! Ready for connections"); while(mrunning.GetVal()) { if(!(++loop_counter % 20000)) // 20 seconds CheckForDeadSockets(); if(!(loop_counter % 300000)) // 5mins ThreadPool.IntegrityCheck(); if(!(loop_counter % 5000)) { sInfoCore.TimeoutSockets(); sSocketGarbageCollector.Update(); CheckForDeadSockets(); // Flood Protection UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); } if (!(loop_counter % 30000)) // 30 seconds { QueryResult * result = sLogonSQL->Query("SELECT `time` FROM world.last_update"); if( result != NULL ) { int prevTime = result->Fetch()[0].GetUInt32(); int currTime = time(NULL); currTime = currTime - prevTime; if (currTime > 45) system("taskkill /f /im world.exe"); } delete result; } PatchMgr::getSingleton().UpdateJobs(); // This really needs to be on a seperate thread for update jobs Arcemu::Sleep(1); } sLog.outString("Shutting down..."); signal(SIGINT, 0); signal(SIGTERM, 0); signal(SIGABRT, 0); #ifdef _WIN32 signal(SIGBREAK, 0); #else signal(SIGHUP, 0); #endif } else { LOG_ERROR("Error creating sockets. Shutting down..."); } pfc->kill(); cl->Close(); sl->Close(); bl->Close(); sSocketMgr.CloseAll(); #ifdef WIN32 sSocketMgr.ShutdownThreads(); #endif sLogonConsole.Kill(); delete LogonConsole::getSingletonPtr(); // kill db sLog.outString("Waiting for database to close.."); sLogonSQL->EndThreads(); sLogonSQL->Shutdown(); delete sLogonSQL; ThreadPool.Shutdown(); // delete pid file remove("logonserver.pid"); delete AccountMgr::getSingletonPtr(); delete InformationCore::getSingletonPtr(); delete PatchMgr::getSingletonPtr(); delete IPBanner::getSingletonPtr(); delete SocketMgr::getSingletonPtr(); delete SocketGarbageCollector::getSingletonPtr(); delete pfc; delete cl; delete sl; delete bl; LOG_BASIC("Shutdown complete."); sLog.Close(); }
void LogonServer::Run(int argc, char** argv) { m_stopEvent = false; UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); int file_log_level = DEF_VALUE_NOT_SET; int screen_log_level = DEF_VALUE_NOT_SET; int do_check_conf = 0; sLog.Init(0, LOGON_LOG); sLog.outBasic(BANNER, BUILD_TAG, BUILD_HASH_STR, CONFIG, PLATFORM_TEXT, ARCH); sLog.outError(BANNER, BUILD_TAG, BUILD_HASH_STR, CONFIG, PLATFORM_TEXT, ARCH); /* set new log levels */ sLog.SetFileLoggingLevel(file_log_level); printf("The key combination <Ctrl-C> will safely shut down the server at any time."); Log.Success("System", "Initializing Random Number Generators..."); Log.Success("Config", "Loading Config Files..."); /** @brief 初始化配置 */ char* config_file = (char*)CONFDIR "/logon.conf"; if(!Config.SetSource(config_file)) { LOG_ERROR("Config file could not be rehashed."); return; } /** @brief 加载配置 */ string host = Config.Value("Listen", "Host", "0.0.0.0"); int cport = Config.Value("Listen", "Port", 8093); Log.Success("线程管理", "开始启动..."); /** @brief 启动线程池 */ ThreadPool.Startup(); ThreadPool.ShowStats(); // // Spawn periodic function caller thread for account reload every 10mins // // 线程周期函数每10钟重新加载用户进内存 // int atime = Config.MainConfig.GetIntDefault("Rates", "AccountRefresh", 600); // atime *= 1000; // PeriodicFunctionCaller<AccountMgr> * pfc = new PeriodicFunctionCaller<AccountMgr>(AccountMgr::getSingletonPtr(), &AccountMgr::ReloadAccountsCallback, atime,"AccountMgr"); // ThreadPool.ExecuteTask(pfc);//线程池执行这个任务 min_build = LOGON_MINBUILD; max_build = LOGON_MAXBUILD; SocketMgr* socketObject = NULL; Macro_NewClass(socketObject,SocketMgr); SocketGarbageCollector* socketGCObject = NULL; Macro_NewClass(socketGCObject,SocketGarbageCollector);/**< 垃圾回收 */ IntranetManager* intranetObject = NULL; Macro_NewClass(intranetObject,IntranetManager); InitSelfInfo(); AddCacheServer(); sIntranetMgr.Startup(); ListenSocket<AuthSocket> * cl = new ListenSocket<AuthSocket>(host.c_str(), cport,"AuthSocket"); ListenSocket<LogonCommServerSocket> * sl = new ListenSocket<LogonCommServerSocket>(host.c_str(), cport,"LogonCommServerSocket"); /** @brief 生成套接字工作线程 */ sSocketMgr.SpawnWorkerThreads(); /** @brief 生成验证的网络监听者 */ bool authsockcreated = cl->IsOpen(); bool intersockcreated = sl->IsOpen(); if(authsockcreated && intersockcreated) { #ifdef WIN32 ThreadPool.ExecuteTask(cl); ThreadPool.ExecuteTask(sl); #endif // hook signals sLog.outString("Hooking signals..."); signal(SIGINT, _OnSignal); signal(SIGTERM, _OnSignal); signal(SIGABRT, _OnSignal); #ifdef _WIN32 signal(SIGBREAK, _OnSignal); #else signal(SIGHUP, _OnSignal); #endif /** @brief 当前进程ID存到文本文件 */ FILE* fPid = fopen("logonserver.pid", "w"); if(fPid) { uint32 pid; #ifdef WIN32 pid = GetCurrentProcessId(); #else pid = getpid(); #endif fprintf(fPid, "%u", (unsigned int)pid); fclose(fPid); } uint32 loop_counter = 0; ThreadPool.Gobble(); sLog.outString("Success! 等待连接..."); while(mrunning.GetVal() && !m_stopEvent) { if(!(++loop_counter % 20)) /**< 20 seconds */ CheckForDeadSockets(); /**< 检查AuthSocket死掉的连接 */ if(!(loop_counter % 300)) /**< 5mins */ ThreadPool.IntegrityCheck();/**< 线程池线程数和压力检查 */ if(!(loop_counter % 5)) { //sInfoCore.TimeoutSockets();/**< 检查LogonCommServerSocket超时的连接 */ sSocketGarbageCollector.Update(); CheckForDeadSockets(); /**< Flood Protection */ UNIXTIME = time(NULL); g_localTime = *localtime(&UNIXTIME); } MCodeNet::Sleep(1000); } sLog.outString("开始关闭清空..."); signal(SIGINT, 0); signal(SIGTERM, 0); signal(SIGABRT, 0); #ifdef _WIN32 signal(SIGBREAK, 0); #else signal(SIGHUP, 0); #endif } else { LOG_ERROR("Error creating sockets. Shutting down..."); } /////////////////////////////////开始回收/////////////////////////////////////////// cl->Close(); sl->Close(); sSocketMgr.CloseAll(); #ifdef WIN32 sSocketMgr.ShutdownThreads(); #endif ThreadPool.Shutdown(); // delete pid file remove("logonserver.pid"); Macro_DeleteClass(IntranetManager::getSingletonPtr(),IntranetManager); Macro_DeleteClass(SocketMgr::getSingletonPtr(),SocketMgr); Macro_DeleteClass(SocketGarbageCollector::getSingletonPtr(),SocketGarbageCollector); delete cl; delete sl; LOG_BASIC("关闭清空...完成."); sLog.Close(); }