// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #ifndef NELNS_CONFIG #define NELNS_CONFIG "" #endif // NELNS_CONFIG #ifndef NELNS_LOGS #define NELNS_LOGS "" #endif // NELNS_LOGS // // Includes // #include "nel/misc/types_nl.h" #include <list> #include <string> #include "nel/misc/debug.h" #include "nel/misc/command.h" #include "nel/misc/variable.h" #include "nel/misc/displayer.h" #include "nel/net/callback_server.h" #include "nel/net/service.h" #include "nel/net/module_manager.h" // // Namespaces // using namespace std; using namespace NLMISC; using namespace NLNET; NLMISC_COMMAND(test, "none", "none") { log.displayNL("Raw cmd line : '%s'", rawCommandString.c_str()); log.displayNL("Dumping %u parameters :", args.size()); for (uint i=0; i<args.size(); ++i) { log.displayNL(" %u : '%s'", i, args[i].c_str()); } return true; } // // Structures // struct CServiceEntry { CServiceEntry (TSockId sock, const vector<CInetAddress> &a, const string &n, TServiceId s) : SockId(sock), Addr(a), Name(n), SId (s), WaitingUnregistration(false) { } TSockId SockId; // the connection between the service and the naming service vector<CInetAddress> Addr; // address to send to the service who wants to lookup this service // it s possible to have more than one addr, anyway, the naming service // will send good address depending of the sub net address of the service string Name; // name of the service TServiceId SId; // id of the service bool WaitingUnregistration; // true if this service is in unregistration process (wait other service ACK) TTime WaitingUnregistrationTime; // time of the beginning of the inregistration process list<TServiceId> WaitingUnregistrationServices; // list of service that we wait the answer }; // Helper that emulates layer5's send() //void sendToService( uint16 sid, CMessage& msgout ); // Helper that emulate layer5's getServiceName() string getServiceName( TServiceId sid ); // Helper that returns the first address of a service CInetAddress getHostAddress( TServiceId sid ); // Asks a service to stop and tell every one void doUnregisterService (TServiceId sid); /** * Manager for services instances * (Moved from the TICKS to the NS) * Implementable with layer 5, here implemented in NS (layer 3) * \author Olivier Cado * \author Nevrax France * \date 2003 */ class CServiceInstanceManager { public: /// Constructor CServiceInstanceManager(); /** Add the name of a service which must not be duplicated * If uniqueOnShard is true, only one service is allowed. * If uniqueOnShard is false, one service is allowed by physical machine. */ void addUniqueService( const std::string& serviceName, bool uniqueOnShard ) { _UniqueServices.insert( std::make_pair( serviceName, uniqueOnShard ) ); } /// Check if a service is allowed to start (if so, add it) bool queryStartService( const std::string& serviceName, TServiceId serviceId, const std::vector<NLNET::CInetAddress> &addr, string& reason ); /// Release a service instance void releaseService( NLNET::TServiceId serviceId ); /// Display information void displayInfo( NLMISC::CLog *log = NLMISC::InfoLog ) const; /// Make all controlled services quit void killAllServices(); private: /// List of restricted services std::map< std::string, bool > _UniqueServices; /// List of granted (online) services std::set< TServiceId > _OnlineServices; }; CServiceInstanceManager *SIMInstance = NULL; /* * Constructor */ CServiceInstanceManager::CServiceInstanceManager() { nlassert( ! SIMInstance ); SIMInstance = this; // Note: addCallbackArray() done in CRangeMirrorManager::init() } /* * Check if a service is allowed to start. Answer with a GSTS (Grant Start Service) message */ bool CServiceInstanceManager::queryStartService( const std::string& serviceName, TServiceId serviceId, const vector<CInetAddress> &addr, string& reason ) { bool grantStarting = true; std::map< std::string, bool >::iterator ius = _UniqueServices.find( serviceName ); if ( ius != _UniqueServices.end() ) { // Service is restricted set< TServiceId >::iterator ios; bool uniqueOnShard = (*ius).second; for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) { string name = getServiceName( *ios ); if ( name == serviceName ) { if ( uniqueOnShard ) { // Only one service by shard is allowed => deny grantStarting = false; reason = toString( "Service %s already found as %hu, must be unique on shard", serviceName.c_str(), ios->get() ); nlinfo( reason.c_str() ); break; } else { // Only one service by physical machine is allowed // Implementation for layer5 //TSockId hostid1, hostid2; /*CCallbackNetBase *cnb1 = CUnifiedNetwork::getInstance()->getNetBase( serviceId, hostid1 ); CCallbackNetBase *cnb2 = CUnifiedNetwork::getInstance()->getNetBase( *ios, hostid2 ); if ( cnb1->hostAddress( hostid1 ).internalIPAddress() == cnb2->hostAddress( hostid2 ).internalIPAddress() )*/ // Implementation for NS if ( addr[0].internalIPAddress() == getHostAddress( *ios ).internalIPAddress() ) { grantStarting = false; reason = toString( "Service %s already found as %hu on same machine", serviceName.c_str(), ios->get() ); nlinfo( reason.c_str() ); break; } } } } } if ( grantStarting ) { _OnlineServices.insert( serviceId ); } return grantStarting; } /* * Release a service instance */ void CServiceInstanceManager::releaseService( NLNET::TServiceId serviceId ) { _OnlineServices.erase( serviceId ); // not a problem if not found } /* * Display information */ void CServiceInstanceManager::displayInfo( NLMISC::CLog *log ) const { log->displayNL( "Restricted services:" ); std::map< std::string, bool >::const_iterator ius; for ( ius=_UniqueServices.begin(); ius!=_UniqueServices.end(); ++ius ) { log->displayNL( "%s -> only one per %s", (*ius).first.c_str(), (*ius).second?"shard":"machine" ); } log->displayNL( "Online registered services:" ); std::set< TServiceId >::const_iterator ios; for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) { log->displayNL( "%s", CUnifiedNetwork::getInstance()->getServiceUnifiedName( *ios ).c_str() ); } } /* * Make all controlled services quit */ void CServiceInstanceManager::killAllServices() { // Send to all known online services std::set< TServiceId >::const_iterator ios; for ( ios=_OnlineServices.begin(); ios!=_OnlineServices.end(); ++ios ) { doUnregisterService( (TServiceId)(*ios) ); } } // // Variables // list<CServiceEntry> RegisteredServices; /// List of all registred services uint16 MinBasePort = 51000; /// Ports begin at 51000 uint16 MaxBasePort = 52000; /// (note: in this implementation there can be no more than 1000 services) const TServiceId BaseSId(128); /// Allocated SIds begin at 128 (except for Agent Service) const TTime UnregisterTimeout = 10000; /// After 10s we remove an unregister service if every server didn't ACK the message CCallbackServer *CallbackServer = NULL; // // Functions // bool canAccess (const vector<CInetAddress> &addr, const CServiceEntry &entry, vector<CInetAddress> &accessibleAddr) { accessibleAddr.clear (); if (entry.WaitingUnregistration) return false; for (uint i = 0; i < addr.size(); i++) { uint32 net = addr[i].internalNetAddress(); for (uint j = 0; j < entry.Addr.size(); j++) { if (net == entry.Addr[j].internalNetAddress()) { accessibleAddr.push_back (entry.Addr[j]); } } } if (accessibleAddr.empty()) { nldebug ("service %s-%hu is not accessible by '%s'", entry.Name.c_str(), entry.SId.get(), vectorCInetAddressToString (addr).c_str ()); } else { nldebug ("service %s-%hu is accessible by '%s'", entry.Name.c_str(), entry.SId.get(), vectorCInetAddressToString (accessibleAddr).c_str ()); } return !accessibleAddr.empty (); } void displayRegisteredServices (CLog *log = InfoLog) { log->displayNL ("Display the %d registered services :", RegisteredServices.size()); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { TSockId id = (*it).SockId; if (id == NULL) { log->displayNL ("> %s-%hu %s '%s' %s %d addr", (*it).Name.c_str(), it->SId.get(), "<NULL>", "<NULL>", (*it).WaitingUnregistration?"WaitUnreg":"", (*it).Addr.size()); for(uint i = 0; i < (*it).Addr.size(); i++) log->displayNL (" '%s'", (*it).Addr[i].asString().c_str()); } else { log->displayNL ("> %s-%hu %s '%s' %s %d addr", (*it).Name.c_str(), it->SId.get(), (*it).SockId->asString().c_str(), CallbackServer->hostAddress((*it).SockId).asString().c_str(), (*it).WaitingUnregistration?"WaitUnreg":"", (*it).Addr.size()); for(uint i = 0; i < (*it).Addr.size(); i++) log->displayNL (" '%s'", (*it).Addr[i].asString().c_str()); } } log->displayNL ("End of the list"); } list<CServiceEntry>::iterator effectivelyRemove (list<CServiceEntry>::iterator &it) { // remove the service from the registered service list nlinfo ("Effectively remove the service %s-%hu", (*it).Name.c_str(), it->SId.get()); return RegisteredServices.erase (it); } /* * Helper procedure for cbLookupAlternate and cbUnregister. * Note: name is used for a LOGS. */ list<CServiceEntry>::iterator doRemove (list<CServiceEntry>::iterator it) { nldebug ("Unregister the service %s-%hu '%s'", (*it).Name.c_str(), it->SId.get(), (*it).Addr[0].asString().c_str()); // tell to everybody that this service is unregistered CMessage msgout ("UNB"); msgout.serial ((*it).Name); msgout.serial ((*it).SId); vector<CInetAddress> accessibleAddress; nlinfo ("Broadcast the Unregistration of %s-%hu to all registered services", (*it).Name.c_str(), it->SId.get()); for (list<CServiceEntry>::iterator it3 = RegisteredServices.begin(); it3 != RegisteredServices.end (); it3++) { if (canAccess((*it).Addr, (*it3), accessibleAddress)) { CallbackServer->send (msgout, (*it3).SockId); //CNetManager::send ("NS", msgout, (*it3).SockId); nldebug ("Broadcast to %s-%hu", (*it3).Name.c_str(), it3->SId.get()); } } // new system, after the unregistation broadcast, we wait ACK from all services before really remove // the service, before, we tag the service as 'wait before unregister' // if everybody didn't answer before the time out, we remove it (*it).SockId = NULL; (*it).WaitingUnregistration = true; (*it).WaitingUnregistrationTime = CTime::getLocalTime(); // we remove all services awaiting his ACK because this service is down so it'll never ACK for (list<CServiceEntry>::iterator itr = RegisteredServices.begin(); itr != RegisteredServices.end (); itr++) { for (list<TServiceId>::iterator itw = (*itr).WaitingUnregistrationServices.begin(); itw != (*itr).WaitingUnregistrationServices.end ();) { if ((*itw) == (*it).SId) { itw = (*itr).WaitingUnregistrationServices.erase (itw); } else { itw++; } } } string res; for (list<CServiceEntry>::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) { if (!(*it2).WaitingUnregistration) { (*it).WaitingUnregistrationServices.push_back ((*it2).SId); res += toString((*it2).SId.get()) + " "; } } nlinfo ("Before removing the service %s-%hu, we wait the ACK of '%s'", (*it).Name.c_str(), (*it).SId.get(), res.c_str()); if ((*it).WaitingUnregistrationServices.empty()) { return effectivelyRemove (it); } else { return ++it; } // Release from the service instance manager SIMInstance->releaseService( (*it).SId ); } void doUnregisterService (TServiceId sid) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { // found it, remove it doRemove (it); return; } } nlwarning ("Service %hu not found", sid.get()); } void doUnregisterService (TSockId from) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end ();) { if ((*it).SockId == from) { // it's possible that one "from" have more than one registred service, so we have to find in all the list // found it, remove it it = doRemove (it); } else { it++; } } } /*void doUnregisterService (const CInetAddress &addr) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).Addr == addr) { // found it, remove it doRemove (it); return; } } nlwarning ("Service %s not found", addr.asString().c_str()); }*/ /* * Helper function for cbRegister. * If alloc_sid is true, sid is ignored * Returns false in case of failure of sid allocation or bad sid provided * Note: the reply is included in this function, because it must be done before things such as syncUniTime() */ bool doRegister (const string &name, const vector<CInetAddress> &addr, TServiceId sid, TSockId from, CCallbackNetBase &netbase, bool reconnection = false) { // Find if the service is not already registered string reason; uint8 ok = true; bool needRegister = true; /*for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).Addr.asIPString() == addr.asIPString() ) { // we already have a service on this address, remplace it if it's the same name if ((*it).Name == name) { // it's the same service, replace it (*it).SockId = from; sid = (*it).SId; nlinfo ("Replace the service %s", name.c_str()); } else { nlwarning ("Try to register %s to %s but the service %s already on this address. ignore it!", name.c_str(), addr.asIPString().c_str(), (*it).Name.c_str()); ok = false; } needRegister = false; break; } }*/ if (needRegister) { if (sid.get() == 0) { // we have to find a sid sid = BaseSId; bool found = false; while (!found) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { break; } } if (it == RegisteredServices.end ()) { // ok, we have an empty sid found = true; } else { sid.set(sid.get()+1); if (sid.get() == 0) // round the clock { nlwarning ("Service identifier allocation overflow"); ok = false; break; } } } } else { // we have to check that the user provided sid is available list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { nlwarning ("Sid %d already used by another service", sid.get()); ok = false; break; } } if (it != RegisteredServices.end ()) { ok = true; } } // if ok, register the service and send a broadcast to other people if (ok) { // Check if the instance is allowed to start, according to the restriction in the config file if ( SIMInstance->queryStartService( name, sid, addr, reason ) ) { // add him in the registered list RegisteredServices.push_back (CServiceEntry(from, addr, name, sid)); // tell to everybody but not him that this service is registered if (!reconnection) { CMessage msgout ("RGB"); TServiceId::size_type s = 1; msgout.serial (s); msgout.serial (const_cast<string &>(name)); msgout.serial (sid); // we need to send all addr to all services even if the service can't access because we use the address index // to know which connection comes. msgout.serialCont (const_cast<vector<CInetAddress> &>(addr)); nlinfo ("The service is %s-%d, broadcast the Registration to everybody", name.c_str(), sid.get()); vector<CInetAddress> accessibleAddress; for (list<CServiceEntry>::iterator it3 = RegisteredServices.begin(); it3 != RegisteredServices.end (); it3++) { // send only services that can be accessed and not itself if ((*it3).SId != sid && canAccess(addr, (*it3), accessibleAddress)) { CallbackServer->send (msgout, (*it3).SockId); //CNetManager::send ("NS", msgout, (*it3).SockId); nldebug ("Broadcast to %s-%hu", (*it3).Name.c_str(), it3->SId.get()); } } } // set the sid only if it s ok from->setAppId (sid.get()); } else { // Reply "startup denied", and do not send registration to other services ok = false; } } // send the message to the service to say if it s ok or not if (!reconnection) { // send the answer to the client CMessage msgout ("RG"); msgout.serial (ok); if (ok) { msgout.serial (sid); // send him all services available (also itself) TServiceId::size_type nb = 0; vector<CInetAddress> accessibleAddress; for (list<CServiceEntry>::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) { // send only services that are available if (canAccess(addr, (*it2), accessibleAddress)) nb++; } msgout.serial (nb); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { // send only services that are available if (canAccess(addr, (*it), accessibleAddress)) { msgout.serial ((*it).Name); msgout.serial ((*it).SId); msgout.serialCont ((*it).Addr); } } } else { msgout.serial( reason ); } netbase.send (msgout, from); netbase.flush (from); } } //displayRegisteredServices (); return ok!=0; } void checkWaitingUnregistrationServices () { for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end ();) { if ((*it).WaitingUnregistration && ((*it).WaitingUnregistrationServices.empty() || CTime::getLocalTime() > (*it).WaitingUnregistrationTime + UnregisterTimeout)) { if ((*it).WaitingUnregistrationServices.empty()) { nlinfo ("Removing the service %s-%hu because all services ACKd the removal", (*it).Name.c_str(), (*it).SId.get()); } else { string res; for (list<TServiceId>::iterator it2 = (*it).WaitingUnregistrationServices.begin(); it2 != (*it).WaitingUnregistrationServices.end (); it2++) { res += toString(it2->get()) + " "; } nlwarning ("Removing the service %s-%hu because time out occurs (service numbers %s didn't ACK)", (*it).Name.c_str(), (*it).SId.get(), res.c_str()); } it = effectivelyRemove (it); } else { it++; } } } /** * Callback for service unregistration ACK. Mean that a service was ACK the unregistration broadcast */ static void cbACKUnregistration (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { TServiceId sid; msgin.serial (sid); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid && (*it).WaitingUnregistration) { for (list<TServiceId>::iterator it2 = (*it).WaitingUnregistrationServices.begin(); it2 != (*it).WaitingUnregistrationServices.end (); it2++) { if (*it2 == TServiceId(uint16(from->appId()))) { // remove the acked service (*it).WaitingUnregistrationServices.erase (it2); checkWaitingUnregistrationServices (); return; } } } } } /** * Callback for service registration when the naming service goes down and up (don't need to broadcast) */ static void cbResendRegisteration (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { string name; vector<CInetAddress> addr; TServiceId sid; msgin.serial (name); msgin.serialCont (addr); msgin.serial (sid); doRegister (name, addr, sid, from, netbase, true); } /** * Callback for service registration. * * Message expected : RG * - Name of service to register (string) * - Address of service (CInetAddress) * * Message emitted : RG * - Allocated service identifier (TServiceId) or 0 if failed */ static void cbRegister (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { string name; vector<CInetAddress> addr; TServiceId sid; msgin.serial (name); msgin.serialCont (addr); msgin.serial (sid); doRegister (name, addr, sid, from, netbase); } /** * Callback for service unregistration. * * Message expected : UNI * - Service identifier (TServiceId) */ static void cbUnregisterSId (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { TServiceId sid; msgin.serial( sid ); doUnregisterService (sid); //displayRegisteredServices (); } /* * Helper function for cbQueryPort * * \warning QueryPort + Registration is not atomic so more than one service could ask a port before register */ uint16 doAllocatePort (const CInetAddress &addr) { static uint16 nextAvailablePort = MinBasePort; // check if nextavailableport is free if (nextAvailablePort >= MaxBasePort) nextAvailablePort = MinBasePort; bool ok; do { ok = true; list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).Addr[0].port () == nextAvailablePort) { nextAvailablePort++; ok = false; break; } } } while (!ok); return nextAvailablePort++; } /** * Callback for port allocation * Note: if a service queries a port but does not register itself to the naming service, the * port will remain allocated and unused. * * Message expected : QP * - Name of service to register (string) * - Address of service (CInetAddress) (its port can be 0) * * Message emitted : QP * - Allocated port number (uint16) */ static void cbQueryPort (CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { // Allocate port uint16 port = doAllocatePort (netbase.hostAddress (from)); // Send port back CMessage msgout ("QP"); msgout.serial (port); netbase.send (msgout, from); nlinfo ("The service got port %hu", port); } /* * Unregisters a service if it has not been done before. * Note: this callback is called whenever someone disconnects from the NS. * May be there are too many calls if many clients perform many transactional lookups. */ static void cbDisconnect /*(const string &serviceName, TSockId from, void *arg)*/ ( TSockId from, void *arg ) { doUnregisterService (from); //displayRegisteredServices (); } /* * a service is connected, send him all services infos */ static void cbConnect /*(const string &serviceName, TSockId from, void *arg)*/ ( TSockId from, void *arg ) { // we have to wait the registred services message to send all services because it this points, we can't know which sub net // the service can use //displayRegisteredServices (); // set the appid with a bad id (-1) from->setAppId (~0); } /*// returns the list of accessible services with a list of address static void cbRegisteredServices(CMessage& msgin, TSockId from, CCallbackNetBase &netbase) { vector<CInetAddress> addr; msgin.serialCont (addr); nlinfo ("New service ask me the available services, sending him all services available"); // send to the new service the list of all services that this service can access (depending of his sub net) CMessage msgout ("RGB"); uint8 nb = 0; vector<CInetAddress> accessibleAddress; for (list<CServiceEntry>::iterator it2 = RegisteredServices.begin(); it2 != RegisteredServices.end (); it2++) { // send only services that are available if (canAccess(addr, (*it2), accessibleAddress)) nb++; } msgout.serial (nb); for (list<CServiceEntry>::iterator it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { // send only services that are available if (canAccess(addr, (*it), accessibleAddress)) { msgout.serial ((*it).Name); msgout.serial ((*it).SId); msgout.serialCont (accessibleAddress); } } CNetManager::send ("NS", msgout, from); }*/ /* * Helper that emulates layer5 send() */ /*void sendToService( uint16 sid, CMessage& msgout ) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { CallbackServer->send (msgout, (*it).SockId); } } }*/ /* * Helper that emulate layer5's getServiceName() */ string getServiceName( TServiceId sid ) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { return (*it).Name; } } return ""; // not found } /* * Helper that returns the first address of a service */ CInetAddress getHostAddress( TServiceId sid ) { list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).SId == sid) { return (*it).Addr[0]; } } return CInetAddress(); } // // Callback array // TCallbackItem CallbackArray[] = { { "RG", cbRegister }, { "RRG", cbResendRegisteration }, { "QP", cbQueryPort }, { "UNI", cbUnregisterSId }, { "ACK_UNI", cbACKUnregistration }, // { "RS", cbRegisteredServices }, }; // // Service // class CNamingService : public NLNET::IService { public: /** * Init */ void init() { // if a baseport is available in the config file, get it CConfigFile::CVar *var; if ((var = ConfigFile.getVarPtr ("BasePort")) != NULL) { uint16 newBasePort = var->asInt (); nlinfo ("Changing the MinBasePort number from %hu to %hu", MinBasePort, newBasePort); sint32 delta = MaxBasePort - MinBasePort; nlassert (delta > 0); MinBasePort = newBasePort; MaxBasePort = MinBasePort + uint16 (delta); } // Parameters for the service instance manager try { CConfigFile::CVar& uniqueServices = ConfigFile.getVar("UniqueOnShardServices"); for ( uint i=0; i!=uniqueServices.size(); ++i ) { _ServiceInstances.addUniqueService( uniqueServices.asString(i), true ); } } catch(Exception &) {} try { CConfigFile::CVar& uniqueServicesM = ConfigFile.getVar("UniqueByMachineServices"); for ( uint i=0; i!=uniqueServicesM.size(); ++i ) { _ServiceInstances.addUniqueService( uniqueServicesM.asString(i), false ); } } catch(Exception &) {} /* // we don't try to associate message from client CNetManager::getNetBase ("NS")->ignoreAllUnknownId (true); // add the callback in case of disconnection CNetManager::setConnectionCallback ("NS", cbConnect, NULL); // add the callback in case of disconnection CNetManager::setDisconnectionCallback ("NS", cbDisconnect, NULL); */ // DEBUG // DebugLog->addDisplayer( new CStdDisplayer() ); vector<CInetAddress> v = CInetAddress::localAddresses(); nlinfo ("%d detected local addresses:", v.size()); for (uint i = 0; i < v.size(); i++) { nlinfo (" %d - '%s'",i, v[i].asString().c_str()); } uint16 nsport = 50000; if ((var = ConfigFile.getVarPtr ("NSPort")) != NULL) { nsport = var->asInt (); } CallbackServer = new CCallbackServer; CallbackServer->init(nsport); CallbackServer->addCallbackArray(CallbackArray, sizeof(CallbackArray)/sizeof(CallbackArray[0])); CallbackServer->setConnectionCallback(cbConnect, NULL); CallbackServer->setDisconnectionCallback(cbDisconnect, NULL); } /** * Update */ bool update () { checkWaitingUnregistrationServices (); CallbackServer->update (); return true; } void release() { if (CallbackServer != NULL) delete CallbackServer; CallbackServer = NULL; } private: /// Service instance manager singleton CServiceInstanceManager _ServiceInstances; }; static const char* getCompleteServiceName(const IService* theService) { static std::string s; s= "naming_service"; if (theService->haveLongArg("nsname")) { s+= "_"+theService->getLongArg("nsname"); } if (theService->haveLongArg("fullnsname")) { s= theService->getLongArg("fullnsname"); } return s.c_str(); } static const char* getShortServiceName(const IService* theService) { static std::string s; s= "NS"; if (theService->haveLongArg("shortnsname")) { s= theService->getLongArg("shortnsname"); } return s.c_str(); } // /// Naming Service // NLNET_SERVICE_MAIN( CNamingService, getShortServiceName(scn), getCompleteServiceName(scn), 0, EmptyCallbackArray, NELNS_CONFIG, NELNS_LOGS) // // Commands // NLMISC_COMMAND (nsServices, "displays the list of all registered services", "") { if(args.size() != 0) return false; displayRegisteredServices (&log); return true; } NLMISC_COMMAND (kill, "kill a service and send an unregister broadcast to other service", "<ServiceShortName>|<ServiceId>") { if(args.size() != 1) return false; // try with number TServiceId sid(atoi(args[0].c_str())); if(sid.get() == 0) { // not a number, try a name list<CServiceEntry>::iterator it; for (it = RegisteredServices.begin(); it != RegisteredServices.end (); it++) { if ((*it).Name == args[0]) { sid = (*it).SId; break; } } if (it == RegisteredServices.end()) { log.displayNL ("Bad service name or id '%s'", args[0].c_str()); return false; } } doUnregisterService (sid); return true; } NLMISC_DYNVARIABLE(uint32, NbRegisteredServices, "display the number of service that are registered in naming service") { if (get) *pointer = (uint32)RegisteredServices.size(); } NLMISC_COMMAND( displayServiceInstances, "SIM: Display info on service instances", "" ) { SIMInstance->displayInfo( &log ); return true; } NLMISC_COMMAND( killAllServices, "SIM: Make all the controlled services quit", "" ) { SIMInstance->killAllServices(); return true; }