diff options
Diffstat (limited to 'cvs/cvsweb.cgi/code/nel/src/net/service.cpp?rev=1.136&content-type=text/x-cvsweb-markup&sortby=date/index.html')
-rw-r--r-- | cvs/cvsweb.cgi/code/nel/src/net/service.cpp?rev=1.136&content-type=text/x-cvsweb-markup&sortby=date/index.html | 1475 |
1 files changed, 1475 insertions, 0 deletions
diff --git a/cvs/cvsweb.cgi/code/nel/src/net/service.cpp?rev=1.136&content-type=text/x-cvsweb-markup&sortby=date/index.html b/cvs/cvsweb.cgi/code/nel/src/net/service.cpp?rev=1.136&content-type=text/x-cvsweb-markup&sortby=date/index.html new file mode 100644 index 00000000..c4f12e9a --- /dev/null +++ b/cvs/cvsweb.cgi/code/nel/src/net/service.cpp?rev=1.136&content-type=text/x-cvsweb-markup&sortby=date/index.html @@ -0,0 +1,1475 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><HTML> +<HEAD><style> A { color:black }</style> +<!-- hennerik CVSweb $Revision: 1.93 $ --> +<TITLE>code/nel/src/net/service.cpp - view - 1.136</TITLE></HEAD> +<BODY BGCOLOR="#eeeeee"> +<table width="100%" border=0 cellspacing=0 cellpadding=1 bgcolor="#aaaaaa"><tr valign=bottom><td><a href="service.cpp?sortby=date"><IMG SRC="http://www.nevrax.org/inc/img/picto-up.gif" ALT="[BACK]" BORDER="0" WIDTH="14" HEIGHT="13"></a> <b>Return to <A HREF="service.cpp?sortby=date">service.cpp</A> + CVS log</b> <IMG SRC="http://www.nevrax.org/inc/img/picto-news.gif" ALT="[TXT]" BORDER="0" WIDTH="13" HEIGHT="15"></td><td align=right><IMG SRC="http://www.nevrax.org/inc/img/picto-dir.gif" ALT="[DIR]" BORDER="0" WIDTH="15" HEIGHT="13"> <b>Up to <a href="/cvs/cvsweb.cgi/?sortby=date">Nevrax</a> / <a href="/cvs/cvsweb.cgi/code/?sortby=date">code</a> / <a href="/cvs/cvsweb.cgi/code/nel/?sortby=date">nel</a> / <a href="/cvs/cvsweb.cgi/code/nel/src/?sortby=date">src</a> / <a href="/cvs/cvsweb.cgi/code/nel/src/net/?sortby=date">net</a></b></td></tr></table><HR noshade><table width="100%"><tr><td bgcolor="#ffffff">File: <a href="/cvs/cvsweb.cgi/?sortby=date">Nevrax</a> / <a href="/cvs/cvsweb.cgi/code/?sortby=date">code</a> / <a href="/cvs/cvsweb.cgi/code/nel/?sortby=date">nel</a> / <a href="/cvs/cvsweb.cgi/code/nel/src/?sortby=date">src</a> / <a href="/cvs/cvsweb.cgi/code/nel/src/net/?sortby=date">net</a> / <a href="/cvs/cvsweb.cgi/code/nel/src/net/service.cpp?sortby=date">service.cpp</a> (<A HREF="/cvs/cvsweb.cgi/~checkout~/code/nel/src/net/service.cpp?rev=1.136&sortby=date" target="cvs_checkout" onClick="window.open('/cvs/cvsweb.cgi/~checkout~/code/nel/src/net/service.cpp?rev=1.136','cvs_checkout','resizeable,scrollbars');"><b>download</b></A>)<BR> +Revision <B>1.136</B>, <i>Thu Jul 18 15:01:14 2002 UTC</i> (10 days, 19 hours ago) by <i>lecroart</i> +<BR>Branch: <b>MAIN</b> +<BR>Changes since <b>1.135: +18 -1 + lines</b><PRE> +ADDED: exit and freeze +</PRE> +</td></tr></table><HR noshade><PRE>/** \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 +</PRE>
\ No newline at end of file |