// NeL - MMORPG Framework // 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 . #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 # include #elif defined NL_OS_UNIX # include #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 UserSpeedLoop ("nel", "UserSpeedLoop", "duration of the last user loop (in ms)", 10, false); CVariable NetSpeedLoop ("nel", "NetSpeedLoop", "duration of the last network loop (in ms)", 10, false); /// The time passed in callback during the loop CVariable 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 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 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 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 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 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 (""); 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 > 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 (const NLNET::ESocket &) { localhost = ""; } // 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(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 (const 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 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; iConfigFile.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::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 &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 (const 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 (const 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 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 (const 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::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 acquiretimes = getNewAcquireTimes(); map::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