khanat-opennel-code/code/nel/src/net/service.cpp
2010-07-29 20:18:49 +02:00

2116 lines
59 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// 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 <http://www.gnu.org/licenses/>.
#include "stdnet.h"
//
// Includes
//
#ifdef NL_OS_WINDOWS
// these defines is for IsDebuggerPresent(). it'll not compile on windows 95
// just comment this and the IsDebuggerPresent to compile on windows 95
# define _WIN32_WINDOWS 0x0410
# define WINVER 0x0400
# define NOMINMAX
# include <windows.h>
# include <direct.h>
#elif defined NL_OS_UNIX
# include <unistd.h>
#endif
#include "nel/misc/config_file.h"
#include "nel/misc/displayer.h"
#include "nel/misc/mutex.h"
#include "nel/misc/window_displayer.h"
#include "nel/misc/gtk_displayer.h"
#include "nel/misc/win_displayer.h"
#include "nel/misc/path.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/misc/report.h"
#include "nel/misc/system_info.h"
#include "nel/misc/timeout_assertion_thread.h"
#include "nel/net/naming_client.h"
#include "nel/net/service.h"
#include "nel/net/unified_network.h"
#include "nel/net/net_displayer.h"
#include "nel/net/email.h"
#include "nel/net/varpath.h"
#include "nel/net/admin.h"
#include "nel/net/module_manager.h"
#include "nel/net/transport_class.h"
#include "stdin_monitor_thread.h"
//
// Namespaces
//
using namespace std;
using namespace NLMISC;
namespace NLNET
{
//
// Constants
//
static const sint Signal[] = {
SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
};
static const char *SignalName[]=
{
"SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV", "SIGTERM"
};
static const char* NegFiltersNames[] =
{
"NegFiltersDebug",
"NegFiltersInfo",
"NegFiltersWarning",
"NegFiltersAssert",
"NegFiltersError",
0
};
//
// Variables
//
TUnifiedCallbackItem EmptyCallbackArray[1] = { { "", NULL } };
// class static member
IService *IService::_Instance = NULL;
static sint ExitSignalAsked = 0;
// services stat
CVariable<sint32> UserSpeedLoop ("nel", "UserSpeedLoop", "duration of the last user loop (in ms)", 10, false);
CVariable<sint32> NetSpeedLoop ("nel", "NetSpeedLoop", "duration of the last network loop (in ms)", 10, false);
/// The time passed in callback during the loop
CVariable<uint32> L5CallbackTime("nel", "L5CallbackTime", "Time passed in the L5 callback function in the last loop (in ms)", 0, 100 );
/// The number of L5 callback treated
CVariable<uint32> L5CallbackCount("nel", "L5CallbackCount", "The number of layer 5 callback received in the last loop", 0, 100 );
extern uint32 TotalCallbackCalled;
extern uint32 TimeInCallback;
uint32 LastTotalCallbackCalled = 0;
uint32 LastTimeInCallback = 0;
// this is the thread that initialized the signal redirection
// we'll ignore other thread signals
static size_t SignalisedThread;
static CFileDisplayer fd;
static CNetDisplayer commandDisplayer(false);
//static CLog commandLog;
static string CompilationDate;
static uint32 LaunchingDate;
static uint32 NbUserUpdate = 0;
string CompilationMode = nlMode;
//static bool Bench = false;
CVariable<bool> Bench ("nel", "Bench", "1 if benching 0 if not", 0, true);
// This produce an assertion in the thread if the update loop is too slow
static CTimeoutAssertionThread MyTAT;
static void UpdateAssertionThreadTimeoutCB(IVariable &var) { uint32 timeOut; fromString(var.toString(), timeOut); MyTAT.timeout(timeOut); }
static CVariable<uint32> UpdateAssertionThreadTimeout("nel", "UpdateAssertionThreadTimeout", "in millisecond, timeout before thread assertion", 0, 0, true, UpdateAssertionThreadTimeoutCB);
// Flag to enable/disable the flushing of the sending queues when the service is shut down
// Default: false (matches the former behavior)
// Set it to true in services that need to send data on exit (for instance in their release() method)
CVariable<bool> FlushSendingQueuesOnExit("nel", "FlushSendingQueuesOnExit",
"Flag to enable/disable the flushing of the sending queues when the service is shut down", false, 0, true );
// If FlushSendingQueuesOnExit is on, only the sending queues to these specified services will be flushed
// Format: service short names separated by ':'
// Default: "" (all will be flushed if FlushSendingQueuesOnExit is on, none if it is off)
CVariable<string> NamesOfOnlyServiceToFlushSending("nel", "NamesOfOnlyServiceToFlushSending",
"If FlushSendingQueuesOnExit is on, only the sending queues to these specified services will be flushed (ex: \"WS:LS\"; all will be flushed if empty string)", "", 0, true );
//
// Signals managing
//
// This function is called when a signal comes
static void sigHandler(int Sig)
{
// redirect the signal for the next time
signal(Sig, sigHandler);
// find the signal
for (int i = 0; i < (int)(sizeof(Signal)/sizeof(Signal[0])); i++)
{
if (Sig == Signal[i])
{
if (getThreadId () != SignalisedThread)
{
nldebug ("SERVICE: Not the main thread (%u, %u) received the signal (%s, %d), ignore it", getThreadId (), SignalisedThread, SignalName[i],Sig);
return;
}
else
{
nlinfo ("SERVICE: Signal %s (%d) received", SignalName[i], Sig);
switch (Sig)
{
// Note: SIGKILL (9) and SIGSTOP (19) can't be trapped
case SIGINT :
if (IService::getInstance()->haveLongArg("nobreak"))
{
// ignore ctrl-c
nlinfo("SERVICE: Ignoring ctrl-c");
return;
}
case SIGABRT :
case SIGILL :
case SIGSEGV :
case SIGTERM :
// you should not call a function and system function like printf in a SigHandle because
// signal-handler routines are usually called asynchronously when an interrupt occurs.
if (ExitSignalAsked == 0)
{
nlinfo ("SERVICE: Receive a signal that said that i must exit");
ExitSignalAsked = Sig;
return;
}
else
{
nlinfo ("SERVICE: Signal already received, launch the brutal exit");
exit (EXIT_FAILURE);
}
break;
}
}
}
}
nlwarning ("SERVICE: Unknown signal received (%d)", Sig);
}
// Initialise the signal redirection
static void initSignal()
{
SignalisedThread = getThreadId ();
#ifdef NL_DEBUG
// in debug mode, we only trap the SIGINT signal (for ctrl-c handling)
//signal(Signal[3], sigHandler);
//nldebug("Signal : %s (%d) trapped", SignalName[3], Signal[3]);
#else
// in release, redirect all signals
// don't redirect now because too hard to debug...
// for (int i = 0; i < (int)(sizeof(Signal)/sizeof(Signal[0])); i++)
// {
// signal(Signal[i], sigHandler);
// nldebug("Signal %s (%d) trapped", SignalName[i], Signal[i]);
// }
//
if (IService::getInstance()->haveLongArg("nobreak"))
{
signal(Signal[3], sigHandler);
}
#endif
}
void cbDirectoryChanged (IVariable &var)
{
IService *instance = IService::getInstance();
// Convert to full path if required
// (warning: ConvertSavesFilesDirectoryToFullPath, read from the config file, won't be ready for the initial variable assigments done before the config file has been loaded)
string vp = var.toString();
if ((var.getName() != "SaveFilesDirectory") || instance->ConvertSavesFilesDirectoryToFullPath.get())
{
vp = CPath::getFullPath(vp);
var.fromString(vp);
}
nlinfo ("SERVICE: '%s' changed to '%s'", var.getName().c_str(), vp.c_str());
// Update the running directory if needed
if (var.getName() == "RunningDirectory")
{
CPath::setCurrentPath(vp);
}
// Call the callback if provided
if (instance->_DirectoryChangedCBI)
instance->_DirectoryChangedCBI->onVariableChanged(var);
}
//
// Service built-in callbacks
//
void cbReceiveShardId (CMessage& msgin, const string &serviceName, TServiceId serviceId)
{
uint32 shardId;
msgin.serial(shardId);
if (IService::getInstance()->getDontUseNS())
{
// we don't use NS, so shard ID message don't concern us
return;
}
if (serviceName != "WS")
{
nlwarning("SERVICE: received unauthorized R_SH_ID callback from service %s-%uh asking to set ShardId to %d", serviceName.c_str(), serviceId.get(), shardId);
return;
}
nlinfo("SERVICE: ShardId is %u", shardId);
IService::getInstance()->setShardId( shardId );
}
std::string IService::getServiceStatusString() const
{
static string emptyString;
return emptyString;
}
//
void IService::anticipateShardId( uint32 shardId )
{
if ( ! ((_ShardId == DEFAULT_SHARD_ID) || (shardId == _ShardId)) )
nlerror( "IService::anticipateShardId() overwrites %u with %u", _ShardId, shardId );
_ShardId = shardId;
}
//
void IService::setShardId( uint32 shardId )
{
if ( ! ((_ShardId == DEFAULT_SHARD_ID) || (shardId == _ShardId)) )
nlwarning( "SERVICE: The shardId from the WS (%u) is different from the anticipated shardId (%u)", shardId, _ShardId );
_ShardId = shardId;
}
TUnifiedCallbackItem builtinServiceCallbacks [] =
{
{ "R_SH_ID", cbReceiveShardId },
};
//
// Class implementation
//
// Ctor
IService::IService() :
WindowDisplayer(0),
WriteFilesDirectory("nel", "WriteFilesDirectory", "directory where to save generic shard information (packed_sheets for example)", ".", 0, true, cbDirectoryChanged),
SaveFilesDirectory("nel", "SaveFilesDirectory", "directory where to save specific shard information (shard time for example)", ".", 0, true, cbDirectoryChanged),
ConvertSavesFilesDirectoryToFullPath("nel", "ConvertSaveFilesDirectoryToFullPath", "If true (default), the provided SaveFilesDirectory will be converted to a full path (ex: saves -> /home/dir/saves)", true, 0, true ),
ListeningPort("nel", "ListeningPort", "listening port for this service", 0, 0, true),
_RecordingState(CCallbackNetBase::Off),
_UpdateTimeout(100),
_SId(0),
_ExitStatus(0),
_Initialized(false),
ConfigDirectory("nel", "ConfigDirectory", "directory where config files are", ".", 0, true, cbDirectoryChanged),
LogDirectory("nel", "LogDirectory", "directory where the service is logging", ".", 0, true, cbDirectoryChanged),
RunningDirectory("nel", "RunningDirectory", "directory where the service is running on", ".", 0, true, cbDirectoryChanged),
Version("nel", "Version", "Version of the shard", ""),
_CallbackArray (0),
_CallbackArraySize (0),
_DontUseNS(false),
_DontUseAES(false),
_ResetMeasures(false),
_ShardId(0),
_ClosureClearanceStatus(CCMustRequestClearance),
_RequestClosureClearanceCallback(NULL),
_DirectoryChangedCBI(NULL)
{
// Singleton
_Instance = this;
// register in the safe singleton registry
INelContext::getInstance().setSingletonPointer("IService", this);
}
IService::~IService()
{
// unregister the singleton
INelContext::getInstance().releaseSingletonPointer("IService", this);
}
bool IService::haveArg (char argName) const
{
for (uint32 i = 0; i < _Args.size(); i++)
{
if (_Args[i].size() >= 2 && _Args[i][0] == '-')
{
if (_Args[i][1] == argName)
{
return true;
}
}
}
return false;
}
string IService::getArg (char argName) const
{
for (uint32 i = 0; i < _Args.size(); i++)
{
if (_Args[i].size() >= 2 && _Args[i][0] == '-')
{
if (_Args[i][1] == argName)
{
/* Remove the first and last '"' :
-c"C:\Documents and Settings\toto.tmp"
will return :
C:\Documents and Settings\toto.tmp
*/
uint begin = 2;
if (_Args[i].size() < 3)
return "";
//throw Exception ("Parameter '-%c' is malformed, missing content", argName);
if (_Args[i][begin] == '"')
begin++;
// End
uint size = (uint)_Args[i].size();
if (size && _Args[i][size-1] == '"')
size--;
size = (uint)(std::max((int)0, (int)size-(int)begin));
return _Args[i].substr(begin, size);
}
}
}
throw Exception ("Parameter '-%c' is not found in command line", argName);
}
bool IService::haveLongArg (const char* argName) const
{
for (uint32 i = 0; i < _Args.size(); i++)
{
if (_Args[i].left(2)=="--" && _Args[i].leftCrop(2).splitTo('=')==argName)
{
return true;
}
}
return false;
}
string IService::getLongArg (const char* argName) const
{
for (uint32 i = 0; i < _Args.size(); i++)
{
if (_Args[i].left(2)=="--" && _Args[i].leftCrop(2).splitTo('=')==argName)
{
NLMISC::CSString val= _Args[i].splitFrom('=');
if (!val.empty())
{
return val.unquoteIfQuoted();
}
if (i+1<_Args.size() && _Args[i+1].c_str()[0]!='-')
{
return _Args[i+1].unquoteIfQuoted();
}
return std::string();
}
}
return std::string();
}
void IService::setArgs (const char *args)
{
_Args.push_back ("<ProgramName>");
string sargs (args);
string::size_type pos1 = 0, pos2 = 0;
do
{
// Look for the first non space character
pos1 = sargs.find_first_not_of (" ", pos2);
if (pos1 == string::npos) break;
// Look for the first space or "
pos2 = sargs.find_first_of (" \"", pos1);
if (pos2 != string::npos)
{
// " ?
if (sargs[pos2] == '"')
{
// Look for the final \"
pos2 = sargs.find_first_of ("\"", pos2+1);
if (pos2 != string::npos)
{
// Look for the first space
pos2 = sargs.find_first_of (" ", pos2+1);
}
}
}
// Compute the size of the string to extract
string::difference_type length = (pos2 != string::npos) ? pos2-pos1 : string::npos;
string tmp = sargs.substr (pos1, length);
_Args.push_back (tmp);
}
while (pos2 != string::npos);
}
void IService::setArgs (int argc, const char **argv)
{
for (sint i = 0; i < argc; i++)
{
_Args.push_back (argv[i]);
}
}
void cbLogFilter (CConfigFile::CVar &var)
{
CLog *log = NULL;
if (var.Name == "NegFiltersDebug")
{
log = DebugLog;
}
else if (var.Name == "NegFiltersInfo")
{
log = InfoLog;
}
else if (var.Name == "NegFiltersWarning")
{
log = WarningLog;
}
else if (var.Name == "NegFiltersAssert")
{
log = AssertLog;
}
else if (var.Name == "NegFiltersError")
{
log = ErrorLog;
}
else
{
nlstop;
}
nlinfo ("SERVICE: Updating %s from config file", var.Name.c_str());
// remove all old filters from config file
CConfigFile::CVar &oldvar = IService::getInstance()->ConfigFile.getVar (var.Name);
for (uint j = 0; j < oldvar.size(); j++)
{
log->removeFilter (oldvar.asString(j).c_str());
}
// add all new filters from config file
for (uint i = 0; i < var.size(); i++)
{
log->addNegativeFilter (var.asString(i).c_str());
}
}
void cbExecuteCommands (CConfigFile::CVar &var)
{
for (uint i = 0; i < var.size(); i++)
{
ICommand::execute (var.asString(i), IService::getInstance()->CommandLog);
}
}
//
// The main function of the service
//
sint IService::main (const char *serviceShortName, const char *serviceLongName, uint16 servicePort, const char *configDir, const char *logDir, const char *compilationDate)
{
bool userInitCalled = false;
CConfigFile::CVar *var = NULL;
IThread *timeoutThread = NULL;
// a short name service can't be a number
uint tmp;
nlassert (!fromString(serviceShortName, tmp));
try
{
createDebug();
// init the module manager
IModuleManager::getInstance();
//
// Init parameters
//
// at the very beginning, eventually wrote a file with the pid
if (haveLongArg("writepid"))
{
// use legacy C primitives
FILE *fp = fopen("pid.state", "wt");
if (fp)
{
fprintf(fp, "%u", getpid());
fclose(fp);
}
}
_ShortName = serviceShortName;
CLog::setProcessName (_ShortName);
// get the path where to run the service if any in the command line
if (haveArg('A'))
RunningDirectory = CPath::standardizePath(getArg('A'));
ConfigDirectory = CPath::standardizePath(configDir);
LogDirectory = CPath::standardizePath(logDir);
_LongName = serviceLongName;
CompilationDate = compilationDate;
LaunchingDate = CTime::getSecondsSince1970();
ListeningPort = servicePort;
setReportEmailFunction ((void*)sendEmail);
// setDefaultEmailParams ("gw.nevrax.com", "", "cado@nevrax.com");
//
// Load the config file
//
// get the config file dir if any in the command line
if (haveArg('C'))
ConfigDirectory = CPath::standardizePath(getArg('C'));
string cfn = ConfigDirectory.c_str() + _LongName + ".cfg";
if (!CFile::fileExists(ConfigDirectory.c_str() + _LongName + ".cfg"))
{
// check if the default exists
if (!CFile::fileExists(ConfigDirectory.c_str() + _LongName + "_default.cfg"))
{
nlerror ("SERVICE: Neither the config file '%s' nor the default one can be found, can't launch the service", cfn.c_str());
}
else
{
// create the basic .cfg that link the default one
FILE *fp = fopen (cfn.c_str(), "w");
if (fp == NULL)
{
nlerror ("SERVICE: Can't create config file '%s'", cfn.c_str());
}
fprintf(fp, "// link the default config file for %s\n", _LongName.c_str());
fprintf(fp, "RootConfigFilename = \"%s_default.cfg\";\n", _LongName.c_str());
fclose (fp);
}
}
ConfigFile.load (cfn);
// setup variable with config file variable
IVariable::init (ConfigFile);
if (ConfigFile.exists("DefaultEmailSMTP") && ConfigFile.exists("DefaultEmailTo"))
NLNET::setDefaultEmailParams(
ConfigFile.getVar("DefaultEmailSMTP").asString(),
ConfigFile.exists("DefaultEmailFrom")
? ConfigFile.getVar("DefaultEmailFrom").asString()
: "service@opennel.org",
ConfigFile.getVar("DefaultEmailTo").asString());
//
// Set the shard Id
//
if ((var = ConfigFile.getVarPtr("NoWSShardId")) != NULL)
{
_ShardId = var->asInt();
}
else
{
// something high enough as default
_ShardId = DEFAULT_SHARD_ID;
}
if (haveArg('Z'))
{
string s = IService::getInstance()->getArg('Z');
if (s == "u")
{
// do not release the module manager
}
else
{
// release the module manager
IModuleManager::getInstance().releaseInstance();
}
return 0;
}
// we have to call this again because the config file can changed this variable but the cmd line is more prioritary
if (haveArg('A'))
RunningDirectory = CPath::standardizePath(getArg('A'));
//
// Init debug/log stuffs (must be first things otherwise we can't log if errors)
//
// get the log dir if any in the command line
if (haveArg('L'))
LogDirectory = CPath::standardizePath(getArg('L'));
changeLogDirectory (LogDirectory);
bool noLog= (ConfigFile.exists ("DontLog")) && (ConfigFile.getVar("DontLog").asInt() == 1);
noLog|=haveLongArg("nolog");
if (!noLog)
{
// we create the log with service name filename ("test_service_ALIAS.log" for example)
string logname = LogDirectory.toString() + _LongName;
if (haveArg('N'))
logname += "_" + toLower(getArg('N'));
logname += ".log";
fd.setParam (logname, false);
DebugLog->addDisplayer (&fd);
InfoLog->addDisplayer (&fd);
WarningLog->addDisplayer (&fd);
AssertLog->addDisplayer (&fd);
ErrorLog->addDisplayer (&fd);
CommandLog.addDisplayer (&fd, true);
}
bool dontUseStdIn= (ConfigFile.exists ("DontUseStdIn")) && (ConfigFile.getVar("DontUseStdIn").asInt() == 1);
if (!dontUseStdIn)
{
IStdinMonitorSingleton::getInstance()->init();
}
//
// Init the hierarchical timer
//
CHTimer::startBench(false, true);
CHTimer::endBench();
//
// Set the assert mode
//
if (ConfigFile.exists ("Assert"))
setAssert (ConfigFile.getVar("Assert").asInt() == 1);
//
// Set the update timeout if found in the cfg
//
if ((var = ConfigFile.getVarPtr("UpdateTimeout")) != NULL)
{
_UpdateTimeout = var->asInt();
}
//
// Set the negative filter from the config file
//
for(const char **name = NegFiltersNames; *name; name++)
{
if ((var = ConfigFile.getVarPtr (*name)) != NULL)
{
ConfigFile.setCallback (*name, cbLogFilter);
cbLogFilter(*var);
}
}
ConfigFile.setCallback ("Commands", cbExecuteCommands);
if ((var = ConfigFile.getVarPtr ("Commands")) != NULL)
{
cbExecuteCommands(*var);
}
//
// Command line start
//
commandStart ();
//
// Create the window if needed
//
if ((var = ConfigFile.getVarPtr ("WindowStyle")) != NULL)
{
string disp = var->asString ();
#ifdef NL_USE_GTK
if (disp == "GTK")
{
WindowDisplayer = new CGtkDisplayer ("DEFAULT_WD");
}
#endif // NL_USE_GTK
#ifdef NL_OS_WINDOWS
if (disp == "WIN")
{
WindowDisplayer = new CWinDisplayer ("DEFAULT_WD");
}
#endif // NL_OS_WINDOWS
if (WindowDisplayer == NULL && disp != "NONE")
{
nlinfo ("SERVICE: Unknown value for the WindowStyle (should be GTK, WIN or NONE), use no window displayer");
}
}
vector <pair<string,uint> > displayedVariables;
//uint speedNetLabel, speedUsrLabel, rcvLabel, sndLabel, rcvQLabel, sndQLabel, scrollLabel;
if (WindowDisplayer != NULL)
{
//
// Init window param if necessary
//
sint x=-1, y=-1, w=-1, h=-1, fs=10, history=-1;
bool iconified = false, ww = false;
string fn;
if ((var = ConfigFile.getVarPtr("XWinParam")) != NULL) x = var->asInt();
if ((var = ConfigFile.getVarPtr("YWinParam")) != NULL) y = var->asInt();
if ((var = ConfigFile.getVarPtr("WWinParam")) != NULL) w = var->asInt();
if ((var = ConfigFile.getVarPtr("HWinParam")) != NULL) h = var->asInt();
if ((var = ConfigFile.getVarPtr("HistoryWinParam")) != NULL) history = var->asInt();
if ((var = ConfigFile.getVarPtr("IWinParam")) != NULL) iconified = var->asInt() == 1;
if ((var = ConfigFile.getVarPtr("FontSize")) != NULL) fs = var->asInt();
if ((var = ConfigFile.getVarPtr("FontName")) != NULL) fn = var->asString();
if ((var = ConfigFile.getVarPtr("WordWrap")) != NULL) ww = var->asInt() == 1;
if (haveArg('I')) iconified = true;
WindowDisplayer->create (string("*INIT* ") + _ShortName + " " + _LongName, iconified, x, y, w, h, history, fs, fn, ww, &CommandLog);
DebugLog->addDisplayer (WindowDisplayer);
InfoLog->addDisplayer (WindowDisplayer);
WarningLog->addDisplayer (WindowDisplayer);
ErrorLog->addDisplayer (WindowDisplayer);
AssertLog->addDisplayer (WindowDisplayer);
CommandLog.addDisplayer(WindowDisplayer, true);
// adding default displayed variables
displayedVariables.push_back(make_pair(string("NetLop|NetSpeedLoop"), WindowDisplayer->createLabel ("NetLop")));
displayedVariables.push_back(make_pair(string("UsrLop|UserSpeedLoop"), WindowDisplayer->createLabel ("UsrLop")));
displayedVariables.push_back(make_pair(string("|Scroller"), WindowDisplayer->createLabel ("NeL Rulez")));
CConfigFile::CVar *v = ConfigFile.getVarPtr("DisplayedVariables");
if (v != NULL)
{
for (uint i = 0; i < v->size(); i++)
{
displayedVariables.push_back(make_pair(v->asString(i), WindowDisplayer->createLabel (v->asString(i).c_str())));
}
}
}
nlinfo ("SERVICE: Starting Service '%s' using NeL ("__DATE__" "__TIME__") compiled %s", _ShortName.c_str(), CompilationDate.c_str());
nlinfo ("SERVICE: On OS: %s", CSystemInfo::getOS().c_str());
setExitStatus (EXIT_SUCCESS);
//
// Redirect signal if needed (in release mode only)
//
#ifdef NL_OS_WINDOWS
# ifdef NL_NO_DEBUG
initSignal();
# else
// don't install signal is the application is started in debug mode
if (IsDebuggerPresent ())
{
//nlinfo("Running with the debugger, don't redirect signals");
initSignal();
}
else
{
//nlinfo("Running without the debugger, redirect SIGINT signal");
initSignal();
}
# endif
#else // NL_OS_UNIX
initSignal();
#endif
//
// Ignore SIGPIPE (broken pipe) on unix system
//
#ifdef NL_OS_UNIX
// Ignore the SIGPIPE signal
sigset_t SigList;
bool IgnoredPipe = true;
if (sigemptyset (&SigList) == -1)
{
perror("sigemptyset()");
IgnoredPipe = false;
}
if (sigaddset (&SigList, SIGPIPE) == -1)
{
perror("sigaddset()");
IgnoredPipe = false;
}
if (sigprocmask (SIG_BLOCK, &SigList, NULL) == -1)
{
perror("sigprocmask()");
IgnoredPipe = false;
}
nldebug ("SERVICE: SIGPIPE %s", IgnoredPipe?"Ignored":"Not Ignored");
#endif // NL_OS_UNIX
//
// Initialize the network system
//
string localhost;
try
{
// Initialize WSAStartup and network stuffs
CSock::initNetwork();
// Get the localhost name
localhost = CInetAddress::localHost().hostName();
}
catch (NLNET::ESocket &)
{
localhost = "<UnknownHost>";
}
// Set the localhost name and service name to the logger
CLog::setProcessName (localhost+"/"+_ShortName);
nlinfo ("SERVICE: Host: %s", localhost.c_str());
//
// Initialize server parameters
//
// set the listen port if there are a port arg in the command line
if (haveArg('P'))
{
NLMISC::fromString(getArg('P'), ListeningPort);
}
// set the aes aliasname if present in cfg file
CConfigFile::CVar *varAliasName= ConfigFile.getVarPtr("AESAliasName");
if (varAliasName != NULL)
{
_AliasName = varAliasName->asString();
nlinfo("SERVICE: Setting alias name to: '%s'",_AliasName.c_str());
}
// set the aes aliasname if is present in the command line
if (haveArg('N'))
{
_AliasName = getArg('N');
nlinfo("SERVICE: Setting alias name to: '%s'",_AliasName.c_str());
}
// Load the recording state from the config file
if ((var = ConfigFile.getVarPtr ("Rec")) != NULL)
{
string srecstate = toUpper(var->asString());
if ( srecstate == "RECORD" )
{
_RecordingState = CCallbackNetBase::Record;
nlinfo( "SERVICE: Service recording messages" );
}
else if ( srecstate == "REPLAY" )
{
_RecordingState = CCallbackNetBase::Replay;
nlinfo( "SERVICE: Service replaying messages" );
}
else
{
_RecordingState = CCallbackNetBase::Off;
}
}
else
{
// Not found
_RecordingState = CCallbackNetBase::Off;
}
// Load the default stream format
if ((var = ConfigFile.getVarPtr ("StringMsgFormat")) != NULL)
{
CMessage::setDefaultStringMode( var->asInt() == 1 );
}
else
{
// Not found => binary
CMessage::setDefaultStringMode( false );
}
///
/// Layer5 Startup
///
// get the sid
if ((var = ConfigFile.getVarPtr ("SId")) != NULL)
{
sint32 sid = var->asInt();
if (sid<=0 || sid>255)
{
nlwarning("SERVICE: Bad SId value in the config file, %d is not in [0;255] range", sid);
_SId.set(0);
}
else
{
_SId.set(static_cast<uint16>(sid));
}
}
else
{
// ok, SId not found, use dynamic sid
_SId.set(0);
}
// look if we don't want to use NS
if ((var = ConfigFile.getVarPtr ("DontUseNS")) != NULL)
{
// if we set the value in the config file, get it
_DontUseNS = (var->asInt() == 1);
}
else
{
// if not, we use ns only if service is not ns, ls, aes, as
_DontUseNS = false;
}
if (haveLongArg("nons"))
{
// command line override
_DontUseNS = true;
}
//
// Register all network associations (must be before the CUnifiedNetwork::getInstance()->init)
//
if ((var = ConfigFile.getVarPtr ("Networks")) != NULL)
{
for (uint8 i = 0; i < var->size (); i++)
CUnifiedNetwork::getInstance()->addNetworkAssociation (var->asString(i), i);
}
if ((var = ConfigFile.getVarPtr ("DefaultNetworks")) != NULL)
{
for (uint8 i = 0; i < var->size (); i++)
CUnifiedNetwork::getInstance()->addDefaultNetwork(var->asString(i));
}
// normal setup for the common services
if (!_DontUseNS)
{
bool ok = false;
while (!ok)
{
string LSAddr;
if (haveArg('B'))
{
// if the naming service address is set on the command line, get it (overwrite the cfg)
LSAddr = getArg('B');
}
else
{
// else read the naming service address from the config file
LSAddr = ConfigFile.getVar ("NSHost").asString();
}
// if there's no port to the NS, use the default one 50000
if (LSAddr.find(":") == string::npos)
LSAddr += ":50000";
CInetAddress loc(LSAddr);
try
{
// todo: check if app not closed by user, or you get stuck here
if ( CUnifiedNetwork::getInstance()->init (&loc, _RecordingState, _ShortName, ListeningPort, _SId) )
{
ok = true;
}
else
{
nlinfo( "SERVICE: Exiting..." );
beep( 880, 400 );
beep( 440, 400 );
beep( 220, 400 );
// remove the stdin monitor thread
IStdinMonitorSingleton::getInstance()->release(); // does nothing if not initialized
// release the module manager
IModuleManager::getInstance().releaseInstance();
return 10;
}
}
catch (ESocketConnectionFailed &)
{
nlinfo ("SERVICE: Could not connect to the Naming Service (%s). Retrying in a few seconds...", loc.asString().c_str());
nlSleep (5000);
}
}
}
else
{
CUnifiedNetwork::getInstance()->init(NULL, _RecordingState, _ShortName, ListeningPort, _SId);
}
// get the hostname for later use
_HostName = CInetAddress::localHost().hostName();
// At this point, the _SId must be ok if we use the naming service.
// If it's 0, it means that we don't use NS and we left the other side server to find a sid for your connection
if(!_DontUseNS)
{
nlassert (_SId.get() != 0);
}
//
// Connect to the local AES and send identification
//
// look if we don't want to use NS
if ((var = ConfigFile.getVarPtr ("DontUseAES")) != NULL)
{
// if we set the value in the config file, get it
_DontUseAES = var->asInt() == 1;
}
else
{
// if not, we use aes only if service is not aes or as
_DontUseAES = false;
}
initAdmin (_DontUseAES);
while (NLNET::CUnifiedNetwork::getInstance()->tryFlushAllQueues() != 0)
{
nlSleep(10);
}
//
// Add callback array
//
// add inner service callback array
NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(builtinServiceCallbacks, sizeof(builtinServiceCallbacks)/sizeof(builtinServiceCallbacks[0]));
// add callback set in the NLNET_SERVICE_MAIN macro
NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(_CallbackArray, _CallbackArraySize);
//
// Now we have the service id, we can set the entites id generator
//
NLMISC::CEntityId::setServiceId(TServiceId8(_SId).get());
// Set the localhost name and service name and the sid
CLog::setProcessName (localhost+"/"+_ShortName+"-"+toString(_SId.get()));
//
// Add default pathes
//
if ((var = ConfigFile.getVarPtr ("IgnoredFiles")) != NULL)
{
for (uint i = 0; i < var->size(); i++)
{
CPath::addIgnoredDoubleFile (var->asString(i));
}
}
if ((var = ConfigFile.getVarPtr ("Paths")) != NULL)
{
for (uint i = 0; i < var->size(); i++)
{
CPath::addSearchPath (var->asString(i), true, false);
}
}
if ((var = ConfigFile.getVarPtr ("PathsNoRecurse")) != NULL)
{
for (uint i = 0; i < var->size(); i++)
{
CPath::addSearchPath (var->asString(i), false, false);
}
}
// if we can, try to setup where to save files
if (IService::getInstance()->haveArg('W'))
{
// use the command line param if set (must be done after the config file has been loaded)
SaveFilesDirectory = IService::getInstance()->getArg('W');
}
CTransportClass::init();
//
// Call the user service init
//
userInitCalled = true; // the bool must be put *before* the call to init()
setCurrentStatus("Initializing");
init ();
clearCurrentStatus("Initializing");
//
// Connects to the present services
// WARNING: only after the user init() was called because the
// addService may call up service callbacks.
//
CUnifiedNetwork::getInstance()->connect();
//
// Say to the AES that the service is ready
//
if (!_DontUseAES)
{
// send the ready message (service init finished)
CMessage msgout ("SR");
CUnifiedNetwork::getInstance()->send("AES", msgout);
}
_Initialized = true;
nlinfo ("SERVICE: Service initialized, executing StartCommands");
//
// Call the user command from the config file if any
//
string cmdRoot("StartCommands");
vector<string> posts;
// add an empty string (for the common part of start commands)
posts.push_back(string());
if (IService::getInstance()->haveArg('S'))
{
string s = IService::getInstance()->getArg('S');
posts.push_back(s);
}
CConfigFile::CVar *var;
for (uint i=0; i<posts.size(); ++i)
{
string varName = cmdRoot + posts[i];
if ((var = IService::getInstance()->ConfigFile.getVarPtr (varName)) != NULL)
{
for (uint i = 0; i < var->size(); i++)
{
ICommand::execute (var->asString(i), CommandLog);
}
}
}
string str;
CLog logDisplayVars;
CLightMemDisplayer mdDisplayVars;
logDisplayVars.addDisplayer (&mdDisplayVars);
//
// Activate the timeout assertion thread
//
timeoutThread = IThread::create(&MyTAT, 1024*4);
timeoutThread->start();
//
// Set service ready
//
nlinfo ("SERVICE: Service ready");
if (WindowDisplayer != NULL)
WindowDisplayer->setTitleBar (_ShortName + " " + _LongName + " " + Version.c_str());
//
// Call the user service update each loop and check files and network activity
//
TTime checkCpuProcTime = 0;
for(;;)
{
MyTAT.activate();
if(Bench) CHTimer::startBench(false, true, false);
// count the amount of time to manage internal system
TTime bbefore = CTime::getLocalTime ();
// every second, check for CPU usage
if (bbefore - checkCpuProcTime > 1000)
{
checkCpuProcTime = bbefore;
_CPUUsageStats.peekMeasures();
}
// call the user update and exit if the user update asks it
{
H_AUTO(NLNETServiceUpdate);
if (!update ())
{
CHTimer::endBench();
break;
}
}
// deal with any input waiting from stdin
{
H_AUTO(NLNETStdinMonitorUpdate);
IStdinMonitorSingleton::getInstance()->update();
}
// if the launching mode is 'quit after the first update' we set the exit signal
if (haveArg('Q'))
{
// we use -100 so that the final return code ends as 0 (100+ExitSignalAsked) because
// otherwise we can't launch services with -Q in a makefile
ExitSignalAsked = -100;
}
NbUserUpdate++;
// count the amount of time to manage internal system
TTime before = CTime::getLocalTime ();
if (WindowDisplayer != NULL)
{
// update the window displayer and quit if asked
if (!WindowDisplayer->update ())
{
nlinfo ("SERVICE: The window displayer was closed by user, need to quit");
ExitSignalAsked = 1;
}
}
// stop the loop if the exit signal asked
if (ExitSignalAsked != 0)
{
if ( _RequestClosureClearanceCallback )
{
if ( _ClosureClearanceStatus == CCClearedForClosure )
{
// Clearance has been granted
CHTimer::endBench();
break;
}
else if ( _ClosureClearanceStatus == CCMustRequestClearance )
{
if ( _RequestClosureClearanceCallback() )
{
// Direct clearance
_ClosureClearanceStatus = CCClearedForClosure;
CHTimer::endBench();
break;
}
else
{
// Delayed clearance
_ClosureClearanceStatus = CCWaitingForClearance;
}
}
else if ( _ClosureClearanceStatus >= CCCallbackThenClose )
{
// Always direct closure, because we don't have a connection to the naming service anymore
// But still call the callback
_RequestClosureClearanceCallback();
CHTimer::endBench();
break;
}
}
else
{
// Immediate closure, no clearance needed
CHTimer::endBench();
break;
}
}
CConfigFile::checkConfigFiles ();
updateAdmin ();
CFile::checkFileChange();
// update updatable interface
set<IServiceUpdatable*>::iterator first(_Updatables.begin()), last(_Updatables.end());
for (; first != last; ++first)
{
IServiceUpdatable *updatable = *first;
updatable->serviceLoopUpdate();
}
// get and manage layer 5 messages
CUnifiedNetwork::getInstance()->update (_UpdateTimeout);
// update modules
IModuleManager::getInstance().updateModules();
// Allow direct closure if the naming service was lost
if ( _RequestClosureClearanceCallback )
{
if ( ! CNamingClient::connected() )
{
if ( _ClosureClearanceStatus < CCCallbackThenClose )
_ClosureClearanceStatus += CCCallbackThenClose; // change status but backup old value
}
else
{
if ( _ClosureClearanceStatus >= CCCallbackThenClose )
_ClosureClearanceStatus -= CCCallbackThenClose; // set the closure state back if the NS comes back
}
}
NetSpeedLoop = (sint32) (CTime::getLocalTime () - before);
UserSpeedLoop = (sint32) (before - bbefore);
L5CallbackTime = TimeInCallback - LastTimeInCallback;
LastTimeInCallback = TimeInCallback;
L5CallbackCount = TotalCallbackCalled - LastTotalCallbackCalled;
LastTotalCallbackCalled = TotalCallbackCalled;
if (WindowDisplayer != NULL)
{
static TTime lt = 0;
TTime ct = CTime::getLocalTime();
if(ct > lt+100)
{
lt = ct;
uint64 rcv, snd, rcvq, sndq;
rcv = CUnifiedNetwork::getInstance()->getBytesReceived ();
snd = CUnifiedNetwork::getInstance()->getBytesSent ();
rcvq = CUnifiedNetwork::getInstance()->getReceiveQueueSize ();
sndq = CUnifiedNetwork::getInstance()->getSendQueueSize ();
for (uint i = 0; i < displayedVariables.size(); i++)
{
// it s a separator, do nothing
if (displayedVariables[i].first.empty())
continue;
// it s a command, do nothing
if (displayedVariables[i].first[0] == '@')
continue;
string dispName = displayedVariables[i].first;
string varName = dispName;
string::size_type pos = dispName.find("|");
if (pos != string::npos)
{
varName = displayedVariables[i].first.substr(pos+1);
dispName = displayedVariables[i].first.substr(0, pos);
}
if (dispName.empty())
str = "";
else
str = dispName + ": ";
mdDisplayVars.clear ();
ICommand::execute(varName, logDisplayVars, true);
const std::deque<std::string> &strs = mdDisplayVars.lockStrings();
if (strs.size()>0)
{
str += strs[0].substr(0,strs[0].size()-1);
}
else
{
str += "???";
}
mdDisplayVars.unlockStrings();
WindowDisplayer->setLabel (displayedVariables[i].second, str);
}
}
}
// nldebug ("SYNC: updatetimeout must be %d and is %d, sleep the rest of the time", _UpdateTimeout, delta);
CHTimer::endBench();
// Resetting the hierarchical timer must be done outside the top-level timer
if ( _ResetMeasures )
{
CHTimer::clear();
_ResetMeasures = false;
}
MyTAT.deactivate();
}
}
catch (EFatalError &)
{
// Somebody call nlerror, so we have to quit now, the message already display
// so we don't have to to anything
setExitStatus (EXIT_FAILURE);
}
catch (ESocket &e)
{
// Catch NeL network exception to release the system cleanly setExitStatus (EXIT_FAILURE);
ErrorLog->displayNL( "NeL Exception in \"%s\": %s", _ShortName.c_str(), e.what() );
}
catch ( uint ) // SEH exceptions
{
ErrorLog->displayNL( "SERVICE: System exception" );
}
try
{
nlinfo ("SERVICE: Service starts releasing");
//
// Call the user service release() if the init() was called
//
if (userInitCalled)
{
setCurrentStatus("Releasing");
release ();
}
CTransportClass::release();
//
// Delete all network connection (naming client also)
//
std::vector<std::string> namesOfOnlyServiceToFlushSendingV;
explode( NamesOfOnlyServiceToFlushSending.get(), string(":"), namesOfOnlyServiceToFlushSendingV, true );
CUnifiedNetwork::getInstance()->release (FlushSendingQueuesOnExit.get(), namesOfOnlyServiceToFlushSendingV);
// warn the module layer that the application is about to close
IModuleManager::getInstance().applicationExit();
// // release the network
// CSock::releaseNetwork ();
//
//
// Remove the window displayer
//
if (WindowDisplayer != NULL)
{
DebugLog->removeDisplayer (WindowDisplayer);
InfoLog->removeDisplayer (WindowDisplayer);
WarningLog->removeDisplayer (WindowDisplayer);
ErrorLog->removeDisplayer (WindowDisplayer);
AssertLog->removeDisplayer (WindowDisplayer);
CommandLog.removeDisplayer (WindowDisplayer);
delete WindowDisplayer;
WindowDisplayer = NULL;
}
nlinfo ("SERVICE: Service released successfully");
}
catch (EFatalError &)
{
// Somebody call nlerror, so we have to quit now, the message already display
// so we don't have to to anything
setExitStatus (EXIT_FAILURE);
}
// remove the stdin monitor thread
IStdinMonitorSingleton::getInstance()->release(); // does nothing if not initialized
// release the module manager
IModuleManager::getInstance().releaseInstance();
// release the network
CSock::releaseNetwork ();
// stop the timeout thread
MyTAT.quit();
if (timeoutThread != NULL)
{
timeoutThread->wait();
delete timeoutThread;
}
CHTimer::display();
CHTimer::displayByExecutionPath ();
CHTimer::displayHierarchical(&CommandLog, true, 64);
CHTimer::displayHierarchicalByExecutionPathSorted (&CommandLog, CHTimer::TotalTime, true, 64);
nlinfo ("SERVICE: Service ends");
return ExitSignalAsked?100+ExitSignalAsked:getExitStatus ();
}
void IService::exit (sint code)
{
nlinfo ("SERVICE: Somebody called IService::exit(), I have to quit");
ExitSignalAsked = code;
}
/// Push a new status on the status stack.
void IService::setCurrentStatus(const std::string &status)
{
// remove the status if it is already in the stack
_ServiceStatusStack.erase(std::remove(_ServiceStatusStack.begin(), _ServiceStatusStack.end(), status), _ServiceStatusStack.end());
// insert the status on top of the stack
_ServiceStatusStack.push_back(status);
}
/// Remove a status from the status stack. If this status is at top of stack, the next status become the current status
void IService::clearCurrentStatus(const std::string &status)
{
// remove the status of the stack
_ServiceStatusStack.erase(std::remove(_ServiceStatusStack.begin(), _ServiceStatusStack.end(), status), _ServiceStatusStack.end());
}
/// Add a tag in the status string
void IService::addStatusTag(const std::string &statusTag)
{
_ServiveStatusTags.insert(statusTag);
}
/// Remove a tag from the status string
void IService::removeStatusTag(const std::string &statusTag)
{
_ServiveStatusTags.erase(statusTag);
}
/// Get the current status with attached tags
std::string IService::getFullStatus() const
{
string result;
// get hold of the status at the top of the status stack
if (!_ServiceStatusStack.empty())
{
result = _ServiceStatusStack.back();
}
// add status tags to the result so far
set<string>::const_iterator first(_ServiveStatusTags.begin()), last(_ServiveStatusTags.end());
for (; first != last; ++first)
{
if (first != _ServiveStatusTags.begin() || !result.empty())
result += " ";
result += *first;
}
// return the result
return result.empty()? "Online": result;
}
/*
* Require to reset the hierarchical timer
*/
void IService::requireResetMeasures()
{
_ResetMeasures = true;
}
/*
*
*/
std::string IService::getServiceUnifiedName () const
{
nlassert (!_ShortName.empty());
string res;
if (!_AliasName.empty())
{
res = _AliasName+"/";
}
res += _ShortName;
if (_SId.get() != 0)
{
res += "-";
res += toString (_SId.get());
}
return res;
}
/*
* Returns the date of launch of the service. Unit: see CTime::getSecondsSince1970()
*/
uint32 IService::getLaunchingDate () const
{
return LaunchingDate;
}
void IService::registerUpdatable(IServiceUpdatable *updatable)
{
_Updatables.insert(updatable);
}
void IService::unregisterUpdatable(IServiceUpdatable *updatable)
{
_Updatables.erase(updatable);
}
//
// Commands and Variables for controling all services
//
NLMISC_CATEGORISED_DYNVARIABLE(nel, string, LaunchingDate, "date of the launching of the program")
{
nlunreferenced(human);
if (get) *pointer = asctime (localtime ((time_t*)&LaunchingDate));
}
NLMISC_CATEGORISED_DYNVARIABLE(nel, string, Uptime, "time from the launching of the program")
{
if (get)
{
if (human)
*pointer = secondsToHumanReadable (CTime::getSecondsSince1970() - LaunchingDate);
else
*pointer = NLMISC::toString(CTime::getSecondsSince1970() - LaunchingDate);
}
else
{
NLMISC::fromString(*pointer, LaunchingDate);
LaunchingDate = CTime::getSecondsSince1970() - LaunchingDate;
}
}
NLMISC_CATEGORISED_VARIABLE(nel, string, CompilationDate, "date of the compilation");
NLMISC_CATEGORISED_VARIABLE(nel, string, CompilationMode, "mode of the compilation");
NLMISC_CATEGORISED_VARIABLE(nel, uint32, NbUserUpdate, "number of time the user IService::update() called");
NLMISC_CATEGORISED_DYNVARIABLE(nel, string, Scroller, "current size in bytes of the sent queue size")
{
nlunreferenced(human);
if (get)
{
// display the scroll text
static string foo = "Welcome to NeL Service! This scroll is used to see the update frequency of the main function and to see if the service is frozen or not. Have a nice day and hope you'll like NeL!!! "
"Welcome to NeL Service! This scroll is used to see the update frequency of the main function and to see if the service is frozen or not. Have a nice day and hope you'll like NeL!!! ";
static int pos = 0;
*pointer = foo.substr ((pos++)%(foo.size()/2), 10);
}
}
NLMISC_CATEGORISED_COMMAND(nel, quit, "exit the service", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if(args.size() != 0) return false;
log.displayNL("User ask me with a command to quit");
ExitSignalAsked = 0xFFFF;
return true;
}
NLMISC_CATEGORISED_COMMAND(nel, brutalQuit, "exit the service brutally", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(log);
nlunreferenced(quiet);
nlunreferenced(human);
if(args.size() != 0) return false;
::exit (0xFFFFFFFF);
return true;
}
#ifdef MUTEX_DEBUG
NLMISC_CATEGORISED_COMMAND(nel, mutex, "display mutex values", "")
{
if(args.size() != 0) return false;
map<CFairMutex*,TMutexLocks> acquiretimes = getNewAcquireTimes();
map<CFairMutex*,TMutexLocks>::iterator im;
for ( im=acquiretimes.begin(); im!=acquiretimes.end(); ++im )
{
log.displayNL( "%d %p %s: %.0f %.0f, called %u times th(%d, %d wait)%s", (*im).second.MutexNum, (*im).first, (*im).second.MutexName.c_str(),
CTime::cpuCycleToSecond((*im).second.TimeToEnter)*1000.0, CTime::cpuCycleToSecond((*im).second.TimeInMutex)*1000.0,
(*im).second.Nb, (*im).second.ThreadHavingTheMutex, (*im).second.WaitingMutex,
(*im).second.Dead?" DEAD":"");
}
return true;
}
#endif // MUTEX_DEBUG
NLMISC_CATEGORISED_COMMAND(nel, serviceInfo, "display information about this service", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(quiet);
nlunreferenced(human);
if(args.size() != 0) return false;
log.displayNL ("Service %s '%s' using NeL ("__DATE__" "__TIME__")", IService::getInstance()->getServiceLongName().c_str(), IService::getInstance()->getServiceUnifiedName().c_str());
log.displayNL ("Service listening port: %d", IService::getInstance()->ListeningPort.get());
log.displayNL ("Service running directory: '%s'", IService::getInstance()->RunningDirectory.c_str());
log.displayNL ("Service log directory: '%s'", IService::getInstance()->LogDirectory.c_str());
log.displayNL ("Service save files directory: '%s'", IService::getInstance()->SaveFilesDirectory.c_str());
log.displayNL ("Service write files directory: '%s'", IService::getInstance()->WriteFilesDirectory.c_str());
log.displayNL ("Service config directory: '%s' config filename: '%s.cfg'", IService::getInstance()->ConfigDirectory.c_str(), IService::getInstance()->_LongName.c_str());
log.displayNL ("Service id: %hu", IService::getInstance()->_SId.get());
log.displayNL ("Service update timeout: %dms", IService::getInstance()->_UpdateTimeout);
log.displayNL ("Service %suse naming service", IService::getInstance()->_DontUseNS?"don't ":"");
log.displayNL ("Service %suse admin executor service", IService::getInstance()->_DontUseAES?"don't ":"");
log.displayNL ("NeL is compiled in %s mode", CompilationMode.c_str());
log.displayNL ("Services arguments: %d args", IService::getInstance()->_Args.size ());
for (uint i = 0; i < IService::getInstance()->_Args.size (); i++)
{
log.displayNL (" argv[%d] = '%s'", i, IService::getInstance()->_Args[i].c_str ());
}
log.displayNL ("Naming service info: %s", CNamingClient::info().c_str());
ICommand::execute ("services", log);
return true;
}
NLMISC_CATEGORISED_COMMAND(nel, resetMeasures, "reset hierarchical timer", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(args);
nlunreferenced(log);
nlunreferenced(quiet);
nlunreferenced(human);
IService::getInstance()->requireResetMeasures();
return true;
}
NLMISC_CATEGORISED_COMMAND(nel, getWinDisplayerInfo, "display the info about the pos and size of the window displayer", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(args);
nlunreferenced(quiet);
nlunreferenced(human);
uint32 x,y,w,h;
IService::getInstance()->WindowDisplayer->getWindowPos (x,y,w,h);
log.displayNL ("Window Displayer : XWinParam = %d; YWinParam = %d; WWinParam = %d; HWinParam = %d;", x, y, w, h);
return true;
}
NLMISC_CATEGORISED_COMMAND(nel, displayConfigFile, "display the variables of the default configfile", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(args);
nlunreferenced(quiet);
nlunreferenced(human);
IService::getInstance()->ConfigFile.display (&log);
return true;
}
NLMISC_CATEGORISED_COMMAND(nel, getUnknownConfigFileVariables, "display the variables from config file that are called but not present", "")
{
nlunreferenced(rawCommandString);
nlunreferenced(args);
nlunreferenced(quiet);
nlunreferenced(human);
log.displayNL ("%d Variables not found in the configfile '%s'", IService::getInstance()->ConfigFile.UnknownVariables.size(), IService::getInstance()->ConfigFile.getFilename().c_str() );
for (uint i = 0; i < IService::getInstance()->ConfigFile.UnknownVariables.size(); i++)
{
log.displayNL (" %s", IService::getInstance()->ConfigFile.UnknownVariables[i].c_str());
}
return true;
}
// -1 = service is quitting
// 0 = service is not connected
// 1 = service is running
// 2 = service is launching
// 3 = service failed launching
NLMISC_CATEGORISED_DYNVARIABLE(nel, string, State, "Set this value to 0 to shutdown the service and 1 to start the service")
{
nlunreferenced(human);
// read or write the variable
if (get)
{
}
else
{
if (IService::getInstance()->getServiceShortName() == "AES" || IService::getInstance()->getServiceShortName() == "AS")
{
nlinfo ("SERVICE: I can't set State=0 because I'm the admin and I should never quit");
}
else if (*pointer == "0" || *pointer == "2")
{
// ok, we want to set the value to false, just quit
nlinfo ("SERVICE: User ask me with a command to quit using the State variable");
ExitSignalAsked = 0xFFFE;
IService *srv = IService::getInstance();
if( srv == NULL )
{
return;
}
srv->setCurrentStatus("Quitting");
}
else
{
nlwarning ("SERVICE: Unknown value for State '%s'", (*pointer).c_str());
}
}
// whether reading or writing, the internal value of the state variable should end up as the result of getFullStatus()
IService *srv = IService::getInstance();
if( srv == NULL )
{
return;
}
*pointer= srv->getFullStatus();
}
NLMISC_CATEGORISED_DYNVARIABLE(nel, uint32, ShardId, "Get value of shardId set for this particular service")
{
nlunreferenced(human);
// read or write the variable
if (get)
{
*pointer = IService::getInstance()->getShardId();
}
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPULoad, "Get instant CPU load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessLoad, "Get instant CPU load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUUserLoad, "Get instant CPU user load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUSytemLoad, "Get instant CPU system load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUNiceLoad, "Get instant CPU nice processes load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUIOWaitLoad, "Get instant CPU IO wait load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessUserLoad, "Get instant CPU user load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessSystemLoad, "Get instant CPU system load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPULoad, "Get instant CPU load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessLoad, "Get instant CPU load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUUserLoad, "Get instant CPU user load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUSytemLoad, "Get instant CPU system load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUNiceLoad, "Get instant CPU nice processes load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUIOWaitLoad, "Get instant CPU IO wait load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessUserLoad, "Get instant CPU user load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessSystemLoad, "Get instant CPU system load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(CCPUTimeStat::Mean); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPULoad, "Get instant CPU load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessLoad, "Get instant CPU load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUUserLoad, "Get instant CPU user load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUSytemLoad, "Get instant CPU system load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUNiceLoad, "Get instant CPU nice processes load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUIOWaitLoad, "Get instant CPU IO wait load of the server")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessUserLoad, "Get instant CPU user load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(CCPUTimeStat::Peak); }
}
NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessSystemLoad, "Get instant CPU system load of the process/service")
{
nlunreferenced(human);
// read or write the variable
if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(CCPUTimeStat::Peak); }
}
} //NLNET