diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 8d7e412c2..a7e6836ff 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -4583,13 +4583,33 @@ bool ChatHandler::HandleShutDownCommand(const char* args) } else { - int32 time = atoi(args); + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); ///- Prevent interpret wrong arg value as 0 secs shutdown time - if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0) + if(time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0) return false; - sWorld.ShutdownServ(time); + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, 0, exitcode); + } + else + sWorld.ShutdownServ(time,0,SHUTDOWN_EXIT_CODE); } return true; } @@ -4605,13 +4625,33 @@ bool ChatHandler::HandleRestartCommand(const char* args) } else { - int32 time = atoi(args); + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); ///- Prevent interpret wrong arg value as 0 secs shutdown time - if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0) + if(time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0) return false; - sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART); + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART, exitcode); + } + else + sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); } return true; } @@ -4627,13 +4667,33 @@ bool ChatHandler::HandleIdleRestartCommand(const char* args) } else { - int32 time = atoi(args); + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); ///- Prevent interpret wrong arg value as 0 secs shutdown time - if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0) + if(time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0) return false; - sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART+SHUTDOWN_MASK_IDLE); + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE, exitcode); + } + else + sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE,RESTART_EXIT_CODE); } return true; } @@ -4649,13 +4709,33 @@ bool ChatHandler::HandleIdleShutDownCommand(const char* args) } else { - int32 time = atoi(args); + char* time_str = strtok ((char*) args, " "); + char* exitcode_str = strtok (NULL, ""); + + int32 time = atoi (time_str); ///- Prevent interpret wrong arg value as 0 secs shutdown time - if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0) + if(time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0) return false; - sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE); + if (exitcode_str) + { + int32 exitcode = atoi (exitcode_str); + + // Handle atoi() errors + if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0')) + return false; + + // Exit code should be in range of 0-125, 126-255 is used + // in many shells for their own return codes and code > 255 + // is not supported in many others + if (exitcode < 0 || exitcode > 125) + return false; + + sWorld.ShutdownServ (time, SHUTDOWN_MASK_IDLE, exitcode); + } + else + sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE,SHUTDOWN_EXIT_CODE); } return true; } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index a2c865922..01b20bddf 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -5179,7 +5179,7 @@ uint32 ObjectMgr::GenerateArenaTeamId() if(m_arenaTeamId>=0xFFFFFFFF) { sLog.outError("Arena team ids overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_arenaTeamId; } @@ -5190,7 +5190,7 @@ uint32 ObjectMgr::GenerateGuildId() if(m_guildId>=0xFFFFFFFF) { sLog.outError("Guild ids overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_guildId; } @@ -5201,7 +5201,7 @@ uint32 ObjectMgr::GenerateAuctionID() if(m_auctionid>=0xFFFFFFFF) { sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_auctionid; } @@ -5212,7 +5212,7 @@ uint32 ObjectMgr::GenerateMailID() if(m_mailid>=0xFFFFFFFF) { sLog.outError("Mail ids overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_mailid; } @@ -5223,7 +5223,7 @@ uint32 ObjectMgr::GenerateItemTextID() if(m_ItemTextId>=0xFFFFFFFF) { sLog.outError("Item text ids overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_ItemTextId; } @@ -5251,7 +5251,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiItemGuid>=0xFFFFFFFF) { sLog.outError("Item guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiItemGuid; case HIGHGUID_UNIT: @@ -5259,7 +5259,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiCreatureGuid>=0x00FFFFFF) { sLog.outError("Creature guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiCreatureGuid; case HIGHGUID_PET: @@ -5267,7 +5267,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiPetGuid>=0x00FFFFFF) { sLog.outError("Pet guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiPetGuid; case HIGHGUID_PLAYER: @@ -5275,7 +5275,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiCharGuid>=0xFFFFFFFF) { sLog.outError("Players guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiCharGuid; case HIGHGUID_GAMEOBJECT: @@ -5283,7 +5283,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiGoGuid>=0x00FFFFFF) { sLog.outError("Gameobject guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiGoGuid; case HIGHGUID_CORPSE: @@ -5291,7 +5291,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiCorpseGuid>=0xFFFFFFFF) { sLog.outError("Corpse guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiCorpseGuid; case HIGHGUID_DYNAMICOBJECT: @@ -5299,7 +5299,7 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh) if(m_hiDoGuid>=0xFFFFFFFF) { sLog.outError("DynamicObject guid overflow!! Can't continue, shutting down server. "); - sWorld.m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); } return m_hiDoGuid; default: diff --git a/src/game/World.cpp b/src/game/World.cpp index 92bc56548..546cdbbf6 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -62,6 +62,7 @@ INSTANTIATE_SINGLETON_1( World ); volatile bool World::m_stopEvent = false; +uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; volatile uint32 World::m_worldLoopCounter = 0; float World::m_MaxVisibleDistanceForCreature = DEFAULT_VISIBILITY_DISTANCE; @@ -2315,13 +2316,13 @@ void World::_UpdateGameTime() m_gameTime = thisTime; ///- if there is a shutdown timer - if(m_ShutdownTimer > 0 && elapsed > 0) + if(!m_stopEvent && m_ShutdownTimer > 0 && elapsed > 0) { ///- ... and it is overdue, stop the world (set m_stopEvent) if( m_ShutdownTimer <= elapsed ) { if(!(m_ShutdownMask & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0) - m_stopEvent = true; + m_stopEvent = true; // exist code already set else m_ShutdownTimer = 1; // minimum timer value to wait idle state } @@ -2336,15 +2337,20 @@ void World::_UpdateGameTime() } /// Shutdown the server -void World::ShutdownServ(uint32 time, uint32 options) +void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode) { + // ignore if server shutdown at next tick + if(m_stopEvent) + return; + m_ShutdownMask = options; + m_ExitCode = exitcode; ///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions) if(time==0) { if(!(options & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0) - m_stopEvent = true; + m_stopEvent = true; // exist code already set else m_ShutdownTimer = 1; //So that the session count is re-evaluated at next world tick } @@ -2389,13 +2395,15 @@ void World::ShutdownMsg(bool show, Player* player) /// Cancel a planned server shutdown void World::ShutdownCancel() { - if(!m_ShutdownTimer) + // nothing cancel or too later + if(!m_ShutdownTimer || m_stopEvent) return; uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED; m_ShutdownMask = 0; m_ShutdownTimer = 0; + m_ExitCode = SHUTDOWN_EXIT_CODE; // to default value SendServerMessage(msgid); DEBUG_LOG("Server %s cancelled.",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown")); diff --git a/src/game/World.h b/src/game/World.h index baa36fdb5..b5a66e7d6 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -49,6 +49,13 @@ enum ShutdownMask SHUTDOWN_MASK_IDLE = 2, }; +enum ShutdownExitCode +{ + SHUTDOWN_EXIT_CODE = 0, + ERROR_EXIT_CODE = 1, + RESTART_EXIT_CODE = 2, +}; + /// Timers for different object refresh rates enum WorldTimers { @@ -310,7 +317,6 @@ struct CliCommandHolder class World { public: - static volatile bool m_stopEvent; static volatile uint32 m_worldLoopCounter; World(); @@ -387,11 +393,13 @@ class World void SendServerMessage(uint32 type, const char *text = "", Player* player = NULL); /// Are we in the middle of a shutdown? - uint32 GetShutdownMask() const { return m_ShutdownMask; } bool IsShutdowning() const { return m_ShutdownTimer > 0; } - void ShutdownServ(uint32 time, uint32 options = 0); + void ShutdownServ(uint32 time, uint32 options, uint8 exitcode); void ShutdownCancel(); void ShutdownMsg(bool show = false, Player* player = NULL); + static uint8 GetExitCode() { return m_ExitCode; } + static void StopNow(uint8 exitcode) { m_stopEvent = true; m_ExitCode = exitcode; } + static bool IsStopped() { return m_stopEvent; } void Update(time_t diff); @@ -467,6 +475,11 @@ class World void InitDailyQuestResetTime(); void ResetDailyQuests(); private: + static volatile bool m_stopEvent; + static uint8 m_ExitCode; + uint32 m_ShutdownTimer; + uint32 m_ShutdownMask; + time_t m_startTime; time_t m_gameTime; IntervalTimer m_timers[WUPDATE_COUNT]; @@ -493,9 +506,6 @@ class World std::string m_motd; std::string m_dataPath; - uint32 m_ShutdownTimer; - uint32 m_ShutdownMask; - // for max speed access static float m_MaxVisibleDistanceForCreature; static float m_MaxVisibleDistanceForPlayer; diff --git a/src/mangosd/CliRunnable.cpp b/src/mangosd/CliRunnable.cpp index 77d87fcda..d4c635660 100644 --- a/src/mangosd/CliRunnable.cpp +++ b/src/mangosd/CliRunnable.cpp @@ -165,7 +165,7 @@ bool ChatHandler::HandleCharacterDeleteCommand(const char* args) bool ChatHandler::HandleServerExitCommand(const char* args) { SendSysMessage(LANG_COMMAND_EXIT); - World::m_stopEvent = true; + World::StopNow(SHUTDOWN_EXIT_CODE); return true; } @@ -306,14 +306,14 @@ void CliRunnable::run() printf("mangos>"); ///- As long as the World is running (no World::m_stopEvent), get the command line and handle it - while (!World::m_stopEvent) + while (!World::IsStopped()) { fflush(stdout); #ifdef linux - while (!kb_hit_return() && !World::m_stopEvent) + while (!kb_hit_return() && !World::IsStopped()) // With this, we limit CLI to 10commands/second usleep(100); - if (World::m_stopEvent) + if (World::IsStopped()) break; #endif char *command_str = fgets(commandbuf,sizeof(commandbuf),stdin); @@ -344,7 +344,7 @@ void CliRunnable::run() } else if (feof(stdin)) { - World::m_stopEvent = true; + World::StopNow(SHUTDOWN_EXIT_CODE); } } diff --git a/src/mangosd/Master.cpp b/src/mangosd/Master.cpp index 4a01c10ab..1b6929267 100644 --- a/src/mangosd/Master.cpp +++ b/src/mangosd/Master.cpp @@ -75,7 +75,7 @@ public: w_loops = 0; m_lastchange = 0; w_lastchange = 0; - while(!World::m_stopEvent) + while(!World::IsStopped()) { ZThread::Thread::sleep(1000); uint32 curtime = getMSTime(); @@ -170,13 +170,13 @@ public: // if use ra spend time waiting for io, if not use ra ,just sleep if (usera) - while (!World::m_stopEvent) + while (!World::IsStopped()) { h.Select (0, socketSelecttime); checkping (); } else - while (!World::m_stopEvent) + while (!World::IsStopped()) { ZThread::Thread::sleep (static_cast (socketSelecttime / 1000)); checkping (); @@ -308,7 +308,7 @@ int Master::Run() if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1) { sLog.outError ("Failed to start network"); - World::m_stopEvent = true; + World::StopNow(ERROR_EXIT_CODE); // go down and shutdown the server } @@ -379,7 +379,8 @@ int Master::Run() // fixes a memory leak related to detaching threads from the module UnloadScriptingModule(); - return sWorld.GetShutdownMask() & SHUTDOWN_MASK_RESTART ? 2 : 0; + // Exit the process with specified return value + return World::GetExitCode(); } /// Initialize connection to the databases @@ -462,17 +463,18 @@ void Master::clearOnlineAccounts() } /// Handle termination signals -/** Put the World::m_stopEvent to 'true' if a termination signal is caught **/ void Master::_OnSignal(int s) { switch (s) { case SIGINT: + World::StopNow(RESTART_EXIT_CODE); + break; case SIGTERM: #ifdef _WIN32 case SIGBREAK: #endif - World::m_stopEvent = true; + World::StopNow(SHUTDOWN_EXIT_CODE); break; } diff --git a/src/mangosd/WorldRunnable.cpp b/src/mangosd/WorldRunnable.cpp index e812e23ec..7ebe95fc7 100644 --- a/src/mangosd/WorldRunnable.cpp +++ b/src/mangosd/WorldRunnable.cpp @@ -49,7 +49,7 @@ void WorldRunnable::run() uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST ///- While we have not World::m_stopEvent, update the world - while (!World::m_stopEvent) + while (!World::IsStopped()) { ++World::m_worldLoopCounter; realCurrTime = getMSTime(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index cfcb1eea6..46606a548 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "6829" + #define REVISION_NR "6830" #endif // __REVISION_NR_H__