[6830] Implement custom exit codes on server shutdown/restart

Added the possibility to use custom process return values
instead of hardcoded 0 (shutdown) or 2 (restart) to allow using them
for various custom external features / better handling.

This can be used through 4 commands in ".server" family:
 - shutdown
 - restart
 - idleshutdown
 - idlerestart

Those have from now on 2 arguments, where the second (return value)
is optional:

.server <command> <time> [return_value]

If return_value is not specified, default value (0 or 2) is used.

Signed-off-by: freghar <compmancz@gmail.com>

Set restart exist code for SIGINT singnal case.
Some code simplifications for original patch.
Related code cleanups.

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
This commit is contained in:
freghar 2008-11-12 21:09:56 +01:00 committed by VladimirMangos
parent 3d72b06fcd
commit 528a9d830a
8 changed files with 149 additions and 49 deletions

View file

@ -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;
}

View file

@ -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:

View file

@ -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"));

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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<unsigned long> (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;
}

View file

@ -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();

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "6829"
#define REVISION_NR "6830"
#endif // __REVISION_NR_H__