Return to service.cpp CVS log | Up to Nevrax / code / nel / src / net |
File: Nevrax / code / nel / src / net / service.cpp (download) Revision 1.136, Thu Jul 18 15:01:14 2002 UTC (10 days, 19 hours ago) by lecroart Branch: MAIN Changes since 1.135: +18 -1 lines ADDED: exit and freeze |
/** \file service.cpp * Base class for all network services * * $Id: service.cpp,v 1.136 2002/07/18 15:01:14 lecroart Exp $ * * \todo ace: test the signal redirection on Unix * \todo ace: add parsing command line (with CLAP?) */ /* Copyright, 2001 Nevrax Ltd. * * This file is part of NEVRAX NEL. * NEVRAX NEL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * NEVRAX NEL 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NEVRAX NEL; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ #include "stdnet.h" #ifdef NL_OS_WINDOWS // // Includes // // these defines is for IsDebuggerPresent(). it'll not compile on windows 95 // just comment this and the IsDebuggerPresent to compile on windows 95 # define _WIN32_WINDOWS 0x0410 # define WINVER 0x0400 # include <windows.h> # include <direct.h> #elif defined NL_OS_UNIX # include <unistd.h> #endif #include <stdlib.h> #include <signal.h> #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/net/naming_client.h" #include "nel/net/service.h" #include "nel/net/unified_network.h" #include "nel/net/net_manager.h" #include "nel/net/net_displayer.h" #include "nel/misc/hierarchical_timer.h" // // Namespace // 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" }; // // Variables // // class static member IService *IService::_Instance = NULL; static sint ExitSignalAsked = 0; // services stat static sint32 NetSpeedLoop, UserSpeedLoop; // this is the thread that initialized the signal redirection // we ll ignore other thread signals static uint SignalisedThread; static CFileDisplayer fd; static CNetDisplayer commandDisplayer(false); static CLog commandLog; // // Callback managing // void AESConnection (const string &serviceName, TSockId from, void *arg) { // established a connection to the AES, identify myself // // Sends the identification message with the name of the service and all commands available on this service // CMessage msgout (CNetManager::getSIDA ("AES"), "SID"); msgout.serial (IService::getInstance()->_AliasName, IService::getInstance()->_ShortName, IService::getInstance()->_LongName); ICommand::serialCommands (msgout); CNetManager::send ("AES", msgout); if (IService::getInstance()->_Initialized) { CMessage msgout2 (CNetManager::getSIDA ("AES"), "SR"); CNetManager::send ("AES", msgout2); } // add the displayer to the standard logger CCallbackClient *client = dynamic_cast<CCallbackClient *>(CNetManager::getNetBase("AES")); commandDisplayer.setLogServer (client); commandLog.addDisplayer (&commandDisplayer); } void AESConnection5 (const string &serviceName, uint16 sid, void *arg) { // established a connection to the AES, identify myself // // Sends the identification message with the name of the service and all commands available on this service // CMessage msgout ("SID"); msgout.serial (IService::getInstance()->_AliasName, IService::getInstance()->_ShortName, IService::getInstance()->_LongName); ICommand::serialCommands (msgout); CUnifiedNetwork::getInstance()->send("AES", msgout); if (IService::getInstance()->_Initialized) { CMessage msgout2 ("SR"); CUnifiedNetwork::getInstance()->send("AES", msgout2); } // add the displayer to the standard logger TSockId hid; CCallbackClient *client = dynamic_cast<CCallbackClient *>(CUnifiedNetwork::getInstance()->getNetBase("AES", hid)); commandDisplayer.setLogServer (client); commandLog.addDisplayer (&commandDisplayer); } static void AESDisconnection (const string &serviceName, TSockId from, void *arg) { commandLog.removeDisplayer (&commandDisplayer); } static void AESDisconnection5 (const std::string &serviceName, uint16 sid, void *arg) { commandLog.removeDisplayer (&commandDisplayer); } static void cbExecCommand (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { string command; msgin.serial (command); ICommand::execute (command, commandLog); } static void cbExecCommand5 (CMessage &msgin, const std::string &serviceName, uint16 sid) { string command; msgin.serial (command); ICommand::execute (command, commandLog); } // if we receive the stop service, we try to exit now static void cbStopService (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { ExitSignalAsked = 0xFFFF; } static void cbStopService5 (CMessage &msgin, const std::string &serviceName, uint16 sid) { ExitSignalAsked = 0xFFFF; } // layer4 static TCallbackItem AESCallbackArray[] = { { "STOPS", cbStopService }, { "EXEC_COMMAND", cbExecCommand }, }; // layer 5 static TUnifiedCallbackItem AESCallbackArray5[] = { { "STOPS", cbStopService5 }, { "EXEC_COMMAND", cbExecCommand5 }, }; // // 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 ("Not the main thread received the signal (%s, %d), ignore it", SignalName[i],Sig); return; } else { nlinfo ("Signal %s (%d) received", SignalName[i], Sig); switch (Sig) { case SIGABRT : case SIGILL : case SIGINT : 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) { ExitSignalAsked = Sig; return; } else { nlinfo ("Signal already received, launch the brutal exit"); exit (EXIT_FAILURE); } break; } } } } nlinfo ("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 signal(Signal[3], sigHandler); //nldebug("Signal : %s (%d) trapped", SignalName[3], Signal[3]); #else // in release, redirect all signals /* don't redirect now because to 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]); } */ #endif } // // Class implementation // // Ctor IService::IService() : _Initialized(false), WindowDisplayer(NULL), _UpdateTimeout(100), _Port(0), _RecordingState(CCallbackNetBase::Off), _SId(0), _Status(0), _IsService5(false), _ResetMeasures(false) { // Singleton nlassert( _Instance == NULL ); _Instance = this; } bool IService::haveArg (char argName) { 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) { for (uint32 i = 0; i < _Args.size(); i++) { if (_Args[i].size() >= 2 && _Args[i][0] == '-') { if (_Args[i][1] == argName) { return _Args[i].substr(2); } } } throw Exception ("Parameter '-%c' is not found in command line", argName); } void IService::setArgs (const char *args) { _Args.push_back ("<ProgramName>"); string sargs (args); sint pos1 = 0, pos2 = 0; do { pos1 = sargs.find_first_not_of (" ", pos2); if (pos1 == string::npos) break; pos2 = sargs.find_first_of (" ", pos1); _Args.push_back (sargs.substr (pos1, pos2-pos1)); } while (pos2 != string::npos); } void IService::setArgs (int argc, const char **argv) { for (sint i = 0; i < argc; i++) { _Args.push_back (argv[i]); } } /* * Returns a pointer to the CCallbackServer object */ CCallbackServer *IService::getServer() { if (isService5()) return NULL; else return dynamic_cast<CCallbackServer*>(CNetManager::getNetBase(IService::_ShortName)); } void cbLogFilter (CConfigFile::CVar &var) { CLog *log = NULL; if (var.Name == "NegFiltersDebug") { log = DebugLog; nlinfo ("Updating negative filter on debug from config file"); } else if (var.Name == "NegFiltersInfo") { log = InfoLog; nlinfo ("Updating negative filter on info from config file"); } else { nlstop; } // remove all old filter from configfile CConfigFile::CVar &oldvar = IService::getInstance()->ConfigFile.getVar (var.Name); for (sint j = 0; j < oldvar.size(); j++) { log->removeFilter (oldvar.asString(j).c_str()); } // add all new filter from configfile for (sint i = 0; i < var.size(); i++) { log->addNegativeFilter (var.asString(i).c_str()); } } // // The main function of the service // sint IService::main (const char *serviceShortName, const char *serviceLongName, uint16 servicePort, const char *configDir, const char *logDir) { bool userInitCalled = false; bool resyncEvenly = false; CConfigFile::CVar *var = NULL; try { // get the path where to run the service if any in the command line if (haveArg('A')) { _RunningPath = CPath::standardizePath(getArg('A')); #ifdef NL_OS_WINDOWS _chdir (_RunningPath.c_str()); #else chdir (_RunningPath.c_str()); #endif } // // Init parameters // _ConfigDir = CPath::standardizePath(configDir); _LogDir = CPath::standardizePath(logDir); _ShortName = serviceShortName; _LongName = serviceLongName; // // 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')) _LogDir = CPath::standardizePath(getArg('L')); // get the config file dir if any in the command line if (haveArg('C')) _ConfigDir = CPath::standardizePath(getArg('C')); createDebug (_LogDir.c_str(), false); DebugLog->addNegativeFilter ("NETL"); // we create the log with service name filename ("test_service.log" for example) fd.setParam (_LogDir + _LongName + ".log", false); DebugLog->addDisplayer (&fd); InfoLog->addDisplayer (&fd); WarningLog->addDisplayer (&fd); AssertLog->addDisplayer (&fd); ErrorLog->addDisplayer (&fd); // // Init the hierarchical timer // CHTimer::startBench(false, true); // // Load the config file // ConfigFile.load (_ConfigDir + _LongName + ".cfg"); // // Set the negatif filter from the config file // if ((var = ConfigFile.getVarPtr ("NegFiltersDebug")) != NULL) { ConfigFile.setCallback ("NegFiltersDebug", cbLogFilter); for (sint i = 0; i < var->size(); i++) { DebugLog->addNegativeFilter (var->asString(i).c_str()); } } if ((var = ConfigFile.getVarPtr ("NegFiltersInfo")) != NULL) { ConfigFile.setCallback ("NegFiltersInfo", cbLogFilter); for (sint i = 0; i < var->size(); i++) { InfoLog->addNegativeFilter (var->asString(i).c_str()); } } // // Create the window if neeeded // 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") { nlwarning ("Unknown value for the WindowStyle (should be GTK, WIN or NONE), use no window displayer"); } } vector <pair<string,uint> > displayedVariables; //uint speedNetLabel, speedUsrLabel, rcvLabel, sndLabel, rcvQLabel, sndQLabel, scrollLabel; if (WindowDisplayer != NULL) { // // Init window param if necessary // sint x=-1, y=-1, w=-1, h=-1; bool iconified = false; 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("HWinParam")) != NULL) iconified = var->asInt() == 1; if (haveArg('I')) iconified = true; if (w == -1 && h == -1) WindowDisplayer->create (string("*INIT* ") + _ShortName + " " + _LongName, iconified, x, y); else WindowDisplayer->create (string("*INIT* ") + _ShortName + " " + _LongName, iconified, x, y, w, h); DebugLog->addDisplayer (WindowDisplayer); InfoLog->addDisplayer (WindowDisplayer); WarningLog->addDisplayer (WindowDisplayer); ErrorLog->addDisplayer (WindowDisplayer); AssertLog->addDisplayer (WindowDisplayer); // 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("Rcv|ReceivedBytes"), WindowDisplayer->createLabel ("Rcv"))); // displayedVariables.push_back(make_pair(string("Snd|SentBytes"), WindowDisplayer->createLabel ("Snd"))); // displayedVariables.push_back(make_pair(string("RcvQ|ReceivedQueueSize"), WindowDisplayer->createLabel ("RcvQ"))); // displayedVariables.push_back(make_pair(string("SndQ|SentQueueSize"), WindowDisplayer->createLabel ("SndQ"))); displayedVariables.push_back(make_pair(string("|Scroller"), WindowDisplayer->createLabel ("NeL Rulez"))); CConfigFile::CVar *v = ConfigFile.getVarPtr("DisplayedVariables"); if (v != NULL) { for (sint i = 0; i < v->size(); i++) { displayedVariables.push_back(make_pair(v->asString(i), WindowDisplayer->createLabel (v->asString(i).c_str()))); } } } nlinfo ("Starting Service %d '%s' using NeL ("__DATE__" "__TIME__")", isService5()?5:4, _ShortName.c_str()); setStatus (EXIT_SUCCESS); // // Redirect signal if needed (in release mode only) // #ifdef NL_OS_WINDOWS #ifdef NL_RELEASE 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 ("SIGPIPE %s", IgnoredPipe?"Ignored":"Not Ignored"); #endif // NL_OS_UNIX // // Initialize the network system // string localhost; try { // Initialize WSAStartup and network stuffs CSock::initNetwork(); // Get the localhost name localhost = CInetAddress::localHost().hostName(); } catch (NLNET::ESocket &) { localhost = "<UnknownHost>"; } // Set the localhost name and service name to the logger string processName = localhost; processName += '/'; processName += _ShortName; CLog::setProcessName (processName); // // Initialize server parameters // // Get the port from config file or in the macro (overload the port set by the user init()) if ((var = ConfigFile.getVarPtr("Port")) != NULL) { // set the listen port with the value in the config file if any _Port = var->asInt(); } else { // set the listen port with the value in the NLNET_SERVICE_MAIN macro _Port = servicePort; } // set the listen port if there are a port arg in the command line if (haveArg('P')) { _Port = atoi(getArg('P').c_str()); } // set the aliasname if is present in the command line if (haveArg('N')) { _AliasName = getArg('N'); } // Load the recording state from the config file if ((var = ConfigFile.getVarPtr ("Rec")) != NULL) { string srecstate = var->asString(); strupr( srecstate ); if ( srecstate == "RECORD" ) { _RecordingState = CCallbackNetBase::Record; nlinfo( "Service recording messages" ); } else if ( srecstate == "REPLAY" ) { _RecordingState = CCallbackNetBase::Replay; nlinfo( "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 ); } /* // // Layer4 Startup (Connect to the Naming Service (except for the NS itself and Login Service)) // if (IService::_ShortName != "NS" && IService::_ShortName != "LS" && IService::_ShortName != "AES" && IService::_ShortName != "AS") { bool ok = false; while (!ok) { // read the naming service address from the config file CInetAddress loc(ConfigFile.getVar("NSHost").asString(), ConfigFile.getVar("NSPort").asInt()); try { CNetManager::init( &loc, _RecordingState ); ok = true; } catch (ESocketConnectionFailed &) { nlwarning ("Could not connect to the Naming Service (%s). Retrying in a few seconds...", loc.asString().c_str()); nlSleep (5000); } } } else { CNetManager::init( NULL, _RecordingState ); } */ /// /// Layer5 Startup /// // get the sid if ((var = ConfigFile.getVarPtr ("SId")) != NULL) { sint32 sid = var->asInt(); if (sid<0 || sid>255) { nlwarning("Bad SId value in the config file, %d is not in [0;255] range", sid); _SId = 0; } else { _SId = (uint8) sid; } } else { // ok, SId not found, use dynamic sid _SId = 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 = (_ShortName == "NS" || _ShortName == "LS" || _ShortName == "AES" || _ShortName == "AS"); } // normal setup for the common services if (!_DontUseNS) { bool ok = false; while (!ok) { // read the naming service address from the config file string 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 { if (isService5()) CUnifiedNetwork::getInstance()->init (&loc, _RecordingState, _ShortName, _Port, _SId); else CNetManager::init( &loc, _RecordingState ); ok = true; } catch (ESocketConnectionFailed &) { nlwarning ("Could not connect to the Naming Service (%s). Retrying in a few seconds...", loc.asString().c_str()); nlSleep (5000); } } } else { if (isService5()) CUnifiedNetwork::getInstance()->init(NULL, _RecordingState, _ShortName, _Port, _SId); else CNetManager::init( NULL, _RecordingState ); } // // 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 = (_ShortName == "AES" || _ShortName == "AS"); } if (!_DontUseAES) { if (isService5()) { CUnifiedNetwork::getInstance()->setServiceUpCallback ("AES", AESConnection5, NULL); CUnifiedNetwork::getInstance()->setServiceDownCallback ("AES", AESDisconnection5, NULL); CUnifiedNetwork::getInstance()->addService ("AES", CInetAddress("localhost:49997"), false); CUnifiedNetwork::getInstance()->addCallbackArray (AESCallbackArray5, sizeof(AESCallbackArray5)/sizeof(AESCallbackArray5[0])); } else { CNetManager::setConnectionCallback ("AES", AESConnection, NULL); CNetManager::setDisconnectionCallback ("AES", AESDisconnection, NULL); CNetManager::addClient ("AES", "localhost:49997"); CNetManager::addCallbackArray ("AES", AESCallbackArray, sizeof(AESCallbackArray)/sizeof(AESCallbackArray[0])); } } // // Add callback array // if (isService5()) { // add callback set in the NLNET_SERVICE_MAIN macro NLNET::CUnifiedNetwork::getInstance()->addCallbackArray(_CallbackArray5, _CallbackArraySize); } else { CNetManager::addServer (_ShortName, _Port, _SId); CNetManager::addCallbackArray (_ShortName, _CallbackArray, _CallbackArraySize); } // // Now we have the service id, we can set the entites id generator // _NextEntityId.setServiceId(_SId); // // Call the user service init // userInitCalled = true; // the bool must be put *before* the call to init() init (); // // Connects to the present services // WARNING: only after the user init() was called because the // addService may call up service callbacks. // if (isService5()) CUnifiedNetwork::getInstance()->connect(); // // On Unix system, the service fork itself to give back the hand to the shell // // note: we don't forking anymore because it doesn't work with thread system // #ifdef NL_OS_UNIX /* nlinfo( "Forking the service" ); int pid = fork(); /// \todo ace: when we fork() on linux, the father process tries to release threads but it should not, so we have to find a solution if (pid == -1) { nlerror ("Couldn't fork the service"); } else if (pid != 0) { // It's the father, return the hand to the shell. exit(EXIT_SUCCESS); } */ #endif // NL_OS_UNIX // // Say to the AES that the service is ready // if (!_DontUseAES) { if (isService5()) { // send the ready message (service init finished) CMessage msgout ("SR"); CUnifiedNetwork::getInstance()->send("AES", msgout); } else { // send the ready message (service init finished) CMessage msgout (CNetManager::getSIDA ("AES"), "SR"); CNetManager::send ("AES", msgout); } } // // Add default pathes // if ((var = ConfigFile.getVarPtr ("Paths")) != NULL) { for (sint i = 0; i < var->size(); i++) { CPath::addSearchPath (var->asString(i)); } } _Initialized = true; nlinfo ("Service initialised"); // // Call the user command from the config file if any // if ((var = ConfigFile.getVarPtr ("StartCommands")) != NULL) { for (sint i = 0; i < var->size(); i++) { ICommand::execute (var->asString(i), *InfoLog); } } nlinfo ("Service ready"); if (WindowDisplayer != NULL) WindowDisplayer->setTitleBar (_ShortName + " " + _LongName); // // Call the user service update each loop and check files and network activity // do { H_BEFORE(NLNETServiceLoop); // count the amount of time to manage internal system TTime bbefore = CTime::getLocalTime (); // call the user update and exit if the user update asks it H_BEFORE(NLNETServiceUpdate); if (!update ()) { H_AFTER(NLNETServiceUpdate); H_AFTER(NLNETServiceLoop); break; } H_AFTER(NLNETServiceUpdate); // 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 ()) ExitSignalAsked = true; } // stop the loop if the exit signal asked if (ExitSignalAsked) { H_AFTER(NLNETServiceLoop); break; } CConfigFile::checkConfigFiles (); CFile::checkFileChange(); H_BEFORE(NLNETServiceManageMessages); if (isService5()) { // get and manage layer 5 messages CUnifiedNetwork::getInstance()->update (_UpdateTimeout); } else { // get and manage layer 4 messages CNetManager::update (_UpdateTimeout); } H_AFTER(NLNETServiceManageMessages); // resync the clock every hours if (resyncEvenly) { static TTime LastSyncTime = CTime::getLocalTime (); //--------------------------------------- // To simulate Ctrl-C in the debugger... Exit after 1 min ! /*if (CTime::getLocalTime () - LastSyncTime > 60 * 1000 ) { ExitSignalAsked = 1; }*/ //--------------------------------------- /* if (CTime::getLocalTime () - LastSyncTime > 60*60*1000) { CUniTime::syncUniTimeFromService ( _RecordingState ); LastSyncTime = CTime::getLocalTime (); } */ } NetSpeedLoop = (sint32) (CTime::getLocalTime () - before); UserSpeedLoop = (sint32) (before - bbefore); if (WindowDisplayer != NULL) { uint64 rcv, snd, rcvq, sndq; if (isService5()) { rcv = CUnifiedNetwork::getInstance()->getBytesReceived (); snd = CUnifiedNetwork::getInstance()->getBytesSent (); rcvq = CUnifiedNetwork::getInstance()->getReceiveQueueSize (); sndq = CUnifiedNetwork::getInstance()->getSendQueueSize (); } else { rcv = CNetManager::getBytesReceived (); snd = CNetManager::getBytesSent (); rcvq = CNetManager::getReceiveQueueSize (); sndq = CNetManager::getSendQueueSize (); } string str; CLog log; CMemDisplayer md; log.addDisplayer (&md); 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; sint 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 + ": "; md.clear (); ICommand::execute(varName, log, true); const std::deque<std::string> &strs = md.lockStrings(); if (strs.size()>0) { sint pos = strs[0].find("="); if(pos != string::npos && pos + 2 < (sint)strs[0].size()) { sint pos2 = string::npos; if(strs[0][strs[0].size()-1] == '\n') pos2 = strs[0].size() - pos - 2 - 1; str += strs[0].substr (pos+2, pos2); } else { str += "???"; } } md.unlockStrings(); WindowDisplayer->setLabel (displayedVariables[i].second, str); } } // nldebug ("SYNC: updatetimeout must be %d and is %d, sleep the rest of the time", _UpdateTimeout, delta); H_AFTER(NLNETServiceLoop); // Resetting the hierarchical timer must be done outside the top-level timer if ( _ResetMeasures ) { CHTimer::clear(); _ResetMeasures = false; } } while (true); } catch (EFatalError &) { // Somebody call nlerror, so we have to quit now, the message already display // so we don't have to to anything setStatus (EXIT_FAILURE); } catch ( uint ) // SEH exceptions { ErrorLog->displayNL( "System exception" ); } #ifdef NL_RELEASE /* // in release mode, we catch everything to handle clean release. catch (Exception &e) { // Catch NeL exception to release the system cleanly setStatus (EXIT_FAILURE); nlinfo ("ERROR: NeL Exception: Error running the service \"%s\": %s", _ShortName.c_str(), e.what()); } catch (...) { // Catch anything we can to release the system cleanly setStatus (EXIT_FAILURE); nlinfo ("ERROR: Unknown external exception"); } */ #endif try { nlinfo ("Service starts releasing"); // // Call the user service release() if the init() was called // if (userInitCalled) release (); // // Delete all network connection (naming client also) // if (isService5()) CUnifiedNetwork::getInstance()->release (); else CNetManager::release (); CSock::releaseNetwork (); // // Remove the window displayer // if (WindowDisplayer != NULL) { DebugLog->removeDisplayer (WindowDisplayer); InfoLog->removeDisplayer (WindowDisplayer); WarningLog->removeDisplayer (WindowDisplayer); ErrorLog->removeDisplayer (WindowDisplayer); AssertLog->removeDisplayer (WindowDisplayer); delete WindowDisplayer; WindowDisplayer = NULL; } nlinfo ("Service released succesfuly"); } catch (EFatalError &) { // Somebody call nlerror, so we have to quit now, the message already display // so we don't have to to anything setStatus (EXIT_FAILURE); } #ifdef NL_RELEASE /* // in release mode, we catch everything to handle clean release. catch (Exception &e) { setStatus (EXIT_FAILURE); nlinfo ("ERROR: NeL Exception: Error releasing the service \"%s\": %s", _ShortName.c_str(), e.what()); } catch (...) { // Catch anything we can to release the system cleanly setStatus (EXIT_FAILURE); nlinfo ("ERROR: Unknown external exception"); } */ #endif CHTimer::endBench(); CHTimer::display(); CHTimer::displayByExecutionPath (); CHTimer::displayHierarchical(InfoLog, true, 64); CHTimer::displayHierarchicalByExecutionPathSorted (InfoLog, CHTimer::TotalTime, true, 64); nlinfo ("Service ends"); return ExitSignalAsked?100+ExitSignalAsked:getStatus (); } void IService::exit (sint code) { ExitSignalAsked = code; } /* * Require to reset the hierarchical timer */ void IService::requireResetMeasures() { _ResetMeasures = true; } // // Commands and Variables for controling all services // NLMISC_VARIABLE(sint32, NetSpeedLoop, "duration of the last network loop (in ms)"); NLMISC_VARIABLE(sint32, UserSpeedLoop, "duration of the last user loop (in ms)"); NLMISC_DYNVARIABLE(uint64, ReceivedBytes, "total of bytes received by this service") { // we can only read the value if (get) *pointer = IService::getInstance()->isService5()?CUnifiedNetwork::getInstance()->getBytesReceived ():CNetManager::getBytesReceived (); } NLMISC_DYNVARIABLE(uint64, SentBytes, "total of bytes sent by this service") { // we can only read the value if (get) *pointer = IService::getInstance()->isService5()?CUnifiedNetwork::getInstance()->getBytesSent ():CNetManager::getBytesSent (); } NLMISC_DYNVARIABLE(uint64, ReceivedQueueSize, "current size in bytes of the received queue size") { // we can only read the value if (get) *pointer = IService::getInstance()->isService5()?CUnifiedNetwork::getInstance()->getReceiveQueueSize ():CNetManager::getReceiveQueueSize (); } NLMISC_DYNVARIABLE(uint64, SentQueueSize, "current size in bytes of the sent queue size") { // we can only read the value if (get) *pointer = IService::getInstance()->isService5()?CUnifiedNetwork::getInstance()->getSendQueueSize ():CNetManager::getSendQueueSize (); } NLMISC_DYNVARIABLE(string, Scroller, "current size in bytes of the sent queue size") { 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_COMMAND (quit, "exit the service", "") { if(args.size() != 0) return false; ExitSignalAsked = 0xFFFF; return true; } NLMISC_COMMAND (brutalQuit, "exit the service brutally", "") { if(args.size() != 0) return false; exit (0xFFFFFFFF); return true; } #ifdef MUTEX_DEBUG NLMISC_COMMAND (mutex, "display mutex values", "") { if(args.size() != 0) return false; map<CFairMutex*,TMutexLocks> acquiretimes = getNewAcquireTimes(); map<CFairMutex*,TMutexLocks>::iterator im; for ( im=acquiretimes.begin(); im!=acquiretimes.end(); ++im ) { nlinfo( "%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_COMMAND (serviceInfo, "display information about this service", "") { if(args.size() != 0) return false; log.displayNL ("Service %d '%s' '%s' '%s' using NeL ("__DATE__" "__TIME__")", IService::getInstance()->isService5()?5:4, IService::getInstance()->_ShortName.c_str(), IService::getInstance()->_LongName.c_str(), IService::getInstance()->_AliasName.c_str()); log.displayNL ("Service listening port: %d", IService::getInstance()->_Port); log.displayNL ("Service running directory: '%s'", IService::getInstance()->_RunningPath.c_str()); log.displayNL ("Service log directory: '%s'", IService::getInstance()->_LogDir.c_str()); log.displayNL ("Service config directory: '%s' config filename: '%s.cfg'", IService::getInstance()->_ConfigDir.c_str(), IService::getInstance()->_LongName.c_str()); log.displayNL ("Service id: %d", IService::getInstance()->_SId); 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 ":""); #ifdef NL_RELEASE_DEBUG string mode = "NL_RELEASE_DEBUG"; #elif defined(NL_DEBUG_FAST) string mode = "NL_DEBUG_FAST"; #elif defined(NL_DEBUG) string mode = "NL_DEBUG"; #elif defined(NL_RELEASE) string mode = "NL_RELEASE"; #else string mode = "???"; #endif log.displayNL ("NeL is compiled in %s mode", mode.c_str()); nlinfo ("Services arguments: %d args", IService::getInstance()->_Args.size ()); for (uint i = 0; i < IService::getInstance()->_Args.size (); i++) { nlinfo (" 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_COMMAND(resetMeasures, "reset hierarchical timer", "") { IService::getInstance()->requireResetMeasures(); return true; } NLMISC_COMMAND(displayMeasures, "display hierarchical timer", "") { CHTimer::display(); CHTimer::displayHierarchicalByExecutionPathSorted (InfoLog, CHTimer::TotalTime, true, 64); return true; } NLMISC_COMMAND(getWinDisplayerInfo, "display the info about the pos and size of the window displayer", "") { 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_COMMAND(printConfigFile, "display the variables of the default configfile", "") { IService::getInstance()->ConfigFile.print(&log); return true; } NLMISC_COMMAND(getUnknownConfigFileVariables, "display the variables from config file that are called but not present", "") { 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; } NLMISC_COMMAND (freeze, "Freeze the service for N seconds (for debug purpose)", "<N>") { if(args.size() != 1) return false; sint32 n = atoi (args[0].c_str()); log.displayNL ("Freezing %d seconds", n); nlSleep(n * 1000); return true; } } //NLNET