Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / net / service.cpp
blobf5af032365c54ee69910c9cd33fecaaa04964e80
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdnet.h"
23 // Includes
26 #ifdef NL_OS_WINDOWS
27 // these defines is for IsDebuggerPresent(). it'll not compile on windows 95
28 // just comment this and the IsDebuggerPresent to compile on windows 95
29 # include <direct.h>
30 #elif defined NL_OS_UNIX
31 # include <unistd.h>
32 #endif
34 #include "nel/misc/config_file.h"
35 #include "nel/misc/displayer.h"
36 #include "nel/misc/mutex.h"
37 #include "nel/misc/window_displayer.h"
38 #include "nel/misc/gtk_displayer.h"
39 #include "nel/misc/win_displayer.h"
40 #include "nel/misc/path.h"
41 #include "nel/misc/hierarchical_timer.h"
42 #include "nel/misc/report.h"
43 #include "nel/misc/system_info.h"
44 #include "nel/misc/timeout_assertion_thread.h"
46 #include "nel/net/naming_client.h"
47 #include "nel/net/service.h"
48 #include "nel/net/unified_network.h"
49 #include "nel/net/net_displayer.h"
50 #include "nel/net/email.h"
51 #include "nel/net/varpath.h"
52 #include "nel/net/admin.h"
53 #include "nel/net/module_manager.h"
54 #include "nel/net/transport_class.h"
56 #include "stdin_monitor_thread.h"
60 // Namespaces
63 using namespace std;
64 using namespace NLMISC;
66 namespace NLNET
71 // Constants
74 static const sint Signal[] = {
75 SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
78 static const char *SignalName[]=
80 "SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV", "SIGTERM"
83 static const char* NegFiltersNames[] =
85 "NegFiltersDebug",
86 "NegFiltersInfo",
87 "NegFiltersWarning",
88 "NegFiltersAssert",
89 "NegFiltersError",
95 // Variables
98 TUnifiedCallbackItem EmptyCallbackArray[1] = { { "", NULL } };
100 // class static member
101 IService *IService::_Instance = NULL;
103 static sint ExitSignalAsked = 0;
105 // services stat
106 CVariable<sint32> UserSpeedLoop ("nel", "UserSpeedLoop", "duration of the last user loop (in ms)", 10, false);
107 CVariable<sint32> NetSpeedLoop ("nel", "NetSpeedLoop", "duration of the last network loop (in ms)", 10, false);
108 /// The time passed in callback during the loop
109 CVariable<uint32> L5CallbackTime("nel", "L5CallbackTime", "Time passed in the L5 callback function in the last loop (in ms)", 0, 100 );
110 /// The number of L5 callback treated
111 CVariable<uint32> L5CallbackCount("nel", "L5CallbackCount", "The number of layer 5 callback received in the last loop", 0, 100 );
113 extern uint32 TotalCallbackCalled;
114 extern uint32 TimeInCallback;
115 uint32 LastTotalCallbackCalled = 0;
116 uint32 LastTimeInCallback = 0;
120 // this is the thread that initialized the signal redirection
121 // we'll ignore other thread signals
122 static size_t SignalisedThread;
124 static CFileDisplayer fd;
125 static CNetDisplayer commandDisplayer(false);
126 //static CLog commandLog;
128 static string CompilationDate;
129 static uint32 LaunchingDate;
131 static uint32 NbUserUpdate = 0;
133 string CompilationMode = nlMode;
135 //static bool Bench = false;
137 CVariable<bool> Bench ("nel", "Bench", "1 if benching 0 if not", 0, true);
139 // This produce an assertion in the thread if the update loop is too slow
140 static CTimeoutAssertionThread MyTAT;
141 static void UpdateAssertionThreadTimeoutCB(IVariable &var) { uint32 timeOut; fromString(var.toString(), timeOut); MyTAT.timeout(timeOut); }
142 static CVariable<uint32> UpdateAssertionThreadTimeout("nel", "UpdateAssertionThreadTimeout", "in millisecond, timeout before thread assertion", 0, 0, true, UpdateAssertionThreadTimeoutCB);
144 // Flag to enable/disable the flushing of the sending queues when the service is shut down
145 // Default: false (matches the former behavior)
146 // Set it to true in services that need to send data on exit (for instance in their release() method)
147 CVariable<bool> FlushSendingQueuesOnExit("nel", "FlushSendingQueuesOnExit",
148 "Flag to enable/disable the flushing of the sending queues when the service is shut down", false, 0, true );
150 // If FlushSendingQueuesOnExit is on, only the sending queues to these specified services will be flushed
151 // Format: service short names separated by ':'
152 // Default: "" (all will be flushed if FlushSendingQueuesOnExit is on, none if it is off)
153 CVariable<string> NamesOfOnlyServiceToFlushSending("nel", "NamesOfOnlyServiceToFlushSending",
154 "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 );
158 // Signals managing
161 // This function is called when a signal comes
162 static void sigHandler(int Sig)
164 // redirect the signal for the next time
165 signal(Sig, sigHandler);
167 // find the signal
168 for (int i = 0; i < (int)(sizeof(Signal)/sizeof(Signal[0])); i++)
170 if (Sig == Signal[i])
172 if (getThreadId () != SignalisedThread)
174 nldebug ("SERVICE: Not the main thread (%u, %u) received the signal (%s, %d), ignore it", getThreadId (), SignalisedThread, SignalName[i],Sig);
175 return;
177 else
179 nlinfo ("SERVICE: Signal %s (%d) received", SignalName[i], Sig);
180 switch (Sig)
182 // Note: SIGKILL (9) and SIGSTOP (19) can't be trapped
183 case SIGINT :
184 if (IService::getInstance()->haveLongArg("nobreak"))
186 // ignore ctrl-c
187 nlinfo("SERVICE: Ignoring ctrl-c");
188 return;
190 case SIGABRT :
191 case SIGILL :
192 case SIGSEGV :
193 case SIGTERM :
194 // you should not call a function and system function like printf in a SigHandle because
195 // signal-handler routines are usually called asynchronously when an interrupt occurs.
196 if (ExitSignalAsked == 0)
198 nlinfo ("SERVICE: Receive a signal that said that i must exit");
199 ExitSignalAsked = Sig;
200 return;
202 else
204 nlinfo ("SERVICE: Signal already received, launch the brutal exit");
205 exit (EXIT_FAILURE);
207 break;
212 nlwarning ("SERVICE: Unknown signal received (%d)", Sig);
215 // Initialise the signal redirection
216 static void initSignal()
218 SignalisedThread = getThreadId ();
219 #ifdef NL_DEBUG
220 // in debug mode, we only trap the SIGINT signal (for ctrl-c handling)
221 //signal(Signal[3], sigHandler);
222 //nldebug("Signal : %s (%d) trapped", SignalName[3], Signal[3]);
223 #else
224 // in release, redirect all signals
225 // don't redirect now because too hard to debug...
226 // for (int i = 0; i < (int)(sizeof(Signal)/sizeof(Signal[0])); i++)
227 // {
228 // signal(Signal[i], sigHandler);
229 // nldebug("Signal %s (%d) trapped", SignalName[i], Signal[i]);
230 // }
232 if (IService::getInstance()->haveLongArg("nobreak"))
234 signal(Signal[3], sigHandler);
236 #endif
239 void cbDirectoryChanged (IVariable &var)
241 IService *instance = IService::getInstance();
243 // Convert to full path if required
244 // (warning: ConvertSavesFilesDirectoryToFullPath, read from the config file, won't be ready for the initial variable assigments done before the config file has been loaded)
245 string vp = var.toString();
246 if ((var.getName() != "SaveFilesDirectory") || instance->ConvertSavesFilesDirectoryToFullPath.get())
248 vp = CPath::getFullPath(vp);
249 var.fromString(vp);
251 nlinfo ("SERVICE: '%s' changed to '%s'", var.getName().c_str(), vp.c_str());
253 // Update the running directory if needed
254 if (var.getName() == "RunningDirectory")
256 CPath::setCurrentPath(vp);
259 // Call the callback if provided
260 if (instance->_DirectoryChangedCBI)
261 instance->_DirectoryChangedCBI->onVariableChanged(var);
266 // Service built-in callbacks
269 void cbReceiveShardId (CMessage& msgin, const string &serviceName, TServiceId serviceId)
271 uint32 shardId;
272 msgin.serial(shardId);
274 if (IService::getInstance()->getDontUseNS())
276 // we don't use NS, so shard ID message don't concern us
277 return;
280 if (serviceName != "WS")
282 nlwarning("SERVICE: received unauthorized R_SH_ID callback from service %s-%uh asking to set ShardId to %d", serviceName.c_str(), serviceId.get(), shardId);
283 return;
286 nlinfo("SERVICE: ShardId is %u", shardId);
287 IService::getInstance()->setShardId( shardId );
290 std::string IService::getServiceStatusString() const
292 static string emptyString;
294 return emptyString;
299 void IService::anticipateShardId( uint32 shardId )
301 if ( ! ((_ShardId == DEFAULT_SHARD_ID) || (shardId == _ShardId)) )
302 nlerror( "IService::anticipateShardId() overwrites %u with %u", _ShardId, shardId );
303 _ShardId = shardId;
307 void IService::setShardId( uint32 shardId )
309 if ( ! ((_ShardId == DEFAULT_SHARD_ID) || (shardId == _ShardId)) )
310 nlwarning( "SERVICE: The shardId from the WS (%u) is different from the anticipated shardId (%u)", shardId, _ShardId );
311 _ShardId = shardId;
314 TUnifiedCallbackItem builtinServiceCallbacks [] =
316 { "R_SH_ID", cbReceiveShardId },
322 // Class implementation
325 // Ctor
326 IService::IService() :
327 WindowDisplayer(0),
328 WriteFilesDirectory("nel", "WriteFilesDirectory", "directory where to save generic shard information (packed_sheets for example)", ".", 0, true, cbDirectoryChanged),
329 SaveFilesDirectory("nel", "SaveFilesDirectory", "directory where to save specific shard information (shard time for example)", ".", 0, true, cbDirectoryChanged),
330 ConvertSavesFilesDirectoryToFullPath("nel", "ConvertSaveFilesDirectoryToFullPath", "If true (default), the provided SaveFilesDirectory will be converted to a full path (ex: saves -> /home/dir/saves)", true, 0, true ),
331 ListeningPort("nel", "ListeningPort", "listening port for this service", 0, 0, true),
332 _RecordingState(CCallbackNetBase::Off),
333 _UpdateTimeout(100),
334 _SId(0),
335 _ExitStatus(0),
336 _Initialized(false),
337 ConfigDirectory("nel", "ConfigDirectory", "directory where config files are", ".", 0, true, cbDirectoryChanged),
338 LogDirectory("nel", "LogDirectory", "directory where the service is logging", ".", 0, true, cbDirectoryChanged),
339 RunningDirectory("nel", "RunningDirectory", "directory where the service is running on", ".", 0, true, cbDirectoryChanged),
340 Version("nel", "Version", "Version of the shard", ""),
341 _CallbackArray (0),
342 _CallbackArraySize (0),
343 _DontUseNS(false),
344 _DontUseAES(false),
345 _ResetMeasures(false),
346 _ShardId(0),
347 _ClosureClearanceStatus(CCMustRequestClearance),
348 _RequestClosureClearanceCallback(NULL),
349 _DirectoryChangedCBI(NULL)
351 // Singleton
352 _Instance = this;
354 // register in the safe singleton registry
355 INelContext::getInstance().setSingletonPointer("IService", this);
358 IService::~IService()
360 // unregister the singleton
361 INelContext::getInstance().releaseSingletonPointer("IService", this);
365 bool IService::haveArg (char argName) const
367 for (uint32 i = 0; i < _Args.size(); i++)
369 if (_Args[i].size() >= 2 && _Args[i][0] == '-')
371 if (_Args[i][1] == argName)
373 return true;
377 return false;
381 string IService::getArg (char argName) const
383 for (uint32 i = 0; i < _Args.size(); i++)
385 if (_Args[i].size() >= 2 && _Args[i][0] == '-')
387 if (_Args[i][1] == argName)
389 /* Remove the first and last '"' :
390 -c"C:\Documents and Settings\toto.tmp"
391 will return :
392 C:\Documents and Settings\toto.tmp
394 uint begin = 2;
395 if (_Args[i].size() < 3)
396 return "";
397 //throw Exception ("Parameter '-%c' is malformed, missing content", argName);
399 if (_Args[i][begin] == '"')
400 begin++;
402 // End
403 uint size = (uint)_Args[i].size();
404 if (size && _Args[i][size-1] == '"')
405 size--;
406 size = (uint)(std::max((int)0, (int)size-(int)begin));
407 return _Args[i].substr(begin, size);
411 throw Exception ("Parameter '-%c' is not found in command line", argName);
415 bool IService::haveLongArg (const char* argName) const
417 for (uint32 i = 0; i < _Args.size(); i++)
419 if (_Args[i].left(2)=="--" && _Args[i].leftCrop(2).splitTo('=')==argName)
421 return true;
424 return false;
428 string IService::getLongArg (const char* argName) const
430 for (uint32 i = 0; i < _Args.size(); i++)
432 if (_Args[i].left(2)=="--" && _Args[i].leftCrop(2).splitTo('=')==argName)
434 NLMISC::CSString val= _Args[i].splitFrom('=');
435 if (!val.empty())
437 return val.unquoteIfQuoted();
439 if (i+1<_Args.size() && _Args[i+1].c_str()[0]!='-')
441 return _Args[i+1].unquoteIfQuoted();
444 return std::string();
447 return std::string();
451 void IService::setArgs (const char *args)
453 _Args.push_back ("<ProgramName>");
455 string sargs (args);
456 string::size_type pos1 = 0, pos2 = 0;
460 // Look for the first non space character
461 pos1 = sargs.find_first_not_of (" ", pos2);
462 if (pos1 == string::npos) break;
464 // Look for the first space or "
465 pos2 = sargs.find_first_of (" \"", pos1);
466 if (pos2 != string::npos)
468 // " ?
469 if (sargs[pos2] == '"')
471 // Look for the final \"
472 pos2 = sargs.find_first_of ("\"", pos2+1);
473 if (pos2 != string::npos)
475 // Look for the first space
476 pos2 = sargs.find_first_of (" ", pos2+1);
481 // Compute the size of the string to extract
482 string::difference_type length = (pos2 != string::npos) ? pos2-pos1 : string::npos;
484 string tmp = sargs.substr (pos1, length);
485 _Args.push_back (tmp);
487 while (pos2 != string::npos);
490 void IService::setArgs (int argc, const char **argv)
492 for (sint i = 0; i < argc; i++)
494 _Args.push_back (argv[i]);
498 void IService::setArgs(int argc, const wchar_t **argv)
500 for (sint i = 0; i < argc; i++)
502 _Args.push_back(nlWideToUtf8(argv[i]));
506 void cbLogFilter (CConfigFile::CVar &var)
508 CLog *log = NULL;
509 if (var.Name == "NegFiltersDebug")
511 log = DebugLog;
513 else if (var.Name == "NegFiltersInfo")
515 log = InfoLog;
517 else if (var.Name == "NegFiltersWarning")
519 log = WarningLog;
521 else if (var.Name == "NegFiltersAssert")
523 log = AssertLog;
525 else if (var.Name == "NegFiltersError")
527 log = ErrorLog;
529 else
531 nlstop;
534 nlinfo ("SERVICE: Updating %s from config file", var.Name.c_str());
536 // remove all old filters from config file
537 CConfigFile::CVar &oldvar = IService::getInstance()->ConfigFile.getVar (var.Name);
538 for (uint j = 0; j < oldvar.size(); j++)
540 log->removeFilter (oldvar.asString(j).c_str());
543 // add all new filters from config file
544 for (uint i = 0; i < var.size(); i++)
546 log->addNegativeFilter (var.asString(i).c_str());
550 void cbExecuteCommands (CConfigFile::CVar &var)
552 for (uint i = 0; i < var.size(); i++)
554 ICommand::execute (var.asString(i), IService::getInstance()->CommandLog);
561 // The main function of the service
564 sint IService::main (const char *serviceShortName, const char *serviceLongName, uint16 servicePort, const char *configDir, const char *logDir, const char *compilationDate)
566 bool userInitCalled = false;
567 CConfigFile::CVar *var = NULL;
569 IThread *timeoutThread = NULL;
571 // a short name service can't be a number
572 uint tmp;
573 nlassert (!fromString(serviceShortName, tmp));
577 createDebug();
578 // init the module manager
579 IModuleManager::getInstance();
581 // Init parameters
584 // at the very beginning, eventually wrote a file with the pid
585 if (haveLongArg("writepid"))
587 // use legacy C primitives
588 FILE *fp = nlfopen("pid.state", "wt");
589 if (fp)
591 fprintf(fp, "%u", getpid());
592 fclose(fp);
596 _ShortName = serviceShortName;
597 CLog::setProcessName (_ShortName);
599 // get the path where to run the service if any in the command line
600 if (haveArg('A'))
601 RunningDirectory = CPath::standardizePath(getArg('A'));
603 ConfigDirectory = CPath::standardizePath(configDir);
604 LogDirectory = CPath::standardizePath(logDir);
605 _LongName = serviceLongName;
607 CompilationDate = compilationDate;
609 LaunchingDate = CTime::getSecondsSince1970();
611 ListeningPort = servicePort;
613 // setDefaultEmailParams ("gw.nevrax.com", "", "cado@nevrax.com");
617 // Load the config file
620 // get the config file dir if any in the command line
621 if (haveArg('C'))
622 ConfigDirectory = CPath::standardizePath(getArg('C'));
624 string cfn = ConfigDirectory.c_str() + _LongName + ".cfg";
625 if (!CFile::fileExists(ConfigDirectory.c_str() + _LongName + ".cfg"))
627 // check if the default exists
628 if (!CFile::fileExists(ConfigDirectory.c_str() + _LongName + "_default.cfg"))
630 nlerror ("SERVICE: Neither the config file '%s' nor the default one can be found, can't launch the service", cfn.c_str());
632 else
634 // create the basic .cfg that link the default one
635 FILE *fp = nlfopen (cfn, "w");
636 if (fp == NULL)
638 nlerror ("SERVICE: Can't create config file '%s'", cfn.c_str());
640 fprintf(fp, "// link the default config file for %s\n", _LongName.c_str());
641 fprintf(fp, "RootConfigFilename = \"%s_default.cfg\";\n", _LongName.c_str());
642 fclose (fp);
646 ConfigFile.load (cfn);
648 // setup variable with config file variable
649 IVariable::init (ConfigFile);
651 if (ConfigFile.exists("DefaultEmailSMTP") && ConfigFile.exists("DefaultEmailTo"))
652 NLNET::setDefaultEmailParams(
653 ConfigFile.getVar("DefaultEmailSMTP").asString(),
654 ConfigFile.exists("DefaultEmailFrom")
655 ? ConfigFile.getVar("DefaultEmailFrom").asString()
656 : "service@opennel.org",
657 ConfigFile.getVar("DefaultEmailTo").asString());
660 // Set the shard Id
663 if ((var = ConfigFile.getVarPtr("NoWSShardId")) != NULL)
665 _ShardId = var->asInt();
667 else
669 // something high enough as default
670 _ShardId = DEFAULT_SHARD_ID;
673 if (haveArg('Z'))
675 string s = IService::getInstance()->getArg('Z');
676 if (s == "u")
678 // do not release the module manager
680 else
682 // release the module manager
683 IModuleManager::getInstance().releaseInstance();
686 return 0;
689 // we have to call this again because the config file can changed this variable but the cmd line is more prioritary
690 if (haveArg('A'))
691 RunningDirectory = CPath::standardizePath(getArg('A'));
695 // Init debug/log stuffs (must be first things otherwise we can't log if errors)
698 // get the log dir if any in the command line
699 if (haveArg('L'))
700 LogDirectory = CPath::standardizePath(getArg('L'));
702 changeLogDirectory (LogDirectory);
704 bool noLog= (ConfigFile.exists ("DontLog")) && (ConfigFile.getVar("DontLog").asInt() == 1);
705 noLog|=haveLongArg("nolog");
706 if (!noLog)
708 // we create the log with service name filename ("test_service_ALIAS.log" for example)
709 string logname = LogDirectory.toString() + _LongName;
710 if (haveArg('N'))
711 logname += "_" + toLowerAscii(getArg('N'));
712 logname += ".log";
713 fd.setParam (logname, false);
715 DebugLog->addDisplayer (&fd);
716 InfoLog->addDisplayer (&fd);
717 WarningLog->addDisplayer (&fd);
718 AssertLog->addDisplayer (&fd);
719 ErrorLog->addDisplayer (&fd);
720 CommandLog.addDisplayer (&fd, true);
723 bool dontUseStdIn= (ConfigFile.exists ("DontUseStdIn")) && (ConfigFile.getVar("DontUseStdIn").asInt() == 1);
724 if (!dontUseStdIn)
726 IStdinMonitorSingleton::getInstance()->init();
730 // Init the hierarchical timer
733 CHTimer::startBench(false, true);
734 CHTimer::endBench();
737 // Set the assert mode
740 if (ConfigFile.exists ("Assert"))
741 setAssert (ConfigFile.getVar("Assert").asInt() == 1);
744 // Set the update timeout if found in the cfg
746 if ((var = ConfigFile.getVarPtr("UpdateTimeout")) != NULL)
748 _UpdateTimeout = var->asInt();
752 // Set the negative filter from the config file
755 for(const char **name = NegFiltersNames; *name; name++)
757 if ((var = ConfigFile.getVarPtr (*name)) != NULL)
759 ConfigFile.setCallback (*name, cbLogFilter);
760 cbLogFilter(*var);
764 ConfigFile.setCallback ("Commands", cbExecuteCommands);
765 if ((var = ConfigFile.getVarPtr ("Commands")) != NULL)
767 cbExecuteCommands(*var);
771 // Command line start
773 commandStart ();
776 // Create the window if needed
779 if ((var = ConfigFile.getVarPtr ("WindowStyle")) != NULL)
781 string disp = var->asString ();
782 #ifdef NL_USE_GTK
783 if (disp == "GTK")
785 WindowDisplayer = new CGtkDisplayer ("DEFAULT_WD");
787 #endif // NL_USE_GTK
789 #ifdef NL_OS_WINDOWS
790 if (disp == "WIN")
792 WindowDisplayer = new CWinDisplayer ("DEFAULT_WD");
794 #endif // NL_OS_WINDOWS
796 if (WindowDisplayer == NULL && disp != "NONE")
798 nlinfo ("SERVICE: Unknown value for the WindowStyle (should be GTK, WIN or NONE), use no window displayer");
802 vector <pair<string,uint> > displayedVariables;
803 //uint speedNetLabel, speedUsrLabel, rcvLabel, sndLabel, rcvQLabel, sndQLabel, scrollLabel;
804 if (WindowDisplayer != NULL)
807 // Init window param if necessary
810 sint x=-1, y=-1, w=-1, h=-1, fs=10, history=-1;
811 bool iconified = false, ww = false;
812 string fn;
814 if ((var = ConfigFile.getVarPtr("XWinParam")) != NULL) x = var->asInt();
815 if ((var = ConfigFile.getVarPtr("YWinParam")) != NULL) y = var->asInt();
816 if ((var = ConfigFile.getVarPtr("WWinParam")) != NULL) w = var->asInt();
817 if ((var = ConfigFile.getVarPtr("HWinParam")) != NULL) h = var->asInt();
818 if ((var = ConfigFile.getVarPtr("HistoryWinParam")) != NULL) history = var->asInt();
819 if ((var = ConfigFile.getVarPtr("IWinParam")) != NULL) iconified = var->asInt() == 1;
820 if ((var = ConfigFile.getVarPtr("FontSize")) != NULL) fs = var->asInt();
821 if ((var = ConfigFile.getVarPtr("FontName")) != NULL) fn = var->asString();
822 if ((var = ConfigFile.getVarPtr("WordWrap")) != NULL) ww = var->asInt() == 1;
824 if (haveArg('I')) iconified = true;
826 WindowDisplayer->create (string("*INIT* ") + _ShortName + " " + _LongName, iconified, x, y, w, h, history, fs, fn, ww, &CommandLog);
828 DebugLog->addDisplayer (WindowDisplayer);
829 InfoLog->addDisplayer (WindowDisplayer);
830 WarningLog->addDisplayer (WindowDisplayer);
831 ErrorLog->addDisplayer (WindowDisplayer);
832 AssertLog->addDisplayer (WindowDisplayer);
833 CommandLog.addDisplayer(WindowDisplayer, true);
835 // adding default displayed variables
836 displayedVariables.push_back(make_pair(string("NetLop|NetSpeedLoop"), WindowDisplayer->createLabel ("NetLop")));
837 displayedVariables.push_back(make_pair(string("UsrLop|UserSpeedLoop"), WindowDisplayer->createLabel ("UsrLop")));
838 displayedVariables.push_back(make_pair(string("|Scroller"), WindowDisplayer->createLabel ("NeL Rulez")));
840 CConfigFile::CVar *v = ConfigFile.getVarPtr("DisplayedVariables");
841 if (v != NULL)
843 for (uint i = 0; i < v->size(); i++)
845 displayedVariables.push_back(make_pair(v->asString(i), WindowDisplayer->createLabel (v->asString(i).c_str())));
850 nlinfo ("SERVICE: Starting Service '%s' using NeL (" __DATE__ " " __TIME__ ") compiled %s", _ShortName.c_str(), CompilationDate.c_str());
851 nlinfo ("SERVICE: On OS: %s", CSystemInfo::getOS().c_str());
853 setExitStatus (EXIT_SUCCESS);
856 // Redirect signal if needed (in release mode only)
859 #ifdef NL_OS_WINDOWS
860 # ifdef NL_NO_DEBUG
861 initSignal();
862 # else
863 // don't install signal is the application is started in debug mode
864 if (IsDebuggerPresent ())
866 //nlinfo("Running with the debugger, don't redirect signals");
867 initSignal();
869 else
871 //nlinfo("Running without the debugger, redirect SIGINT signal");
872 initSignal();
874 # endif
875 #else // NL_OS_UNIX
876 initSignal();
877 #endif
881 // Ignore SIGPIPE (broken pipe) on unix system
884 #ifdef NL_OS_UNIX
885 // Ignore the SIGPIPE signal
886 sigset_t SigList;
887 bool IgnoredPipe = true;
888 if (sigemptyset (&SigList) == -1)
890 perror("sigemptyset()");
891 IgnoredPipe = false;
894 if (sigaddset (&SigList, SIGPIPE) == -1)
896 perror("sigaddset()");
897 IgnoredPipe = false;
900 if (sigprocmask (SIG_BLOCK, &SigList, NULL) == -1)
902 perror("sigprocmask()");
903 IgnoredPipe = false;
905 nldebug ("SERVICE: SIGPIPE %s", IgnoredPipe?"Ignored":"Not Ignored");
906 #endif // NL_OS_UNIX
910 // Initialize the network system
913 string localhost;
916 // Initialize WSAStartup and network stuffs
917 CSock::initNetwork();
919 // Get the localhost name
920 localhost = CInetAddress::localHost().hostName();
922 catch (const NLNET::ESocket &)
924 localhost = "<UnknownHost>";
927 // Set the localhost name and service name to the logger
928 CLog::setProcessName (localhost+"/"+_ShortName);
929 nlinfo ("SERVICE: Host: %s", localhost.c_str());
932 // Initialize server parameters
935 // set the listen port if there are a port arg in the command line
936 if (haveArg('P'))
938 NLMISC::fromString(getArg('P'), ListeningPort);
941 // set the aes aliasname if present in cfg file
942 CConfigFile::CVar *varAliasName= ConfigFile.getVarPtr("AESAliasName");
943 if (varAliasName != NULL)
945 _AliasName = varAliasName->asString();
946 nlinfo("SERVICE: Setting alias name to: '%s'",_AliasName.c_str());
949 // set the aes aliasname if is present in the command line
950 if (haveArg('N'))
952 _AliasName = getArg('N');
953 nlinfo("SERVICE: Setting alias name to: '%s'",_AliasName.c_str());
956 // Load the recording state from the config file
957 if ((var = ConfigFile.getVarPtr ("Rec")) != NULL)
959 string srecstate = toUpperAscii(var->asString());
960 if ( srecstate == "RECORD" )
962 _RecordingState = CCallbackNetBase::Record;
963 nlinfo( "SERVICE: Service recording messages" );
965 else if ( srecstate == "REPLAY" )
967 _RecordingState = CCallbackNetBase::Replay;
968 nlinfo( "SERVICE: Service replaying messages" );
970 else
972 _RecordingState = CCallbackNetBase::Off;
975 else
977 // Not found
978 _RecordingState = CCallbackNetBase::Off;
981 // Load the default stream format
982 if ((var = ConfigFile.getVarPtr ("StringMsgFormat")) != NULL)
984 CMessage::setDefaultStringMode( var->asInt() == 1 );
986 else
988 // Not found => binary
989 CMessage::setDefaultStringMode( false );
994 /// Layer5 Startup
997 // get the sid
998 if ((var = ConfigFile.getVarPtr ("SId")) != NULL)
1000 sint32 sid = var->asInt();
1001 if (sid<=0 || sid>255)
1003 nlwarning("SERVICE: Bad SId value in the config file, %d is not in [0;255] range", sid);
1004 _SId.set(0);
1006 else
1008 _SId.set(static_cast<uint16>(sid));
1011 else
1013 // ok, SId not found, use dynamic sid
1014 _SId.set(0);
1018 // look if we don't want to use NS
1019 if ((var = ConfigFile.getVarPtr ("DontUseNS")) != NULL)
1021 // if we set the value in the config file, get it
1022 _DontUseNS = (var->asInt() == 1);
1024 else
1026 // if not, we use ns only if service is not ns, ls, aes, as
1027 _DontUseNS = false;
1029 if (haveLongArg("nons"))
1031 // command line override
1032 _DontUseNS = true;
1036 // Register all network associations (must be before the CUnifiedNetwork::getInstance()->init)
1039 if ((var = ConfigFile.getVarPtr ("Networks")) != NULL)
1041 for (uint8 i = 0; i < var->size (); i++)
1042 CUnifiedNetwork::getInstance()->addNetworkAssociation (var->asString(i), i);
1045 if ((var = ConfigFile.getVarPtr ("DefaultNetworks")) != NULL)
1047 for (uint8 i = 0; i < var->size (); i++)
1048 CUnifiedNetwork::getInstance()->addDefaultNetwork(var->asString(i));
1051 // normal setup for the common services
1052 if (!_DontUseNS)
1054 bool ok = false;
1055 while (!ok)
1057 string LSAddr;
1059 if (haveArg('B'))
1061 // if the naming service address is set on the command line, get it (overwrite the cfg)
1062 LSAddr = getArg('B');
1064 else
1066 // else read the naming service address from the config file
1067 LSAddr = ConfigFile.getVar ("NSHost").asString();
1070 // if there's no port to the NS, use the default one 50000
1071 if (LSAddr.find(":") == string::npos)
1072 LSAddr += ":50000";
1074 CInetAddress loc(LSAddr);
1077 // todo: check if app not closed by user, or you get stuck here
1079 if ( CUnifiedNetwork::getInstance()->init (&loc, _RecordingState, _ShortName, ListeningPort, _SId) )
1081 ok = true;
1083 else
1085 nlinfo( "SERVICE: Exiting..." );
1086 beep( 880, 400 );
1087 beep( 440, 400 );
1088 beep( 220, 400 );
1090 // remove the stdin monitor thread
1091 IStdinMonitorSingleton::getInstance()->release(); // does nothing if not initialized
1093 // release the module manager
1094 IModuleManager::getInstance().releaseInstance();
1096 return 10;
1099 catch (const ESocketConnectionFailed &)
1101 nlinfo ("SERVICE: Could not connect to the Naming Service (%s). Retrying in a few seconds...", loc.asString().c_str());
1102 nlSleep (5000);
1106 else
1108 CUnifiedNetwork::getInstance()->init(NULL, _RecordingState, _ShortName, ListeningPort, _SId);
1111 // get the hostname for later use
1112 _HostName = CInetAddress::localHost().hostName();
1114 // At this point, the _SId must be ok if we use the naming service.
1115 // 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
1117 if(!_DontUseNS)
1119 nlassert (_SId.get() != 0);
1123 // Connect to the local AES and send identification
1126 // look if we don't want to use NS
1127 if ((var = ConfigFile.getVarPtr ("DontUseAES")) != NULL)
1129 // if we set the value in the config file, get it
1130 _DontUseAES = var->asInt() == 1;
1132 else
1134 // if not, we use aes only if service is not aes or as
1135 _DontUseAES = false;
1138 initAdmin (_DontUseAES);
1140 while (NLNET::CUnifiedNetwork::getInstance()->tryFlushAllQueues() != 0)
1142 nlSleep(10);
1146 // Add callback array
1149 // add inner service callback array
1150 NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(builtinServiceCallbacks, sizeof(builtinServiceCallbacks)/sizeof(builtinServiceCallbacks[0]));
1152 // add callback set in the NLNET_SERVICE_MAIN macro
1153 NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(_CallbackArray, _CallbackArraySize);
1156 // Now we have the service id, we can set the entites id generator
1159 NLMISC::CEntityId::setServiceId(TServiceId8(_SId).get());
1161 // Set the localhost name and service name and the sid
1162 CLog::setProcessName (localhost+"/"+_ShortName+"-"+toString(_SId.get()));
1166 // Add default pathes
1169 if ((var = ConfigFile.getVarPtr ("IgnoredFiles")) != NULL)
1171 for (uint i = 0; i < var->size(); i++)
1173 CPath::addIgnoredDoubleFile (var->asString(i));
1177 if ((var = ConfigFile.getVarPtr ("Paths")) != NULL)
1179 for (uint i = 0; i < var->size(); i++)
1181 CPath::addSearchPath (NLMISC::expandEnvironmentVariables(var->asString(i)), true, false);
1185 if ((var = ConfigFile.getVarPtr ("PathsNoRecurse")) != NULL)
1187 for (uint i = 0; i < var->size(); i++)
1189 CPath::addSearchPath (NLMISC::expandEnvironmentVariables(var->asString(i)), false, false);
1193 // if we can, try to setup where to save files
1194 if (IService::getInstance()->haveArg('W'))
1196 // use the command line param if set (must be done after the config file has been loaded)
1197 SaveFilesDirectory = IService::getInstance()->getArg('W');
1200 CTransportClass::init();
1203 // Call the user service init
1206 userInitCalled = true; // the bool must be put *before* the call to init()
1208 setCurrentStatus("Initializing");
1209 init ();
1210 clearCurrentStatus("Initializing");
1214 // Connects to the present services
1215 // WARNING: only after the user init() was called because the
1216 // addService may call up service callbacks.
1219 CUnifiedNetwork::getInstance()->connect();
1222 // Say to the AES that the service is ready
1225 if (!_DontUseAES)
1227 // send the ready message (service init finished)
1228 CMessage msgout ("SR");
1229 CUnifiedNetwork::getInstance()->send("AES", msgout);
1233 _Initialized = true;
1235 nlinfo ("SERVICE: Service initialized, executing StartCommands");
1238 // Call the user command from the config file if any
1240 string cmdRoot("StartCommands");
1241 vector<string> posts;
1243 // add an empty string (for the common part of start commands)
1244 posts.push_back(string());
1246 if (IService::getInstance()->haveArg('S'))
1248 string s = IService::getInstance()->getArg('S');
1249 posts.push_back(s);
1252 CConfigFile::CVar *var;
1253 for (uint i=0; i<posts.size(); ++i)
1255 string varName = cmdRoot + posts[i];
1256 if ((var = IService::getInstance()->ConfigFile.getVarPtr (varName)) != NULL)
1258 for (uint i = 0; i < var->size(); i++)
1260 ICommand::execute (var->asString(i), CommandLog);
1265 string str;
1266 CLog logDisplayVars;
1267 CLightMemDisplayer mdDisplayVars;
1268 logDisplayVars.addDisplayer (&mdDisplayVars);
1271 // Activate the timeout assertion thread
1274 timeoutThread = IThread::create(&MyTAT, 1024*4);
1275 timeoutThread->start();
1278 // Set service ready
1281 nlinfo ("SERVICE: Service ready");
1283 if (WindowDisplayer != NULL)
1284 WindowDisplayer->setTitleBar (_ShortName + " " + _LongName + " " + Version.c_str());
1287 // Call the user service update each loop and check files and network activity
1290 TTime checkCpuProcTime = 0;
1292 for(;;)
1294 MyTAT.activate();
1296 if(Bench) CHTimer::startBench(false, true, false);
1298 // count the amount of time to manage internal system
1299 TTime bbefore = CTime::getLocalTime ();
1301 // every second, check for CPU usage
1302 if (bbefore - checkCpuProcTime > 1000)
1304 checkCpuProcTime = bbefore;
1305 _CPUUsageStats.peekMeasures();
1308 // call the user update and exit if the user update asks it
1310 H_AUTO(NLNETServiceUpdate);
1311 if (!update ())
1313 CHTimer::endBench();
1314 break;
1318 // deal with any input waiting from stdin
1320 H_AUTO(NLNETStdinMonitorUpdate);
1321 IStdinMonitorSingleton::getInstance()->update();
1324 // if the launching mode is 'quit after the first update' we set the exit signal
1325 if (haveArg('Q'))
1327 // we use -100 so that the final return code ends as 0 (100+ExitSignalAsked) because
1328 // otherwise we can't launch services with -Q in a makefile
1329 ExitSignalAsked = -100;
1331 NbUserUpdate++;
1333 // count the amount of time to manage internal system
1334 TTime before = CTime::getLocalTime ();
1336 if (WindowDisplayer != NULL)
1338 // update the window displayer and quit if asked
1339 if (!WindowDisplayer->update ())
1341 nlinfo ("SERVICE: The window displayer was closed by user, need to quit");
1342 ExitSignalAsked = 1;
1346 // stop the loop if the exit signal asked
1347 if (ExitSignalAsked != 0)
1349 if ( _RequestClosureClearanceCallback )
1351 if ( _ClosureClearanceStatus == CCClearedForClosure )
1353 // Clearance has been granted
1354 CHTimer::endBench();
1355 break;
1357 else if ( _ClosureClearanceStatus == CCMustRequestClearance )
1359 if ( _RequestClosureClearanceCallback() )
1361 // Direct clearance
1362 _ClosureClearanceStatus = CCClearedForClosure;
1363 CHTimer::endBench();
1364 break;
1366 else
1368 // Delayed clearance
1369 _ClosureClearanceStatus = CCWaitingForClearance;
1372 else if ( _ClosureClearanceStatus >= CCCallbackThenClose )
1374 // Always direct closure, because we don't have a connection to the naming service anymore
1375 // But still call the callback
1376 _RequestClosureClearanceCallback();
1377 CHTimer::endBench();
1378 break;
1381 else
1383 // Immediate closure, no clearance needed
1384 CHTimer::endBench();
1385 break;
1389 CConfigFile::checkConfigFiles ();
1391 updateAdmin ();
1393 CFile::checkFileChange();
1395 // update updatable interface
1396 set<IServiceUpdatable*>::iterator first(_Updatables.begin()), last(_Updatables.end());
1397 for (; first != last; ++first)
1399 IServiceUpdatable *updatable = *first;
1400 updatable->serviceLoopUpdate();
1403 // get and manage layer 5 messages
1404 CUnifiedNetwork::getInstance()->update (_UpdateTimeout);
1407 // update modules
1408 IModuleManager::getInstance().updateModules();
1411 // Allow direct closure if the naming service was lost
1412 if ( _RequestClosureClearanceCallback )
1414 if ( ! CNamingClient::connected() )
1416 if ( _ClosureClearanceStatus < CCCallbackThenClose )
1417 _ClosureClearanceStatus += CCCallbackThenClose; // change status but backup old value
1419 else
1421 if ( _ClosureClearanceStatus >= CCCallbackThenClose )
1422 _ClosureClearanceStatus -= CCCallbackThenClose; // set the closure state back if the NS comes back
1426 NetSpeedLoop = (sint32) (CTime::getLocalTime () - before);
1427 UserSpeedLoop = (sint32) (before - bbefore);
1428 L5CallbackTime = TimeInCallback - LastTimeInCallback;
1429 LastTimeInCallback = TimeInCallback;
1430 L5CallbackCount = TotalCallbackCalled - LastTotalCallbackCalled;
1431 LastTotalCallbackCalled = TotalCallbackCalled;
1433 if (WindowDisplayer != NULL)
1435 static TTime lt = 0;
1436 TTime ct = CTime::getLocalTime();
1437 if(ct > lt+100)
1439 lt = ct;
1440 uint64 rcv, snd, rcvq, sndq;
1441 rcv = CUnifiedNetwork::getInstance()->getBytesReceived ();
1442 snd = CUnifiedNetwork::getInstance()->getBytesSent ();
1443 rcvq = CUnifiedNetwork::getInstance()->getReceiveQueueSize ();
1444 sndq = CUnifiedNetwork::getInstance()->getSendQueueSize ();
1446 for (uint i = 0; i < displayedVariables.size(); i++)
1448 // it s a separator, do nothing
1449 if (displayedVariables[i].first.empty())
1450 continue;
1452 // it s a command, do nothing
1453 if (displayedVariables[i].first[0] == '@')
1454 continue;
1456 string dispName = displayedVariables[i].first;
1457 string varName = dispName;
1458 string::size_type pos = dispName.find("|");
1459 if (pos != string::npos)
1461 varName = displayedVariables[i].first.substr(pos+1);
1462 dispName = displayedVariables[i].first.substr(0, pos);
1465 if (dispName.empty())
1466 str.clear();
1467 else
1468 str = dispName + ": ";
1470 mdDisplayVars.clear ();
1471 ICommand::execute(varName, logDisplayVars, true);
1472 const std::deque<std::string> &strs = mdDisplayVars.lockStrings();
1473 if (!strs.empty())
1475 str += strs[0].substr(0,strs[0].size()-1);
1477 else
1479 str += "???";
1481 mdDisplayVars.unlockStrings();
1482 WindowDisplayer->setLabel (displayedVariables[i].second, str);
1489 // nldebug ("SYNC: updatetimeout must be %d and is %d, sleep the rest of the time", _UpdateTimeout, delta);
1491 CHTimer::endBench();
1493 // Resetting the hierarchical timer must be done outside the top-level timer
1494 if ( _ResetMeasures )
1496 CHTimer::clear();
1497 _ResetMeasures = false;
1500 MyTAT.deactivate();
1503 catch (const EFatalError &)
1505 // Somebody call nlerror, so we have to quit now, the message already display
1506 // so we don't have to to anything
1507 setExitStatus (EXIT_FAILURE);
1509 catch (const ESocket &e)
1511 // Catch NeL network exception to release the system cleanly setExitStatus (EXIT_FAILURE);
1512 ErrorLog->displayNL( "NeL Exception in \"%s\": %s", _ShortName.c_str(), e.what() );
1514 catch ( uint ) // SEH exceptions
1516 ErrorLog->displayNL( "SERVICE: System exception" );
1521 nlinfo ("SERVICE: Service starts releasing");
1524 // Call the user service release() if the init() was called
1527 if (userInitCalled)
1529 setCurrentStatus("Releasing");
1530 release ();
1533 CTransportClass::release();
1536 // Delete all network connection (naming client also)
1539 std::vector<std::string> namesOfOnlyServiceToFlushSendingV;
1540 explode( NamesOfOnlyServiceToFlushSending.get(), string(":"), namesOfOnlyServiceToFlushSendingV, true );
1541 CUnifiedNetwork::getInstance()->release (FlushSendingQueuesOnExit.get(), namesOfOnlyServiceToFlushSendingV);
1543 // warn the module layer that the application is about to close
1544 IModuleManager::getInstance().applicationExit();
1546 // // release the network
1547 // CSock::releaseNetwork ();
1550 // Remove the window displayer
1553 if (WindowDisplayer != NULL)
1555 DebugLog->removeDisplayer (WindowDisplayer);
1556 InfoLog->removeDisplayer (WindowDisplayer);
1557 WarningLog->removeDisplayer (WindowDisplayer);
1558 ErrorLog->removeDisplayer (WindowDisplayer);
1559 AssertLog->removeDisplayer (WindowDisplayer);
1560 CommandLog.removeDisplayer (WindowDisplayer);
1562 delete WindowDisplayer;
1563 WindowDisplayer = NULL;
1566 nlinfo ("SERVICE: Service released successfully");
1568 catch (const EFatalError &)
1570 // Somebody call nlerror, so we have to quit now, the message already display
1571 // so we don't have to to anything
1572 setExitStatus (EXIT_FAILURE);
1575 // remove the stdin monitor thread
1576 IStdinMonitorSingleton::getInstance()->release(); // does nothing if not initialized
1578 // release the module manager
1579 IModuleManager::getInstance().releaseInstance();
1581 // release the network
1582 CSock::releaseNetwork ();
1584 // stop the timeout thread
1585 MyTAT.quit();
1586 if (timeoutThread != NULL)
1588 timeoutThread->wait();
1589 delete timeoutThread;
1592 CHTimer::display();
1593 CHTimer::displayByExecutionPath ();
1594 CHTimer::displayHierarchical(&CommandLog, true, 64);
1595 CHTimer::displayHierarchicalByExecutionPathSorted (&CommandLog, CHTimer::TotalTime, true, 64);
1597 nlinfo ("SERVICE: Service ends");
1599 return ExitSignalAsked?100+ExitSignalAsked:getExitStatus ();
1602 void IService::exit (sint code)
1604 nlinfo ("SERVICE: Somebody called IService::exit(), I have to quit");
1605 ExitSignalAsked = code;
1608 /// Push a new status on the status stack.
1609 void IService::setCurrentStatus(const std::string &status)
1611 // remove the status if it is already in the stack
1612 _ServiceStatusStack.erase(std::remove(_ServiceStatusStack.begin(), _ServiceStatusStack.end(), status), _ServiceStatusStack.end());
1614 // insert the status on top of the stack
1615 _ServiceStatusStack.push_back(status);
1619 /// Remove a status from the status stack. If this status is at top of stack, the next status become the current status
1620 void IService::clearCurrentStatus(const std::string &status)
1622 // remove the status of the stack
1623 _ServiceStatusStack.erase(std::remove(_ServiceStatusStack.begin(), _ServiceStatusStack.end(), status), _ServiceStatusStack.end());
1626 /// Add a tag in the status string
1627 void IService::addStatusTag(const std::string &statusTag)
1629 _ServiveStatusTags.insert(statusTag);
1632 /// Remove a tag from the status string
1633 void IService::removeStatusTag(const std::string &statusTag)
1635 _ServiveStatusTags.erase(statusTag);
1638 /// Get the current status with attached tags
1639 std::string IService::getFullStatus() const
1641 string result;
1643 // get hold of the status at the top of the status stack
1644 if (!_ServiceStatusStack.empty())
1646 result = _ServiceStatusStack.back();
1649 // add status tags to the result so far
1650 set<string>::const_iterator first(_ServiveStatusTags.begin()), last(_ServiveStatusTags.end());
1651 for (; first != last; ++first)
1653 if (first != _ServiveStatusTags.begin() || !result.empty())
1654 result += " ";
1655 result += *first;
1658 // return the result
1659 return result.empty()? "Online": result;
1664 * Require to reset the hierarchical timer
1666 void IService::requireResetMeasures()
1668 _ResetMeasures = true;
1675 std::string IService::getServiceUnifiedName () const
1677 nlassert (!_ShortName.empty());
1678 string res;
1679 if (!_AliasName.empty())
1681 res = _AliasName+"/";
1683 res += _ShortName;
1684 if (_SId.get() != 0)
1686 res += "-";
1687 res += toString (_SId.get());
1689 return res;
1694 * Returns the date of launch of the service. Unit: see CTime::getSecondsSince1970()
1696 uint32 IService::getLaunchingDate () const
1698 return LaunchingDate;
1702 void IService::registerUpdatable(IServiceUpdatable *updatable)
1704 _Updatables.insert(updatable);
1707 void IService::unregisterUpdatable(IServiceUpdatable *updatable)
1709 _Updatables.erase(updatable);
1713 // Commands and Variables for controling all services
1716 NLMISC_CATEGORISED_DYNVARIABLE(nel, string, LaunchingDate, "date of the launching of the program")
1718 nlunreferenced(human);
1719 if (get) *pointer = asctime (localtime ((time_t*)&LaunchingDate));
1722 NLMISC_CATEGORISED_DYNVARIABLE(nel, string, Uptime, "time from the launching of the program")
1724 if (get)
1726 if (human)
1727 *pointer = secondsToHumanReadable (CTime::getSecondsSince1970() - LaunchingDate);
1728 else
1729 *pointer = NLMISC::toString(CTime::getSecondsSince1970() - LaunchingDate);
1731 else
1733 NLMISC::fromString(*pointer, LaunchingDate);
1734 LaunchingDate = CTime::getSecondsSince1970() - LaunchingDate;
1738 NLMISC_CATEGORISED_VARIABLE(nel, string, CompilationDate, "date of the compilation");
1739 NLMISC_CATEGORISED_VARIABLE(nel, string, CompilationMode, "mode of the compilation");
1741 NLMISC_CATEGORISED_VARIABLE(nel, uint32, NbUserUpdate, "number of time the user IService::update() called");
1743 NLMISC_CATEGORISED_DYNVARIABLE(nel, string, Scroller, "current size in bytes of the sent queue size")
1745 nlunreferenced(human);
1747 if (get)
1749 // display the scroll text
1750 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!!! "
1751 "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!!! ";
1752 static int pos = 0;
1753 *pointer = foo.substr ((pos++)%(foo.size()/2), 10);
1757 NLMISC_CATEGORISED_COMMAND(nel, quit, "exit the service", "")
1759 nlunreferenced(rawCommandString);
1760 nlunreferenced(quiet);
1761 nlunreferenced(human);
1763 if(args.size() != 0) return false;
1765 log.displayNL("User ask me with a command to quit");
1766 ExitSignalAsked = 0xFFFF;
1768 return true;
1771 NLMISC_CATEGORISED_COMMAND(nel, brutalQuit, "exit the service brutally", "")
1773 nlunreferenced(rawCommandString);
1774 nlunreferenced(log);
1775 nlunreferenced(quiet);
1776 nlunreferenced(human);
1778 if(args.size() != 0) return false;
1780 ::exit (0xFFFFFFFF);
1782 return true;
1786 #ifdef MUTEX_DEBUG
1787 NLMISC_CATEGORISED_COMMAND(nel, mutex, "display mutex values", "")
1789 if(args.size() != 0) return false;
1791 map<CFairMutex*,TMutexLocks> acquiretimes = getNewAcquireTimes();
1793 map<CFairMutex*,TMutexLocks>::iterator im;
1794 for ( im=acquiretimes.begin(); im!=acquiretimes.end(); ++im )
1796 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(),
1797 CTime::cpuCycleToSecond((*im).second.TimeToEnter)*1000.0, CTime::cpuCycleToSecond((*im).second.TimeInMutex)*1000.0,
1798 (*im).second.Nb, (*im).second.ThreadHavingTheMutex, (*im).second.WaitingMutex,
1799 (*im).second.Dead?" DEAD":"");
1802 return true;
1804 #endif // MUTEX_DEBUG
1806 NLMISC_CATEGORISED_COMMAND(nel, serviceInfo, "display information about this service", "")
1808 nlunreferenced(rawCommandString);
1809 nlunreferenced(quiet);
1810 nlunreferenced(human);
1812 if(args.size() != 0) return false;
1814 log.displayNL ("Service %s '%s' using NeL (" __DATE__ " " __TIME__ ")", IService::getInstance()->getServiceLongName().c_str(), IService::getInstance()->getServiceUnifiedName().c_str());
1815 log.displayNL ("Service listening port: %d", IService::getInstance()->ListeningPort.get());
1816 log.displayNL ("Service running directory: '%s'", IService::getInstance()->RunningDirectory.c_str());
1817 log.displayNL ("Service log directory: '%s'", IService::getInstance()->LogDirectory.c_str());
1818 log.displayNL ("Service save files directory: '%s'", IService::getInstance()->SaveFilesDirectory.c_str());
1819 log.displayNL ("Service write files directory: '%s'", IService::getInstance()->WriteFilesDirectory.c_str());
1820 log.displayNL ("Service config directory: '%s' config filename: '%s.cfg'", IService::getInstance()->ConfigDirectory.c_str(), IService::getInstance()->_LongName.c_str());
1821 log.displayNL ("Service id: %hu", IService::getInstance()->_SId.get());
1822 log.displayNL ("Service update timeout: %dms", IService::getInstance()->_UpdateTimeout);
1823 log.displayNL ("Service %suse naming service", IService::getInstance()->_DontUseNS?"don't ":"");
1824 log.displayNL ("Service %suse admin executor service", IService::getInstance()->_DontUseAES?"don't ":"");
1825 log.displayNL ("NeL is compiled in %s mode", CompilationMode.c_str());
1827 log.displayNL ("Services arguments: %d args", IService::getInstance()->_Args.size ());
1828 for (uint i = 0; i < IService::getInstance()->_Args.size (); i++)
1830 log.displayNL (" argv[%d] = '%s'", i, IService::getInstance()->_Args[i].c_str ());
1833 log.displayNL ("Naming service info: %s", CNamingClient::info().c_str());
1835 ICommand::execute ("services", log);
1837 return true;
1840 NLMISC_CATEGORISED_COMMAND(nel, resetMeasures, "reset hierarchical timer", "")
1842 nlunreferenced(rawCommandString);
1843 nlunreferenced(args);
1844 nlunreferenced(log);
1845 nlunreferenced(quiet);
1846 nlunreferenced(human);
1848 IService::getInstance()->requireResetMeasures();
1849 return true;
1852 NLMISC_CATEGORISED_COMMAND(nel, getWinDisplayerInfo, "display the info about the pos and size of the window displayer", "")
1854 nlunreferenced(rawCommandString);
1855 nlunreferenced(args);
1856 nlunreferenced(quiet);
1857 nlunreferenced(human);
1859 uint32 x,y,w,h;
1860 IService::getInstance()->WindowDisplayer->getWindowPos (x,y,w,h);
1861 log.displayNL ("Window Displayer : XWinParam = %d; YWinParam = %d; WWinParam = %d; HWinParam = %d;", x, y, w, h);
1862 return true;
1865 NLMISC_CATEGORISED_COMMAND(nel, displayConfigFile, "display the variables of the default configfile", "")
1867 nlunreferenced(rawCommandString);
1868 nlunreferenced(args);
1869 nlunreferenced(quiet);
1870 nlunreferenced(human);
1872 IService::getInstance()->ConfigFile.display (&log);
1873 return true;
1876 NLMISC_CATEGORISED_COMMAND(nel, getUnknownConfigFileVariables, "display the variables from config file that are called but not present", "")
1878 nlunreferenced(rawCommandString);
1879 nlunreferenced(args);
1880 nlunreferenced(quiet);
1881 nlunreferenced(human);
1883 log.displayNL ("%d Variables not found in the configfile '%s'", IService::getInstance()->ConfigFile.UnknownVariables.size(), IService::getInstance()->ConfigFile.getFilename().c_str() );
1884 for (uint i = 0; i < IService::getInstance()->ConfigFile.UnknownVariables.size(); i++)
1886 log.displayNL (" %s", IService::getInstance()->ConfigFile.UnknownVariables[i].c_str());
1888 return true;
1891 // -1 = service is quitting
1892 // 0 = service is not connected
1893 // 1 = service is running
1894 // 2 = service is launching
1895 // 3 = service failed launching
1897 NLMISC_CATEGORISED_DYNVARIABLE(nel, string, State, "Set this value to 0 to shutdown the service and 1 to start the service")
1899 nlunreferenced(human);
1900 // read or write the variable
1901 if (get)
1904 else
1906 if (IService::getInstance()->getServiceShortName() == "AES" || IService::getInstance()->getServiceShortName() == "AS")
1908 nlinfo ("SERVICE: I can't set State=0 because I'm the admin and I should never quit");
1910 else if (*pointer == "0" || *pointer == "2")
1912 // ok, we want to set the value to false, just quit
1913 nlinfo ("SERVICE: User ask me with a command to quit using the State variable");
1914 ExitSignalAsked = 0xFFFE;
1915 IService *srv = IService::getInstance();
1916 if( srv == NULL )
1918 return;
1920 srv->setCurrentStatus("Quitting");
1922 else
1924 nlwarning ("SERVICE: Unknown value for State '%s'", (*pointer).c_str());
1928 // whether reading or writing, the internal value of the state variable should end up as the result of getFullStatus()
1929 IService *srv = IService::getInstance();
1930 if( srv == NULL )
1932 return;
1934 *pointer= srv->getFullStatus();
1938 NLMISC_CATEGORISED_DYNVARIABLE(nel, uint32, ShardId, "Get value of shardId set for this particular service")
1940 nlunreferenced(human);
1941 // read or write the variable
1942 if (get)
1944 *pointer = IService::getInstance()->getShardId();
1949 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPULoad, "Get instant CPU load of the server")
1951 nlunreferenced(human);
1952 // read or write the variable
1953 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(); }
1956 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessLoad, "Get instant CPU load of the process/service")
1958 nlunreferenced(human);
1959 // read or write the variable
1960 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(); }
1963 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUUserLoad, "Get instant CPU user load of the server")
1965 nlunreferenced(human);
1966 // read or write the variable
1967 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(); }
1970 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUSytemLoad, "Get instant CPU system load of the server")
1972 nlunreferenced(human);
1973 // read or write the variable
1974 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(); }
1977 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUNiceLoad, "Get instant CPU nice processes load of the server")
1979 nlunreferenced(human);
1980 // read or write the variable
1981 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(); }
1984 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, CPUIOWaitLoad, "Get instant CPU IO wait load of the server")
1986 nlunreferenced(human);
1987 // read or write the variable
1988 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(); }
1991 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessUserLoad, "Get instant CPU user load of the process/service")
1993 nlunreferenced(human);
1994 // read or write the variable
1995 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(); }
1998 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, ProcessSystemLoad, "Get instant CPU system load of the process/service")
2000 nlunreferenced(human);
2001 // read or write the variable
2002 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(); }
2006 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPULoad, "Get instant CPU load of the server")
2008 nlunreferenced(human);
2009 // read or write the variable
2010 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(CCPUTimeStat::Mean); }
2013 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessLoad, "Get instant CPU load of the process/service")
2015 nlunreferenced(human);
2016 // read or write the variable
2017 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(CCPUTimeStat::Mean); }
2020 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUUserLoad, "Get instant CPU user load of the server")
2022 nlunreferenced(human);
2023 // read or write the variable
2024 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(CCPUTimeStat::Mean); }
2027 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUSytemLoad, "Get instant CPU system load of the server")
2029 nlunreferenced(human);
2030 // read or write the variable
2031 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(CCPUTimeStat::Mean); }
2034 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUNiceLoad, "Get instant CPU nice processes load of the server")
2036 nlunreferenced(human);
2037 // read or write the variable
2038 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(CCPUTimeStat::Mean); }
2041 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanCPUIOWaitLoad, "Get instant CPU IO wait load of the server")
2043 nlunreferenced(human);
2044 // read or write the variable
2045 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(CCPUTimeStat::Mean); }
2048 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessUserLoad, "Get instant CPU user load of the process/service")
2050 nlunreferenced(human);
2051 // read or write the variable
2052 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(CCPUTimeStat::Mean); }
2055 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, MeanProcessSystemLoad, "Get instant CPU system load of the process/service")
2057 nlunreferenced(human);
2058 // read or write the variable
2059 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(CCPUTimeStat::Mean); }
2064 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPULoad, "Get instant CPU load of the server")
2066 nlunreferenced(human);
2067 // read or write the variable
2068 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPULoad(CCPUTimeStat::Peak); }
2071 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessLoad, "Get instant CPU load of the process/service")
2073 nlunreferenced(human);
2074 // read or write the variable
2075 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessLoad(CCPUTimeStat::Peak); }
2078 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUUserLoad, "Get instant CPU user load of the server")
2080 nlunreferenced(human);
2081 // read or write the variable
2082 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUUserLoad(CCPUTimeStat::Peak); }
2085 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUSytemLoad, "Get instant CPU system load of the server")
2087 nlunreferenced(human);
2088 // read or write the variable
2089 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUSystemLoad(CCPUTimeStat::Peak); }
2092 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUNiceLoad, "Get instant CPU nice processes load of the server")
2094 nlunreferenced(human);
2095 // read or write the variable
2096 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUNiceLoad(CCPUTimeStat::Peak); }
2099 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakCPUIOWaitLoad, "Get instant CPU IO wait load of the server")
2101 nlunreferenced(human);
2102 // read or write the variable
2103 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getCPUIOWaitLoad(CCPUTimeStat::Peak); }
2106 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessUserLoad, "Get instant CPU user load of the process/service")
2108 nlunreferenced(human);
2109 // read or write the variable
2110 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessUserLoad(CCPUTimeStat::Peak); }
2113 NLMISC_CATEGORISED_DYNVARIABLE(cpu, float, PeakProcessSystemLoad, "Get instant CPU system load of the process/service")
2115 nlunreferenced(human);
2116 // read or write the variable
2117 if (get) { *pointer = IService::getInstance()->getCPUUsageStats().getProcessSystemLoad(CCPUTimeStat::Peak); }
2121 } //NLNET